use tonic::Request;
use tonic::transport::server::{TcpConnectInfo, TlsConnectInfo};
use x509_parser::oid_registry::Oid;
use x509_parser::parse_x509_certificate;
use x509_parser::x509::X509Name;
use crate::Error;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Application {
pub app_id: String,
pub box_id: String,
pub app_domain: String,
}
pub fn peer_application<T>(request: &Request<T>) -> Result<Application, Error> {
let certificates = request
.extensions()
.get::<TlsConnectInfo<TcpConnectInfo>>()
.and_then(TlsConnectInfo::peer_certs);
#[cfg(unix)]
let certificates = certificates.or_else(|| {
request
.extensions()
.get::<TlsConnectInfo<tonic::transport::server::UdsConnectInfo>>()
.and_then(TlsConnectInfo::peer_certs)
});
let certificates = certificates.ok_or(Error::UnauthenticatedPeer)?;
application_from_certificates(certificates.as_ref())
}
fn application_from_certificates(
certificates: &[rustls::pki_types::CertificateDer<'static>],
) -> Result<Application, Error> {
if certificates.len() != 1 {
return Err(Error::InvalidPeerCertificateCount {
count: certificates.len(),
});
}
let (_, certificate) = parse_x509_certificate(certificates[0].as_ref())
.map_err(|_| Error::InvalidPeerCertificate)?;
Ok(Application {
app_id: serial_number(certificate.subject()),
box_id: serial_number(certificate.issuer()),
app_domain: certificate
.subject()
.iter_common_name()
.next()
.and_then(|name| name.as_str().ok())
.unwrap_or_default()
.to_owned(),
})
}
fn serial_number(name: &X509Name<'_>) -> String {
let oid = Oid::from(&[2, 5, 4, 5]).expect("serialNumber OID is valid");
name.iter_by_oid(&oid)
.next()
.and_then(|value| value.as_str().ok())
.unwrap_or_default()
.to_owned()
}
#[cfg(test)]
mod tests {
use rustls::pki_types::CertificateDer;
use crate::Error;
use super::application_from_certificates;
#[test]
fn rejects_zero_or_multiple_peer_certificates() {
let zero = application_from_certificates(&[]).expect_err("zero certificates");
assert!(matches!(
zero,
Error::InvalidPeerCertificateCount { count: 0 }
));
let invalid = CertificateDer::from(vec![0_u8]);
let multiple = application_from_certificates(&[invalid.clone(), invalid])
.expect_err("multiple certificates");
assert!(matches!(
multiple,
Error::InvalidPeerCertificateCount { count: 2 }
));
}
}