Add Certificate struct and ClientCertificateStore trait

This commit is contained in:
Nathan Fisher 2023-05-27 01:18:30 -04:00
parent 680ca4e9b9
commit 54a099bb44
8 changed files with 139 additions and 54 deletions

103
src/certificate.rs Normal file
View file

@ -0,0 +1,103 @@
use crate::mailuser::Mailuser;
use std::collections::{BTreeMap, HashMap};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Certificate {
pub der: Vec<u8>,
pub key: Vec<u8>,
}
/// An item which stores known certificates. For convenience, this trait is implemented for
/// `std::collections::HashMap<String, String>` and `std::collections::BTreeMap<String, String>`
pub trait CertificateStore: Send + Sync {
/// Retrieves a certificate fingerprint using the hostname as it's key if
/// it has previously been saved
fn get_certificate(&self, host: &str) -> Option<String>;
/// Inserts a certificate fingerprint with the hostname as the key
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String>;
/// Returns true if a fingerprint has been stored for this host
fn contains_certificate(&self, host: &str) -> bool;
}
/// An item which stores client certificates. For convenience, this trait is implemented for
/// `std::collections::HashMap<String, Certificate>` and `std::collections::BTreeMap<String, Certificate>`
pub trait ClientCertificateStore: Send + Sync {
/// Retrieves a client certificate using the sender's address as the key
fn get_certificate(&self, user: &Mailuser) -> Option<Certificate>;
/// Inserts a client certificate with the sender's address as the key
fn insert_certificate(
&mut self,
user: &Mailuser,
certificate: Certificate,
) -> Option<Certificate>;
/// Returns true if the sender has a certificate in this store
fn contains_certificate(&self, user: &Mailuser) -> bool;
}
impl CertificateStore for HashMap<String, String> {
fn get_certificate(&self, host: &str) -> Option<String> {
self.get(host).cloned()
}
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
self.insert(host.to_string(), fingerprint.to_string())
}
fn contains_certificate(&self, host: &str) -> bool {
self.contains_key(host)
}
}
impl CertificateStore for BTreeMap<String, String> {
fn get_certificate(&self, host: &str) -> Option<String> {
self.get(host).cloned()
}
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
self.insert(host.to_string(), fingerprint.to_string())
}
fn contains_certificate(&self, host: &str) -> bool {
self.contains_key(host)
}
}
impl ClientCertificateStore for HashMap<String, Certificate> {
fn get_certificate(&self, user: &Mailuser) -> Option<Certificate> {
self.get(&user.to_string()).cloned()
}
fn insert_certificate(
&mut self,
user: &Mailuser,
certificate: Certificate,
) -> Option<Certificate> {
self.insert(user.to_string(), certificate)
}
fn contains_certificate(&self, user: &Mailuser) -> bool {
self.contains_key(&user.to_string())
}
}
impl ClientCertificateStore for BTreeMap<String, Certificate> {
fn get_certificate(&self, user: &Mailuser) -> Option<Certificate> {
self.get(&user.to_string()).cloned()
}
fn insert_certificate(
&mut self,
user: &Mailuser,
certificate: Certificate,
) -> Option<Certificate> {
self.insert(user.to_string(), certificate)
}
fn contains_certificate(&self, user: &Mailuser) -> bool {
self.contains_key(&user.to_string())
}
}

View file

@ -1,41 +0,0 @@
use std::collections::{BTreeMap, HashMap};
/// An item which stores known certificates. For convenience, this trait is implemented for
/// `std::collections::HashMap<String, String>` and `std::collections::BTreeMap<String, String>`
pub trait CertificateStore: Send + Sync {
/// Retrieves a certificate fingerprint using the hostname as it's key if
/// it has previously been saved
fn get_certificate(&self, host: &str) -> Option<String>;
/// Inserts a certificate fingerprint with the hostname as the key
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String>;
/// Returns true if a fingerprint has been stored for this host
fn contains_certificate(&self, host: &str) -> bool;
}
impl CertificateStore for HashMap<String, String> {
fn get_certificate(&self, host: &str) -> Option<String> {
self.get(host).cloned()
}
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
self.insert(host.to_string(), fingerprint.to_string())
}
fn contains_certificate(&self, host: &str) -> bool {
self.contains_key(host)
}
}
impl CertificateStore for BTreeMap<String, String> {
fn get_certificate(&self, host: &str) -> Option<String> {
self.get(host).cloned()
}
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
self.insert(host.to_string(), fingerprint.to_string())
}
fn contains_certificate(&self, host: &str) -> bool {
self.contains_key(host)
}
}

View file

@ -1,5 +1,5 @@
#![warn(clippy::all, clippy::pedantic)]
mod certificate_store;
mod certificate;
mod fingerprint;
mod host;
mod mailbox;

View file

@ -1,4 +1,4 @@
use crate::prelude::Host;
use crate::{mailuser::Mailuser, prelude::Host};
use std::{fmt, str::FromStr};
#[cfg(feature = "serde")]
@ -64,3 +64,13 @@ impl FromStr for Mailbox {
}
}
}
impl From<Mailuser> for Mailbox {
fn from(value: Mailuser) -> Self {
Self {
username: value.username,
host: value.host,
blurb: None,
}
}
}

View file

@ -1,4 +1,4 @@
use crate::prelude::{Host, ParseMailboxError as Error};
use crate::prelude::{Host, Mailbox, ParseMailboxError as Error};
use std::{fmt, str::FromStr};
#[cfg(feature = "serde")]
@ -36,3 +36,12 @@ impl FromStr for Mailuser {
}
}
}
impl From<Mailbox> for Mailuser {
fn from(value: Mailbox) -> Self {
Self {
username: value.username,
host: value.host,
}
}
}

View file

@ -1,5 +1,5 @@
pub use super::{
certificate_store::CertificateStore,
certificate::{Certificate, CertificateStore, ClientCertificateStore},
fingerprint::{Error as FingerprintError, Fingerprint, GetFingerprint},
host::{Error as ParseHostError, Host},
mailbox::{Error as ParseMailboxError, Mailbox},

View file

@ -32,11 +32,15 @@ where
impl<S> Sender<S>
where
S: CertificateStore + 'static
S: CertificateStore + 'static,
{
pub fn new(request_str: &str, store: S, client_store: S) -> Result<Self, Error> {
let request: Request = request_str.parse()?;
Ok(Self { request, store, client_store })
Ok(Self {
request,
store,
client_store,
})
}
fn host_string(&self) -> String {
@ -50,7 +54,9 @@ where
.try_into()
.map_err(|_| Error::DnsError)?;
let mut it = self.request.sender.host.to_socket_addrs()?;
let client_cert = self.client_store.get_certificate(&self.request.sender.to_string());
let client_cert = self
.client_store
.get_certificate(&self.request.sender.to_string());
let verifier = Arc::new(Verifier::new(self.store));
let Some(socket_addrs) = it.next() else {
return Err(io::Error::new(io::ErrorKind::Other, "no data retrieved").into());
@ -68,6 +74,8 @@ where
stream.write_all(self.request.to_string().as_bytes())?;
let mut buf = vec![];
stream.read_to_end(&mut buf)?;
stream.conn.send_close_notify();
drop(stream);
Ok(buf)
}
}

View file

@ -4,11 +4,7 @@ use {
client::{ServerCertVerified, ServerCertVerifier},
Certificate,
},
std::{
borrow::BorrowMut,
sync::Mutex,
time,
},
std::{borrow::BorrowMut, sync::Mutex, time},
};
#[derive(Debug)]