diff --git a/Cargo.toml b/Cargo.toml index 27d8051..1c6ea42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ggemini" -version = "0.18.1" +version = "0.19.0" edition = "2024" license = "MIT" readme = "README.md" diff --git a/README.md b/README.md index d81850e..03c633e 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,8 @@ fn main() -> ExitCode { }, Priority::DEFAULT, Cancellable::new(), - None, // optional `GTlsCertificate` + None, // optional auth `GTlsCertificate` + None, // optional TOFU `GTlsCertificate` array |result| match result { Ok((response, _connection)) => match response { Response::Success(success) => match success.mime().unwrap().as_str() { diff --git a/src/client.rs b/src/client.rs index cff557e..f11e9ec 100644 --- a/src/client.rs +++ b/src/client.rs @@ -59,7 +59,8 @@ impl Client { request: Request, priority: Priority, cancellable: Cancellable, - certificate: Option, + client_certificate: Option, + server_certificates: Option>, callback: impl FnOnce(Result<(Response, Connection), Error>) + 'static, ) { // Begin new connection @@ -75,7 +76,8 @@ impl Client { match Connection::build( socket_connection, network_address, - certificate, + client_certificate, + server_certificates, is_session_resumption, ) { Ok(connection) => connection.request_async( diff --git a/src/client/connection.rs b/src/client/connection.rs index d1cd849..b7833a7 100644 --- a/src/client/connection.rs +++ b/src/client/connection.rs @@ -6,11 +6,9 @@ pub use error::Error; pub use request::{Mode, Request}; pub use response::Response; -// Local dependencies - use gio::{ Cancellable, IOStream, NetworkAddress, SocketConnection, TlsCertificate, TlsClientConnection, - prelude::{IOStreamExt, OutputStreamExtManual, TlsConnectionExt}, + prelude::{IOStreamExt, OutputStreamExtManual, TlsCertificateExt, TlsConnectionExt}, }; use glib::{ Bytes, Priority, @@ -30,17 +28,19 @@ impl Connection { pub fn build( socket_connection: SocketConnection, network_address: NetworkAddress, - certificate: Option, + client_certificate: Option, + server_certificates: Option>, is_session_resumption: bool, ) -> Result { Ok(Self { tls_client_connection: match new_tls_client_connection( &socket_connection, Some(&network_address), + server_certificates, is_session_resumption, ) { Ok(tls_client_connection) => { - if let Some(ref c) = certificate { + if let Some(ref c) = client_certificate { tls_client_connection.set_certificate(c); } tls_client_connection @@ -136,6 +136,7 @@ impl Connection { fn new_tls_client_connection( socket_connection: &SocketConnection, server_identity: Option<&NetworkAddress>, + server_certificates: Option>, is_session_resumption: bool, ) -> Result { match TlsClientConnection::new(socket_connection, server_identity) { @@ -149,9 +150,19 @@ fn new_tls_client_connection( // https://geminiprotocol.net/docs/protocol-specification.gmi#closing-connections tls_client_connection.set_require_close_notify(true); - // @TODO validate - // https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation - tls_client_connection.connect_accept_certificate(|_, _, _| true); + // [TOFU](https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation) + tls_client_connection.connect_accept_certificate(move |_, c, _| { + server_certificates + .as_ref() + .is_none_or(|server_certificates| { + for server_certificate in server_certificates { + if server_certificate.is_same(c) { + return true; + } + } + false + }) + }); Ok(tls_client_connection) }