Compare commits

..

No commits in common. "ef307f49837500ec9c80eff51087ac3153b2c93c" and "e042a139bfdef379b39b85a242f702cf971a2f06" have entirely different histories.

7 changed files with 31 additions and 53 deletions

View file

@ -1,4 +1,3 @@
use async_trait::async_trait;
use tokio_gemini::{ use tokio_gemini::{
certs::{ certs::{
dane, file_sscv::KnownHostsFile, fingerprint::CertFingerprint, SelfsignedCertVerifier, dane, file_sscv::KnownHostsFile, fingerprint::CertFingerprint, SelfsignedCertVerifier,
@ -13,7 +12,7 @@ use tokio_gemini::{
// //
const USAGE: &str = "-k\t\tinsecure mode (trust all certs) const USAGE: &str = "-k\t\tinsecure mode (trust all certs)
-d <DNS server>\tuse custom DNS for resolving & DANE -d <DNS server addr>\tuse custom DNS for resolving & DANE
-h\t\tshow help"; -h\t\tshow help";
#[tokio::main] #[tokio::main]
@ -121,11 +120,10 @@ struct CertVerifier {
dns: Option<DnsClient>, dns: Option<DnsClient>,
} }
#[async_trait]
impl SelfsignedCertVerifier for CertVerifier { impl SelfsignedCertVerifier for CertVerifier {
async fn verify( async fn verify<'c>(
&self, &self,
cert: &tokio_gemini::certs::CertificateDer<'_>, cert: &'c tokio_gemini::certs::CertificateDer<'c>,
host: &str, host: &str,
port: u16, port: u16,
) -> Result<bool, tokio_gemini::LibError> { ) -> Result<bool, tokio_gemini::LibError> {

View file

@ -1,4 +1,3 @@
use async_trait::async_trait;
use tokio_gemini::{ use tokio_gemini::{
certs::{fingerprint::CertFingerprint, SelfsignedCertVerifier}, certs::{fingerprint::CertFingerprint, SelfsignedCertVerifier},
Client, LibError, Client, LibError,
@ -33,11 +32,10 @@ async fn main() -> Result<(), LibError> {
struct CertVerifier; struct CertVerifier;
#[async_trait]
impl SelfsignedCertVerifier for CertVerifier { impl SelfsignedCertVerifier for CertVerifier {
async fn verify( async fn verify<'c>(
&self, &self,
cert: &tokio_gemini::certs::CertificateDer<'_>, cert: &'c tokio_gemini::certs::CertificateDer<'c>,
host: &str, host: &str,
port: u16, port: u16,
) -> Result<bool, tokio_gemini::LibError> { ) -> Result<bool, tokio_gemini::LibError> {

View file

@ -4,9 +4,9 @@ use crate::{
LibError, LibError,
}; };
pub async fn dane( pub async fn dane<'d>(
dns: &DnsClient, dns: &DnsClient,
cert: &CertificateDer<'_>, cert: &CertificateDer<'d>,
host: &str, host: &str,
port: u16, port: u16,
) -> Result<CertFingerprint, LibError> { ) -> Result<CertFingerprint, LibError> {

View file

@ -17,9 +17,9 @@ pub use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
/// It is recommended to use helpers from file_sscv. /// It is recommended to use helpers from file_sscv.
#[async_trait] #[async_trait]
pub trait SelfsignedCertVerifier: Send + Sync { pub trait SelfsignedCertVerifier: Send + Sync {
async fn verify( async fn verify<'c>(
&self, &self,
cert: &CertificateDer<'_>, cert: &'c CertificateDer<'c>,
host: &str, host: &str,
port: u16, port: u16,
// now: UnixTime, // now: UnixTime,

View file

@ -25,7 +25,7 @@ use tokio::{
io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt}, io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt},
net::TcpStream, net::TcpStream,
}; };
use tokio_rustls::{rustls, TlsConnector}; use tokio_rustls::TlsConnector;
use url::Url; use url::Url;
pub struct Client { pub struct Client {
@ -101,20 +101,6 @@ impl Client {
let stream = self.try_connect(host, port).await?; let stream = self.try_connect(host, port).await?;
let mut stream = self.connector.connect(domain, stream).await?; let mut stream = self.connector.connect(domain, stream).await?;
if let Some(ssv) = &self.ss_verifier {
let cert = stream
.get_ref()
.1 // rustls::ClientConnection
.peer_certificates()
.unwrap() // i think handshake already completed if we awaited on connector.connect?
.first()
.ok_or(rustls::Error::NoCertificatesPresented)?;
if !ssv.verify(cert, host, port).await? {
return Err(rustls::CertificateError::ApplicationVerificationFailure.into());
}
}
// Write URL, then CRLF // Write URL, then CRLF
stream.write_all(url_str.as_bytes()).await?; stream.write_all(url_str.as_bytes()).await?;
stream.write_all(b"\r\n").await?; stream.write_all(b"\r\n").await?;

View file

@ -89,9 +89,7 @@ impl DnsClient {
Ok(answers.into_iter().filter_map(|rec| { Ok(answers.into_iter().filter_map(|rec| {
if let Some(RData::TLSA(tlsa)) = rec.data() { if let Some(RData::TLSA(tlsa)) = rec.data() {
if tlsa.cert_usage() == CertUsage::DomainIssued if tlsa.cert_usage() == CertUsage::DomainIssued
// maybe implement extracting public key later, && tlsa.selector() == Selector::Spki
// but for now only accept TLSA records with full certs hashed
&& tlsa.selector() == Selector::Full
{ {
match tlsa.matching() { match tlsa.matching() {
Matching::Sha256 => CertFingerprint::try_from_sha256(tlsa.cert_data()) Matching::Sha256 => CertFingerprint::try_from_sha256(tlsa.cert_data())

View file

@ -1,11 +1,11 @@
//! Library error structures and enums //! Library error structures and enums
use tokio_rustls::rustls;
#[cfg(feature = "hickory")] #[cfg(feature = "hickory")]
use hickory_client::{ use hickory_client::{
error::ClientError as HickoryClientError, proto::error::ProtoError as HickoryProtoError, error::ClientError as HickoryClientError, proto::error::ProtoError as HickoryProtoError,
}; };
#[cfg(feature = "hickory")]
use tokio::runtime::TryCurrentError;
/// Main error structure, also a wrapper for everything else /// Main error structure, also a wrapper for everything else
#[derive(Debug)] #[derive(Debug)]
@ -15,12 +15,9 @@ pub enum LibError {
IoError(std::io::Error), IoError(std::io::Error),
/// URL parse or check error /// URL parse or check error
InvalidUrlError(InvalidUrl), InvalidUrlError(InvalidUrl),
/// DNS server has provided no suitable records /// DNS provided no suitable records
/// (e.&nbsp;g. domain does not exist) /// (e.&nbsp;g. domain does not exist)
HostLookupError, HostLookupError,
/// TLS library error related to certificate/signature
/// verification failure or connection failure
RustlsError(rustls::Error),
/// Response status code is out of [10; 69] range /// Response status code is out of [10; 69] range
StatusOutOfRange(u8), StatusOutOfRange(u8),
/// Response metadata or content cannot be parsed /// Response metadata or content cannot be parsed
@ -28,9 +25,16 @@ pub enum LibError {
DataNotUtf8(std::string::FromUtf8Error), DataNotUtf8(std::string::FromUtf8Error),
/// Provided string is not a valid MIME type /// Provided string is not a valid MIME type
InvalidMime(mime::FromStrError), InvalidMime(mime::FromStrError),
/// Hickory DNS client error /// Hickory Client error
#[cfg(feature = "hickory")] #[cfg(feature = "hickory")]
DnsClientError(HickoryClientError), DnsClientError(HickoryClientError),
/// Hickory Proto error
#[cfg(feature = "hickory")]
DnsProtoError(HickoryProtoError),
/// Could not get Tokio runtime handle
/// inside Rustls cert verifier
#[cfg(feature = "hickory")]
NoTokioRuntime(TryCurrentError),
} }
impl From<std::io::Error> for LibError { impl From<std::io::Error> for LibError {
@ -54,20 +58,6 @@ impl From<InvalidUrl> for LibError {
} }
} }
impl From<rustls::Error> for LibError {
#[inline]
fn from(err: rustls::Error) -> Self {
Self::RustlsError(err)
}
}
impl From<rustls::CertificateError> for LibError {
#[inline]
fn from(err: rustls::CertificateError) -> Self {
Self::RustlsError(err.into())
}
}
impl LibError { impl LibError {
#[inline] #[inline]
pub fn status_out_of_range(num: u8) -> Self { pub fn status_out_of_range(num: u8) -> Self {
@ -101,7 +91,15 @@ impl From<HickoryClientError> for LibError {
impl From<HickoryProtoError> for LibError { impl From<HickoryProtoError> for LibError {
#[inline] #[inline]
fn from(err: HickoryProtoError) -> Self { fn from(err: HickoryProtoError) -> Self {
Self::DnsClientError(err.into()) Self::DnsProtoError(err)
}
}
#[cfg(feature = "hickory")]
impl From<TryCurrentError> for LibError {
#[inline]
fn from(err: TryCurrentError) -> Self {
Self::NoTokioRuntime(err)
} }
} }