use crate::bio::Bio;
use crate::error::ErrorStack;
use crate::pkey::{HasPrivate, Pkey};
use crate::x509::X509;
use native_ossl_sys as sys;
use std::ffi::CStr;
use std::mem::ManuallyDrop;
mod private {
pub trait Sealed {}
}
pub struct Server;
pub struct Client;
pub trait Role: private::Sealed {}
impl private::Sealed for Server {}
impl private::Sealed for Client {}
impl Role for Server {}
impl Role for Client {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TlsVersion {
Tls12 = 0x0303,
Tls13 = 0x0304,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SslVerifyMode(i32);
impl SslVerifyMode {
pub const NONE: Self = SslVerifyMode(0x00);
pub const PEER: Self = SslVerifyMode(0x01);
pub const FAIL_IF_NO_PEER_CERT: Self = SslVerifyMode(0x02);
#[must_use]
pub fn or(self, other: Self) -> Self {
SslVerifyMode(self.0 | other.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct HostnameFlags(u32);
impl HostnameFlags {
pub const NONE: Self = Self(0);
pub const NO_PARTIAL_WILDCARDS: Self = Self(0x4);
pub const MULTI_LABEL_WILDCARDS: Self = Self(0x8);
#[must_use]
pub fn or(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
#[derive(Debug)]
pub enum SslIoError {
WantRead,
WantWrite,
ZeroReturn,
Syscall(ErrorStack),
Ssl(ErrorStack),
Other(i32),
}
impl std::fmt::Display for SslIoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::WantRead => write!(f, "SSL want read"),
Self::WantWrite => write!(f, "SSL want write"),
Self::ZeroReturn => write!(f, "SSL zero return (peer closed)"),
Self::Syscall(e) => write!(f, "SSL syscall error: {e}"),
Self::Ssl(e) => write!(f, "SSL error: {e}"),
Self::Other(code) => write!(f, "SSL error code {code}"),
}
}
}
impl std::error::Error for SslIoError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShutdownResult {
Sent,
Complete,
}
pub struct SslSession {
ptr: *mut sys::SSL_SESSION,
}
unsafe impl Send for SslSession {}
unsafe impl Sync for SslSession {}
impl Clone for SslSession {
fn clone(&self) -> Self {
unsafe { sys::SSL_SESSION_up_ref(self.ptr) };
SslSession { ptr: self.ptr }
}
}
impl Drop for SslSession {
fn drop(&mut self) {
unsafe { sys::SSL_SESSION_free(self.ptr) };
}
}
pub struct BorrowedSslSession<'ssl> {
inner: ManuallyDrop<SslSession>,
_marker: std::marker::PhantomData<&'ssl Ssl>,
}
unsafe impl Send for BorrowedSslSession<'_> {}
impl std::ops::Deref for BorrowedSslSession<'_> {
type Target = SslSession;
fn deref(&self) -> &SslSession {
&self.inner
}
}
pub struct SslCtx {
ptr: *mut sys::SSL_CTX,
}
unsafe impl Send for SslCtx {}
unsafe impl Sync for SslCtx {}
impl Clone for SslCtx {
fn clone(&self) -> Self {
unsafe { sys::SSL_CTX_up_ref(self.ptr) };
SslCtx { ptr: self.ptr }
}
}
impl Drop for SslCtx {
fn drop(&mut self) {
unsafe { sys::SSL_CTX_free(self.ptr) };
}
}
impl SslCtx {
pub fn new() -> Result<Self, ErrorStack> {
let method = unsafe { sys::TLS_method() };
let ptr = unsafe { sys::SSL_CTX_new(method) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(SslCtx { ptr })
}
pub fn new_client() -> Result<Self, ErrorStack> {
let method = unsafe { sys::TLS_client_method() };
let ptr = unsafe { sys::SSL_CTX_new(method) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(SslCtx { ptr })
}
pub fn new_server() -> Result<Self, ErrorStack> {
let method = unsafe { sys::TLS_server_method() };
let ptr = unsafe { sys::SSL_CTX_new(method) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(SslCtx { ptr })
}
pub fn set_min_proto_version(&self, ver: TlsVersion) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_ctrl(self.ptr, 123, ver as i64, std::ptr::null_mut()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn set_max_proto_version(&self, ver: TlsVersion) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_ctrl(self.ptr, 124, ver as i64, std::ptr::null_mut()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn set_verify(&self, mode: SslVerifyMode) {
unsafe { sys::SSL_CTX_set_verify(self.ptr, mode.0, None) };
}
pub fn set_cipher_list(&self, list: &CStr) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_set_cipher_list(self.ptr, list.as_ptr()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn set_ciphersuites(&self, list: &CStr) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_set_ciphersuites(self.ptr, list.as_ptr()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn use_certificate(&self, cert: &X509) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_use_certificate(self.ptr, cert.as_ptr()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn use_private_key<T: HasPrivate>(&self, key: &Pkey<T>) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_use_PrivateKey(self.ptr, key.as_ptr()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn check_private_key(&self) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_check_private_key(self.ptr) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn set_default_verify_paths(&self) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_CTX_set_default_verify_paths(self.ptr) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn disable_session_cache(&self) {
unsafe { sys::SSL_CTX_ctrl(self.ptr, 44, 0, std::ptr::null_mut()) };
}
pub fn new_ssl(&self) -> Result<Ssl, ErrorStack> {
let ptr = unsafe { sys::SSL_new(self.ptr) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(Ssl { ptr })
}
}
#[must_use = "call .build() to obtain an SslCtx"]
pub struct SslCtxBuilder<R: Role> {
ptr: *mut sys::SSL_CTX,
_role: std::marker::PhantomData<R>,
}
unsafe impl<R: Role> Send for SslCtxBuilder<R> {}
impl<R: Role> Drop for SslCtxBuilder<R> {
fn drop(&mut self) {
unsafe { sys::SSL_CTX_free(self.ptr) };
}
}
impl SslCtxBuilder<Server> {
pub fn new() -> Result<Self, ErrorStack> {
let ptr = unsafe { sys::SSL_CTX_new(sys::TLS_server_method()) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(Self {
ptr,
_role: std::marker::PhantomData,
})
}
}
impl SslCtxBuilder<Client> {
pub fn new() -> Result<Self, ErrorStack> {
let ptr = unsafe { sys::SSL_CTX_new(sys::TLS_client_method()) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(Self {
ptr,
_role: std::marker::PhantomData,
})
}
}
impl<R: Role> SslCtxBuilder<R> {
pub fn min_proto_version(self, ver: TlsVersion) -> Result<Self, ErrorStack> {
let rc = unsafe {
sys::SSL_CTX_ctrl(
self.ptr,
sys::SSL_CTRL_SET_MIN_PROTO_VERSION.cast_signed(),
ver as i64,
std::ptr::null_mut(),
)
};
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn max_proto_version(self, ver: TlsVersion) -> Result<Self, ErrorStack> {
let rc = unsafe {
sys::SSL_CTX_ctrl(
self.ptr,
sys::SSL_CTRL_SET_MAX_PROTO_VERSION.cast_signed(),
ver as i64,
std::ptr::null_mut(),
)
};
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn cipher_list(self, list: &CStr) -> Result<Self, ErrorStack> {
crate::ossl_call!(sys::SSL_CTX_set_cipher_list(self.ptr, list.as_ptr()))?;
Ok(self)
}
pub fn ciphersuites(self, list: &CStr) -> Result<Self, ErrorStack> {
crate::ossl_call!(sys::SSL_CTX_set_ciphersuites(self.ptr, list.as_ptr()))?;
Ok(self)
}
pub fn session_cache_mode(self, mode: i64) -> Self {
unsafe {
sys::SSL_CTX_ctrl(
self.ptr,
sys::SSL_CTRL_SET_SESS_CACHE_MODE.cast_signed(),
mode,
std::ptr::null_mut(),
)
};
self
}
pub fn alpn_protocols(self, protocols: &[u8]) -> Result<Self, ErrorStack> {
let len = u32::try_from(protocols.len()).map_err(|_| ErrorStack::drain())?;
let ret = unsafe { sys::SSL_CTX_set_alpn_protos(self.ptr, protocols.as_ptr(), len) };
if ret != 0 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn alpn_protos_list(self, protos: &[&str]) -> Result<Self, ErrorStack> {
let mut wire = Vec::new();
for proto in protos {
let len = u8::try_from(proto.len()).map_err(|_| ErrorStack::drain())?;
wire.push(len);
wire.extend_from_slice(proto.as_bytes());
}
self.alpn_protocols(&wire)
}
pub fn build(self) -> Result<SslCtx, ErrorStack> {
let ptr = self.ptr;
std::mem::forget(self);
Ok(SslCtx { ptr })
}
}
impl SslCtxBuilder<Server> {
pub fn certificate(self, cert: &X509) -> Result<Self, ErrorStack> {
let rc = unsafe { sys::SSL_CTX_use_certificate(self.ptr, cert.as_ptr()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn add_chain_cert(self, cert: &X509) -> Result<Self, ErrorStack> {
unsafe { sys::X509_up_ref(cert.as_ptr()) };
let rc = unsafe {
sys::SSL_CTX_ctrl(
self.ptr,
sys::SSL_CTRL_EXTRA_CHAIN_CERT.cast_signed(),
0,
cert.as_ptr().cast::<std::ffi::c_void>(),
)
};
if rc == 0 {
unsafe { sys::X509_free(cert.as_ptr()) };
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn private_key(self, key: &Pkey<crate::pkey::Private>) -> Result<Self, ErrorStack> {
let rc = unsafe { sys::SSL_CTX_use_PrivateKey(self.ptr, key.as_ptr()) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn check_private_key(self) -> Result<Self, ErrorStack> {
crate::ossl_call!(sys::SSL_CTX_check_private_key(self.ptr))?;
Ok(self)
}
pub fn verify_client(self, require: bool) -> Self {
let mode = if require {
(sys::SSL_VERIFY_PEER | sys::SSL_VERIFY_FAIL_IF_NO_PEER_CERT).cast_signed()
} else {
sys::SSL_VERIFY_PEER.cast_signed()
};
unsafe { sys::SSL_CTX_set_verify(self.ptr, mode, None) };
self
}
}
impl SslCtxBuilder<Client> {
pub fn default_ca_paths(self) -> Result<Self, ErrorStack> {
crate::ossl_call!(sys::SSL_CTX_set_default_verify_paths(self.ptr))?;
Ok(self)
}
pub fn ca_bundle_file(self, path: &std::path::Path) -> Result<Self, ErrorStack> {
let path_cstr = std::ffi::CString::new(path.to_str().ok_or_else(ErrorStack::drain)?)
.map_err(|_| ErrorStack::drain())?;
let ret = unsafe {
sys::SSL_CTX_load_verify_locations(self.ptr, path_cstr.as_ptr(), std::ptr::null())
};
if ret != 1 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn ca_cert(self, cert: &X509) -> Result<Self, ErrorStack> {
let store = unsafe { sys::SSL_CTX_get_cert_store(self.ptr) };
if store.is_null() {
return Err(ErrorStack::drain());
}
let ret = unsafe { sys::X509_STORE_add_cert(store, cert.as_ptr()) };
if ret != 1 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn verify_peer(self) -> Self {
unsafe { sys::SSL_CTX_set_verify(self.ptr, sys::SSL_VERIFY_PEER.cast_signed(), None) };
self
}
pub fn verify_hostname(self, hostname: &str) -> Result<Self, ErrorStack> {
let cstr = std::ffi::CString::new(hostname).map_err(|_| ErrorStack::drain())?;
let param = unsafe { sys::SSL_CTX_get0_param(self.ptr) };
if param.is_null() {
return Err(ErrorStack::drain());
}
let ret = unsafe { sys::X509_VERIFY_PARAM_set1_host(param, cstr.as_ptr(), 0) };
if ret != 1 {
return Err(ErrorStack::drain());
}
Ok(self)
}
pub fn verify_hostname_flags(self, flags: HostnameFlags) -> Result<Self, ErrorStack> {
let param = unsafe { sys::SSL_CTX_get0_param(self.ptr) };
if param.is_null() {
return Err(ErrorStack::drain());
}
unsafe { sys::X509_VERIFY_PARAM_set_hostflags(param, flags.0) };
Ok(self)
}
}
pub struct Ssl {
ptr: *mut sys::SSL,
}
unsafe impl Send for Ssl {}
impl Drop for Ssl {
fn drop(&mut self) {
unsafe { sys::SSL_free(self.ptr) };
}
}
impl Ssl {
pub fn set_bio_duplex(&mut self, bio: Bio) {
let ptr = bio.as_ptr();
std::mem::forget(bio);
unsafe { sys::SSL_set_bio(self.ptr, ptr, ptr) };
}
pub fn set_bio(&mut self, rbio: Bio, wbio: Bio) {
let rbio_ptr = rbio.as_ptr();
let wbio_ptr = wbio.as_ptr();
std::mem::forget(rbio);
std::mem::forget(wbio);
unsafe { sys::SSL_set_bio(self.ptr, rbio_ptr, wbio_ptr) };
}
pub fn set_hostname(&mut self, hostname: &CStr) -> Result<(), ErrorStack> {
let rc = unsafe {
sys::SSL_ctrl(
self.ptr,
55, 0, hostname.as_ptr() as *mut std::os::raw::c_void,
)
};
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn set_connect_state(&mut self) {
unsafe { sys::SSL_set_connect_state(self.ptr) };
}
pub fn set_accept_state(&mut self) {
unsafe { sys::SSL_set_accept_state(self.ptr) };
}
pub fn connect(&mut self) -> Result<(), SslIoError> {
let rc = unsafe { sys::SSL_connect(self.ptr) };
if rc == 1 {
return Ok(());
}
Err(self.ssl_io_error(rc))
}
pub fn accept(&mut self) -> Result<(), SslIoError> {
let rc = unsafe { sys::SSL_accept(self.ptr) };
if rc == 1 {
return Ok(());
}
Err(self.ssl_io_error(rc))
}
pub fn do_handshake(&mut self) -> Result<(), SslIoError> {
let rc = unsafe { sys::SSL_do_handshake(self.ptr) };
if rc == 1 {
return Ok(());
}
Err(self.ssl_io_error(rc))
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, SslIoError> {
let mut readbytes: usize = 0;
let rc = unsafe {
sys::SSL_read_ex(
self.ptr,
buf.as_mut_ptr().cast(),
buf.len(),
std::ptr::addr_of_mut!(readbytes),
)
};
if rc == 1 {
return Ok(readbytes);
}
Err(self.ssl_io_error(rc))
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize, SslIoError> {
let mut written: usize = 0;
let rc = unsafe {
sys::SSL_write_ex(
self.ptr,
buf.as_ptr().cast(),
buf.len(),
std::ptr::addr_of_mut!(written),
)
};
if rc == 1 {
return Ok(written);
}
Err(self.ssl_io_error(rc))
}
pub fn shutdown(&mut self) -> Result<ShutdownResult, ErrorStack> {
let rc = unsafe { sys::SSL_shutdown(self.ptr) };
match rc {
1 => Ok(ShutdownResult::Complete),
0 => Ok(ShutdownResult::Sent),
_ => Err(ErrorStack::drain()),
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
#[must_use]
pub fn peer_cert_chain(&self) -> Option<Vec<X509>> {
let stack = unsafe { sys::SSL_get_peer_cert_chain(self.ptr) };
if stack.is_null() {
return None;
}
let n = unsafe { sys::OPENSSL_sk_num(stack.cast::<sys::OPENSSL_STACK>()) };
let n = usize::try_from(n).unwrap_or(0);
let mut certs = Vec::with_capacity(n);
for i in 0..n {
let raw =
unsafe { sys::OPENSSL_sk_value(stack.cast::<sys::OPENSSL_STACK>(), i as i32) };
if raw.is_null() {
continue;
}
let cert_ptr = raw.cast::<sys::X509>();
unsafe { sys::X509_up_ref(cert_ptr) };
certs.push(unsafe { X509::from_ptr(cert_ptr) });
}
Some(certs)
}
#[must_use]
pub fn peer_certificate(&self) -> Option<X509> {
let ptr = unsafe { sys::SSL_get0_peer_certificate(self.ptr) };
if ptr.is_null() {
return None;
}
unsafe { sys::X509_up_ref(ptr) };
Some(unsafe { X509::from_ptr(ptr) })
}
#[must_use]
pub fn get1_session(&self) -> Option<SslSession> {
let ptr = unsafe { sys::SSL_get1_session(self.ptr) };
if ptr.is_null() {
None
} else {
Some(SslSession { ptr })
}
}
#[must_use]
pub fn session(&self) -> Option<BorrowedSslSession<'_>> {
let ptr = unsafe { sys::SSL_get_session(self.ptr) };
if ptr.is_null() {
None
} else {
Some(BorrowedSslSession {
inner: ManuallyDrop::new(SslSession { ptr }),
_marker: std::marker::PhantomData,
})
}
}
pub fn set_session(&mut self, session: &SslSession) -> Result<(), ErrorStack> {
let rc = unsafe { sys::SSL_set_session(self.ptr, session.ptr) };
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn set_verify_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> {
let cstr = std::ffi::CString::new(hostname).map_err(|_| ErrorStack::drain())?;
let ret = unsafe { sys::SSL_set1_host(self.ptr, cstr.as_ptr()) };
if ret != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
fn ssl_io_error(&self, ret: i32) -> SslIoError {
let err = unsafe { sys::SSL_get_error(self.ptr, ret) };
match err {
2 => SslIoError::WantRead,
3 => SslIoError::WantWrite,
5 => SslIoError::Syscall(ErrorStack::drain()),
6 => SslIoError::ZeroReturn,
_ => {
let stack = ErrorStack::drain();
if stack.errors().next().is_none() {
SslIoError::Other(err)
} else {
SslIoError::Ssl(stack)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pkey::{KeygenCtx, Pkey, Private, Public};
use crate::x509::{X509Builder, X509NameOwned};
fn make_ed25519_key() -> (Pkey<Private>, Pkey<Public>) {
let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
let priv_key = kgen.generate().unwrap();
let pub_key = Pkey::<Public>::from(priv_key.clone());
(priv_key, pub_key)
}
fn make_self_signed_cert(priv_key: &Pkey<Private>, pub_key: &Pkey<Public>) -> X509 {
let mut name = X509NameOwned::new().unwrap();
name.add_entry_by_txt(c"CN", b"test").unwrap();
X509Builder::new()
.unwrap()
.set_version(2)
.unwrap()
.set_serial_number(1)
.unwrap()
.set_not_before_offset(0)
.unwrap()
.set_not_after_offset(86400)
.unwrap()
.set_subject_name(&name)
.unwrap()
.set_issuer_name(&name)
.unwrap()
.set_public_key(pub_key)
.unwrap()
.sign(priv_key, None)
.unwrap()
.build()
}
fn do_handshake_pair(mut client: Ssl, mut server: Ssl) -> Result<(Ssl, Ssl), SslIoError> {
let mut client_bio: *mut sys::BIO = std::ptr::null_mut();
let mut server_bio: *mut sys::BIO = std::ptr::null_mut();
let rc = unsafe {
sys::BIO_new_bio_pair(
std::ptr::addr_of_mut!(client_bio),
0,
std::ptr::addr_of_mut!(server_bio),
0,
)
};
assert_eq!(rc, 1, "BIO_new_bio_pair failed");
let client_bio_obj = unsafe { Bio::from_ptr_owned(client_bio) };
let server_bio_obj = unsafe { Bio::from_ptr_owned(server_bio) };
client.set_bio_duplex(client_bio_obj);
server.set_bio_duplex(server_bio_obj);
let mut client_done = false;
let mut server_done = false;
for _ in 0..20 {
if !client_done {
match client.connect() {
Ok(()) => client_done = true,
Err(SslIoError::WantRead | SslIoError::WantWrite) => {}
Err(e) => return Err(e),
}
}
if !server_done {
match server.accept() {
Ok(()) => server_done = true,
Err(SslIoError::WantRead | SslIoError::WantWrite) => {}
Err(e) => return Err(e),
}
}
if client_done && server_done {
return Ok((client, server));
}
}
Err(SslIoError::Other(-1))
}
#[test]
fn ctx_new_variants() {
SslCtx::new().unwrap();
SslCtx::new_client().unwrap();
SslCtx::new_server().unwrap();
}
#[test]
fn ctx_clone() {
let ctx = SslCtx::new().unwrap();
let _clone = ctx.clone();
}
#[test]
fn ctx_proto_version() {
let ctx = SslCtx::new().unwrap();
ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
}
#[test]
fn ctx_verify_mode() {
let ctx = SslCtx::new().unwrap();
ctx.set_verify(SslVerifyMode::NONE);
ctx.set_verify(SslVerifyMode::PEER);
ctx.set_verify(SslVerifyMode::PEER.or(SslVerifyMode::FAIL_IF_NO_PEER_CERT));
}
#[test]
fn ctx_cipher_list() {
let ctx = SslCtx::new().unwrap();
ctx.set_cipher_list(c"HIGH:!aNULL").unwrap();
}
#[test]
fn ctx_load_cert_and_key() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let ctx = SslCtx::new_server().unwrap();
ctx.use_certificate(&cert).unwrap();
ctx.use_private_key(&priv_key).unwrap();
ctx.check_private_key().unwrap();
}
#[test]
fn tls13_handshake_ed25519() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let server_ctx = SslCtx::new_server().unwrap();
server_ctx.set_min_proto_version(TlsVersion::Tls13).unwrap();
server_ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
server_ctx.use_certificate(&cert).unwrap();
server_ctx.use_private_key(&priv_key).unwrap();
server_ctx.check_private_key().unwrap();
server_ctx.disable_session_cache();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_min_proto_version(TlsVersion::Tls13).unwrap();
client_ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
client_ctx.disable_session_cache();
let client_ssl = client_ctx.new_ssl().unwrap();
let server_ssl = server_ctx.new_ssl().unwrap();
let (mut client, mut server) =
do_handshake_pair(client_ssl, server_ssl).expect("TLS 1.3 handshake failed");
let msg = b"hello TLS 1.3";
let n = client.write(msg).unwrap();
assert_eq!(n, msg.len());
let mut buf = [0u8; 64];
let n = server.read(&mut buf).unwrap();
assert_eq!(&buf[..n], msg);
let reply = b"world";
server.write(reply).unwrap();
let n = client.read(&mut buf).unwrap();
assert_eq!(&buf[..n], reply);
}
#[test]
fn tls12_handshake() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let server_ctx = SslCtx::new_server().unwrap();
server_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
server_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
server_ctx.use_certificate(&cert).unwrap();
server_ctx.use_private_key(&priv_key).unwrap();
server_ctx.check_private_key().unwrap();
server_ctx.disable_session_cache();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
client_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
client_ctx.disable_session_cache();
let client_ssl = client_ctx.new_ssl().unwrap();
let server_ssl = server_ctx.new_ssl().unwrap();
let (mut client, mut server) =
do_handshake_pair(client_ssl, server_ssl).expect("TLS 1.2 handshake failed");
client.write(b"tls12").unwrap();
let mut buf = [0u8; 16];
let n = server.read(&mut buf).unwrap();
assert_eq!(&buf[..n], b"tls12");
}
#[test]
fn peer_certificate_after_handshake() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let cert_der = cert.to_der().unwrap();
let server_ctx = SslCtx::new_server().unwrap();
server_ctx.use_certificate(&cert).unwrap();
server_ctx.use_private_key(&priv_key).unwrap();
server_ctx.disable_session_cache();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
client_ctx.disable_session_cache();
let (client, _server) =
do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
.unwrap();
let peer_cert = client.peer_certificate().expect("no peer certificate");
let peer_der = peer_cert.to_der().unwrap();
assert_eq!(peer_der, cert_der, "peer cert DER mismatch");
}
#[test]
fn shutdown_sequence() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let server_ctx = SslCtx::new_server().unwrap();
server_ctx.use_certificate(&cert).unwrap();
server_ctx.use_private_key(&priv_key).unwrap();
server_ctx.disable_session_cache();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
client_ctx.disable_session_cache();
let (mut client, mut server) =
do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
.unwrap();
let r = client.shutdown().unwrap();
assert_eq!(r, ShutdownResult::Sent);
let r = server.shutdown().unwrap();
assert_eq!(r, ShutdownResult::Sent);
let r = client.shutdown().unwrap();
assert_eq!(r, ShutdownResult::Complete);
}
#[test]
fn verify_mode_bits() {
let both = SslVerifyMode::PEER.or(SslVerifyMode::FAIL_IF_NO_PEER_CERT);
assert_eq!(both.0, 0x03);
}
#[test]
fn peer_cert_chain_none_before_handshake() {
let ctx = SslCtx::new_client().unwrap();
let ssl = ctx.new_ssl().unwrap();
assert!(
ssl.peer_cert_chain().is_none(),
"expected no peer cert chain before handshake"
);
}
#[test]
fn peer_cert_chain_after_handshake() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let cert_der = cert.to_der().unwrap();
let server_ctx = SslCtx::new_server().unwrap();
server_ctx.use_certificate(&cert).unwrap();
server_ctx.use_private_key(&priv_key).unwrap();
server_ctx.disable_session_cache();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
client_ctx.disable_session_cache();
let (client, _server) =
do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
.expect("handshake failed");
let chain = client
.peer_cert_chain()
.expect("expected Some peer cert chain after handshake");
assert!(
!chain.is_empty(),
"peer cert chain must contain at least one certificate"
);
let leaf_der = chain[0].to_der().unwrap();
assert_eq!(
leaf_der, cert_der,
"first cert in chain must match server leaf cert"
);
}
#[test]
fn session_none_before_handshake() {
let ctx = SslCtx::new_client().unwrap();
let ssl = ctx.new_ssl().unwrap();
assert!(
ssl.session().is_none(),
"expected no session before handshake"
);
}
#[test]
fn session_some_after_handshake_tls12() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let server_ctx = SslCtx::new_server().unwrap();
server_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
server_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
server_ctx.use_certificate(&cert).unwrap();
server_ctx.use_private_key(&priv_key).unwrap();
server_ctx.check_private_key().unwrap();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
client_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
let (client, _server) =
do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
.expect("TLS 1.2 handshake failed");
let borrowed = client
.session()
.expect("expected Some session after TLS 1.2 handshake");
let owned = client
.get1_session()
.expect("get1_session must also return Some");
assert_eq!(
borrowed.ptr, owned.ptr,
"borrowed and owned sessions should point to the same SSL_SESSION"
);
}
#[test]
fn borrowed_session_does_not_free() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let server_ctx = SslCtx::new_server().unwrap();
server_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
server_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
server_ctx.use_certificate(&cert).unwrap();
server_ctx.use_private_key(&priv_key).unwrap();
server_ctx.check_private_key().unwrap();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
client_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
let (client, _server) =
do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
.expect("handshake failed");
{
let _borrowed = client.session().expect("session must be Some");
}
let _owned = client
.get1_session()
.expect("session still accessible after borrowed drop");
}
#[test]
fn ssl_ctx_builder_server_basic() {
let ctx = SslCtxBuilder::<Server>::new().unwrap().build().unwrap();
ctx.new_ssl().unwrap();
}
#[test]
fn ssl_ctx_builder_client_basic() {
let ctx = SslCtxBuilder::<Client>::new().unwrap().build().unwrap();
ctx.new_ssl().unwrap();
}
#[test]
fn ssl_ctx_builder_client_cipher_list() {
let ctx = SslCtxBuilder::<Client>::new()
.unwrap()
.cipher_list(c"HIGH:!aNULL")
.unwrap()
.build()
.unwrap();
ctx.new_ssl().unwrap();
}
#[test]
fn ssl_ctx_builder_client_alpn_protos_list() {
let ctx = SslCtxBuilder::<Client>::new()
.unwrap()
.alpn_protos_list(&["h2", "http/1.1"])
.unwrap()
.build()
.unwrap();
ctx.new_ssl().unwrap();
}
#[test]
fn ssl_ctx_builder_client_default_ca_paths() {
let ctx = SslCtxBuilder::<Client>::new()
.unwrap()
.default_ca_paths()
.unwrap()
.verify_peer()
.build()
.unwrap();
ctx.new_ssl().unwrap();
}
#[test]
fn ssl_ctx_builder_client_verify_hostname() {
let ctx = SslCtxBuilder::<Client>::new()
.unwrap()
.verify_hostname("example.com")
.unwrap()
.build()
.unwrap();
ctx.new_ssl().unwrap();
}
#[test]
fn ssl_ctx_builder_server_with_cert_key() {
let (priv_key, pub_key) = make_ed25519_key();
let cert = make_self_signed_cert(&priv_key, &pub_key);
let ctx = SslCtxBuilder::<Server>::new()
.unwrap()
.certificate(&cert)
.unwrap()
.private_key(&priv_key)
.unwrap()
.check_private_key()
.unwrap()
.build()
.unwrap();
let client_ctx = SslCtx::new_client().unwrap();
client_ctx.set_verify(SslVerifyMode::NONE);
client_ctx.disable_session_cache();
let server_ssl = ctx.new_ssl().unwrap();
let client_ssl = client_ctx.new_ssl().unwrap();
do_handshake_pair(client_ssl, server_ssl).expect("builder server handshake failed");
}
#[test]
fn ssl_set_verify_hostname_per_connection() {
let ctx = SslCtx::new_client().unwrap();
let mut ssl = ctx.new_ssl().unwrap();
ssl.set_verify_hostname("example.com").unwrap();
}
#[test]
fn ssl_ctx_builder_server_proto_versions() {
let ctx = SslCtxBuilder::<Server>::new()
.unwrap()
.min_proto_version(TlsVersion::Tls12)
.unwrap()
.max_proto_version(TlsVersion::Tls13)
.unwrap()
.build()
.unwrap();
ctx.new_ssl().unwrap();
}
#[test]
fn ssl_ctx_builder_verify_hostname_flags_no_partial_wildcards() {
let ctx = SslCtxBuilder::<Client>::new()
.unwrap()
.verify_hostname_flags(HostnameFlags::NO_PARTIAL_WILDCARDS)
.unwrap()
.build()
.unwrap();
ctx.new_ssl().unwrap();
}
}