Compare commits
5 commits
e042a139bf
...
ef307f4983
Author | SHA1 | Date | |
---|---|---|---|
ef307f4983 | |||
04ebac7d6c | |||
220a4a3316 | |||
060aa4a1f7 | |||
49c46aeaf5 |
7 changed files with 53 additions and 31 deletions
|
@ -1,3 +1,4 @@
|
|||
use async_trait::async_trait;
|
||||
use tokio_gemini::{
|
||||
certs::{
|
||||
dane, file_sscv::KnownHostsFile, fingerprint::CertFingerprint, SelfsignedCertVerifier,
|
||||
|
@ -12,7 +13,7 @@ use tokio_gemini::{
|
|||
//
|
||||
|
||||
const USAGE: &str = "-k\t\tinsecure mode (trust all certs)
|
||||
-d <DNS server addr>\tuse custom DNS for resolving & DANE
|
||||
-d <DNS server>\tuse custom DNS for resolving & DANE
|
||||
-h\t\tshow help";
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -120,10 +121,11 @@ struct CertVerifier {
|
|||
dns: Option<DnsClient>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SelfsignedCertVerifier for CertVerifier {
|
||||
async fn verify<'c>(
|
||||
async fn verify(
|
||||
&self,
|
||||
cert: &'c tokio_gemini::certs::CertificateDer<'c>,
|
||||
cert: &tokio_gemini::certs::CertificateDer<'_>,
|
||||
host: &str,
|
||||
port: u16,
|
||||
) -> Result<bool, tokio_gemini::LibError> {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use async_trait::async_trait;
|
||||
use tokio_gemini::{
|
||||
certs::{fingerprint::CertFingerprint, SelfsignedCertVerifier},
|
||||
Client, LibError,
|
||||
|
@ -32,10 +33,11 @@ async fn main() -> Result<(), LibError> {
|
|||
|
||||
struct CertVerifier;
|
||||
|
||||
#[async_trait]
|
||||
impl SelfsignedCertVerifier for CertVerifier {
|
||||
async fn verify<'c>(
|
||||
async fn verify(
|
||||
&self,
|
||||
cert: &'c tokio_gemini::certs::CertificateDer<'c>,
|
||||
cert: &tokio_gemini::certs::CertificateDer<'_>,
|
||||
host: &str,
|
||||
port: u16,
|
||||
) -> Result<bool, tokio_gemini::LibError> {
|
||||
|
|
|
@ -4,9 +4,9 @@ use crate::{
|
|||
LibError,
|
||||
};
|
||||
|
||||
pub async fn dane<'d>(
|
||||
pub async fn dane(
|
||||
dns: &DnsClient,
|
||||
cert: &CertificateDer<'d>,
|
||||
cert: &CertificateDer<'_>,
|
||||
host: &str,
|
||||
port: u16,
|
||||
) -> Result<CertFingerprint, LibError> {
|
||||
|
|
|
@ -17,9 +17,9 @@ pub use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
|
|||
/// It is recommended to use helpers from file_sscv.
|
||||
#[async_trait]
|
||||
pub trait SelfsignedCertVerifier: Send + Sync {
|
||||
async fn verify<'c>(
|
||||
async fn verify(
|
||||
&self,
|
||||
cert: &'c CertificateDer<'c>,
|
||||
cert: &CertificateDer<'_>,
|
||||
host: &str,
|
||||
port: u16,
|
||||
// now: UnixTime,
|
||||
|
|
|
@ -25,7 +25,7 @@ use tokio::{
|
|||
io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpStream,
|
||||
};
|
||||
use tokio_rustls::TlsConnector;
|
||||
use tokio_rustls::{rustls, TlsConnector};
|
||||
use url::Url;
|
||||
|
||||
pub struct Client {
|
||||
|
@ -101,6 +101,20 @@ impl Client {
|
|||
let stream = self.try_connect(host, port).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
|
||||
stream.write_all(url_str.as_bytes()).await?;
|
||||
stream.write_all(b"\r\n").await?;
|
||||
|
|
|
@ -89,7 +89,9 @@ impl DnsClient {
|
|||
Ok(answers.into_iter().filter_map(|rec| {
|
||||
if let Some(RData::TLSA(tlsa)) = rec.data() {
|
||||
if tlsa.cert_usage() == CertUsage::DomainIssued
|
||||
&& tlsa.selector() == Selector::Spki
|
||||
// maybe implement extracting public key later,
|
||||
// but for now only accept TLSA records with full certs hashed
|
||||
&& tlsa.selector() == Selector::Full
|
||||
{
|
||||
match tlsa.matching() {
|
||||
Matching::Sha256 => CertFingerprint::try_from_sha256(tlsa.cert_data())
|
||||
|
|
42
src/error.rs
42
src/error.rs
|
@ -1,11 +1,11 @@
|
|||
//! Library error structures and enums
|
||||
|
||||
use tokio_rustls::rustls;
|
||||
|
||||
#[cfg(feature = "hickory")]
|
||||
use hickory_client::{
|
||||
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
|
||||
#[derive(Debug)]
|
||||
|
@ -15,9 +15,12 @@ pub enum LibError {
|
|||
IoError(std::io::Error),
|
||||
/// URL parse or check error
|
||||
InvalidUrlError(InvalidUrl),
|
||||
/// DNS provided no suitable records
|
||||
/// DNS server has provided no suitable records
|
||||
/// (e. g. domain does not exist)
|
||||
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
|
||||
StatusOutOfRange(u8),
|
||||
/// Response metadata or content cannot be parsed
|
||||
|
@ -25,16 +28,9 @@ pub enum LibError {
|
|||
DataNotUtf8(std::string::FromUtf8Error),
|
||||
/// Provided string is not a valid MIME type
|
||||
InvalidMime(mime::FromStrError),
|
||||
/// Hickory Client error
|
||||
/// Hickory DNS client error
|
||||
#[cfg(feature = "hickory")]
|
||||
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 {
|
||||
|
@ -58,6 +54,20 @@ 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 {
|
||||
#[inline]
|
||||
pub fn status_out_of_range(num: u8) -> Self {
|
||||
|
@ -91,15 +101,7 @@ impl From<HickoryClientError> for LibError {
|
|||
impl From<HickoryProtoError> for LibError {
|
||||
#[inline]
|
||||
fn from(err: HickoryProtoError) -> Self {
|
||||
Self::DnsProtoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "hickory")]
|
||||
impl From<TryCurrentError> for LibError {
|
||||
#[inline]
|
||||
fn from(err: TryCurrentError) -> Self {
|
||||
Self::NoTokioRuntime(err)
|
||||
Self::DnsClientError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue