aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHenauxg <19689618+Henauxg@users.noreply.github.com>2022-11-17 17:50:19 +0100
committerHenauxg <19689618+Henauxg@users.noreply.github.com>2022-11-17 17:50:19 +0100
commite3076eb17ab306241b876c9aa5876af040d1093d (patch)
tree10c7fb00fbb05f3f872f887781c0bfebbbc259c6 /src
parentf55f36ab2bf15a575e22d2fda67d6ec8dec0dc41 (diff)
[tests] Add trust_on_first_use test (incomplete)
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs298
1 files changed, 296 insertions, 2 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 0f4227a..e25c68a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,8 +18,13 @@ mod tests {
use crate::{
client::{
- self, certificate::CertificateVerificationMode, Client, ConnectionConfiguration,
- QuinnetClientPlugin,
+ self,
+ certificate::{
+ CertConnectionAbortEvent, CertInteractionEvent, CertTrustUpdateEvent,
+ CertVerificationInfo, CertVerificationStatus, CertVerifierAction,
+ CertificateVerificationMode,
+ },
+ Client, ConnectionConfiguration, QuinnetClientPlugin,
},
server::{
self,
@@ -36,10 +41,24 @@ mod tests {
const SERVER_HOST: &str = "127.0.0.1";
const SERVER_PORT: u16 = 6000;
+ const TEST_CERT_FILE: &str = "assets/tests/test_cert.pem";
+ const TEST_KEY_FILE: &str = "assets/tests/test_key.pem";
+ const TEST_CERT_FINGERPRINT_B64: &str = "sieQJ9J6DIrQP37HAlUFk2hYhLZDY9G5OZQpqzkWlKo=";
#[derive(Resource, Debug, Clone, Default)]
struct ClientTestData {
connection_events_received: u64,
+
+ cert_trust_update_events_received: u64,
+ last_trusted_cert_info: Option<CertVerificationInfo>,
+
+ cert_interactions_received: u64,
+ last_cert_interactions_status: Option<CertVerificationStatus>,
+ last_cert_interactions_info: Option<CertVerificationInfo>,
+
+ cert_verif_connection_abort_events_received: u64,
+ last_abort_cert_status: Option<CertVerificationStatus>,
+ last_abort_cert_info: Option<CertVerificationInfo>,
}
#[derive(Resource, Debug, Clone, Default)]
@@ -144,6 +163,254 @@ mod tests {
}
}
+ #[test]
+ fn trust_on_first_use() {
+ // TOFU With default parameters
+ // Server listens with a cert loaded from a file
+ // Client connects with empty cert store
+ // -> The server's certificate is treatead as Unknown by the client, which stores it and continues the connection
+ // Clients disconnects
+ // Client reconnects with the updated cert store
+ // -> The server's certificate is treatead as Trusted by the client, which continues the connection
+ // Clients disconnects
+ // Server reboots, and generates a new self-signed certificate
+ // Client reconnects with its cert store
+ // -> The server's certificate is treatead as Untrusted by the client, which requests a client action
+ // We receive the client action request and ask to abort the connection
+
+ let mut client_app = App::new();
+ client_app
+ .add_plugin(ScheduleRunnerPlugin::default())
+ .add_plugin(QuinnetClientPlugin::default())
+ .insert_resource(ClientTestData::default())
+ .add_system(handle_client_events);
+
+ let mut server_app = App::new();
+ server_app
+ .add_plugin(ScheduleRunnerPlugin::default())
+ .add_plugin(QuinnetServerPlugin::default())
+ .insert_resource(ServerTestData::default())
+ .add_system(handle_server_events);
+
+ // Startup
+ client_app.update();
+ server_app.update();
+
+ // Server listens with a cert loaded from a file
+ {
+ let mut server = server_app.world.resource_mut::<Server>();
+ server
+ .start(
+ ServerConfigurationData::new(
+ SERVER_HOST.to_string(),
+ SERVER_PORT,
+ "0.0.0.0".to_string(),
+ ),
+ CertificateRetrievalMode::LoadFromFile {
+ cert_file: TEST_CERT_FILE.to_string(),
+ key_file: TEST_KEY_FILE.to_string(),
+ },
+ )
+ .unwrap();
+ }
+
+ // Client connects with empty cert store
+ {
+ let mut client = client_app.world.resource_mut::<Client>();
+ client.open_connection(
+ default_client_configuration(),
+ CertificateVerificationMode::TrustOnFirstUse(
+ client::certificate::TrustOnFirstUseConfig {
+ ..Default::default()
+ },
+ ),
+ );
+ }
+
+ // Let the async runtime connection connect.
+ sleep(Duration::from_secs_f32(0.1));
+
+ // Connection & event propagation
+ server_app.update();
+ client_app.update();
+
+ {
+ let mut server_test_data = server_app.world.resource_mut::<ServerTestData>();
+ assert_eq!(
+ TEST_CERT_FINGERPRINT_B64.to_string(),
+ server_test_data
+ .last_cert_fingeprint
+ .as_mut()
+ .expect("A certificate should have been loaded")
+ .to_base64()
+ );
+ assert_eq!(server_test_data.cert_events_received, 1);
+ }
+
+ // The server's certificate is treatead as Unknown by the client, which stores it and continues the connection
+ {
+ let mut client_test_data = client_app.world.resource_mut::<ClientTestData>();
+ assert_eq!(client_test_data.cert_trust_update_events_received, 1);
+ let cert_info = client_test_data
+ .last_trusted_cert_info
+ .as_mut()
+ .expect("A certificate trust update should have happened");
+ assert_eq!(
+ cert_info.fingerprint.to_base64(),
+ TEST_CERT_FINGERPRINT_B64.to_string()
+ );
+ assert!(cert_info.known_fingerprint.is_none());
+ assert_eq!(cert_info.server_name.to_string(), SERVER_HOST.to_string());
+
+ let mut client = client_app.world.resource_mut::<Client>();
+ assert!(
+ client.connection().is_connected(),
+ "The default connection should be connected to the server"
+ );
+
+ // Clients disconnects
+ // Client reconnects with the updated cert store
+ client
+ .close_all_connections()
+ .expect("Failed to close connections on the client");
+
+ client.open_connection(
+ default_client_configuration(),
+ CertificateVerificationMode::TrustOnFirstUse(
+ client::certificate::TrustOnFirstUseConfig {
+ ..Default::default()
+ },
+ ),
+ );
+ }
+
+ // Let the async runtime connection connect.
+ sleep(Duration::from_secs_f32(0.1));
+
+ // Connection & event propagation
+ server_app.update();
+ client_app.update();
+
+ {
+ let client_test_data = client_app.world.resource::<ClientTestData>();
+ assert_eq!(client_test_data.cert_trust_update_events_received, 1);
+
+ let mut client = client_app.world.resource_mut::<Client>();
+ assert!(
+ client.connection().is_connected(),
+ "The default connection should be connected to the server"
+ );
+
+ // Clients disconnects
+ client
+ .close_all_connections()
+ .expect("Failed to close connections on the client");
+ }
+
+ // Server reboots, and generates a new self-signed certificate
+ // TODO Close server endpoint here
+
+ {
+ let mut server = server_app.world.resource_mut::<Server>();
+ server
+ .start(
+ ServerConfigurationData::new(
+ SERVER_HOST.to_string(),
+ SERVER_PORT,
+ "0.0.0.0".to_string(),
+ ),
+ CertificateRetrievalMode::GenerateSelfSigned,
+ )
+ .unwrap();
+ }
+
+ // Client reconnects with its cert store
+ {
+ let mut client = client_app.world.resource_mut::<Client>();
+ assert!(
+ client.connection().is_connected() == false,
+ "The default connection should be disconnected from the server"
+ );
+ client.open_connection(
+ default_client_configuration(),
+ CertificateVerificationMode::TrustOnFirstUse(
+ client::certificate::TrustOnFirstUseConfig {
+ ..Default::default()
+ },
+ ),
+ );
+ }
+
+ // Let the async runtime connection connect.
+ sleep(Duration::from_secs_f32(0.1));
+
+ // Connection & event propagation
+ server_app.update();
+ client_app.update();
+
+ let mut server_test_data = server_app.world.resource_mut::<ServerTestData>();
+ assert_eq!(server_test_data.cert_events_received, 1);
+ let generated_cert_fingerprint = server_test_data
+ .last_cert_fingeprint
+ .as_mut()
+ .expect("The server should have generated a self-signed certificate");
+
+ // The server's certificate is treatead as Untrusted by the client, which requests a client action
+ // We received the client action request and asked to abort the connection
+ {
+ let mut client_test_data = client_app.world.resource_mut::<ClientTestData>();
+ assert_eq!(client_test_data.cert_interactions_received, 1);
+ assert_eq!(
+ client_test_data.cert_verif_connection_abort_events_received,
+ 1
+ );
+
+ // Verify the cert info in the certificate interaction event
+ let interaction_cert_info = client_test_data
+ .last_cert_interactions_info
+ .as_mut()
+ .expect("A certificate interaction event should have happened during certificate verification");
+ // Verify that the cert we received is the one generated by the server
+ assert_eq!(
+ interaction_cert_info.fingerprint.to_base64(),
+ generated_cert_fingerprint.to_base64()
+ );
+ // Verify the known fingerprint
+ assert_eq!(
+ interaction_cert_info
+ .known_fingerprint
+ .as_mut()
+ .expect("There should be a known fingerprint in the store")
+ .to_base64(),
+ TEST_CERT_FINGERPRINT_B64.to_string()
+ );
+ assert_eq!(
+ interaction_cert_info.server_name.to_string(),
+ SERVER_HOST.to_string()
+ );
+ assert_eq!(
+ client_test_data.last_cert_interactions_status,
+ Some(CertVerificationStatus::UntrustedCertificate)
+ );
+
+ // Verify the cert info in the connection abort event
+ assert_eq!(
+ client_test_data.last_abort_cert_info,
+ client_test_data.last_cert_interactions_info
+ );
+ assert_eq!(
+ client_test_data.last_abort_cert_status,
+ Some(CertVerificationStatus::UntrustedCertificate)
+ );
+
+ let client = client_app.world.resource::<Client>();
+ assert!(
+ client.connection().is_connected() == false,
+ "The default connection should not be connected to the server"
+ );
+ }
+ }
+
fn build_client_app() -> App {
let mut client_app = App::new();
client_app
@@ -197,11 +464,38 @@ mod tests {
fn handle_client_events(
mut connection_events: EventReader<client::ConnectionEvent>,
+ mut cert_trust_update_events: EventReader<CertTrustUpdateEvent>,
+ mut cert_interaction_events: EventReader<CertInteractionEvent>,
+ mut cert_connection_abort_events: EventReader<CertConnectionAbortEvent>,
mut test_data: ResMut<ClientTestData>,
) {
for _connected_event in connection_events.iter() {
test_data.connection_events_received += 1;
}
+ for trust_update in cert_trust_update_events.iter() {
+ test_data.cert_trust_update_events_received += 1;
+ test_data.last_trusted_cert_info = Some(trust_update.cert_info.clone());
+ }
+ for cert_interaction in cert_interaction_events.iter() {
+ test_data.cert_interactions_received += 1;
+ test_data.last_cert_interactions_status = Some(cert_interaction.status.clone());
+ test_data.last_cert_interactions_info = Some(cert_interaction.info.clone());
+
+ match cert_interaction.status {
+ CertVerificationStatus::UnknownCertificate => todo!(),
+ CertVerificationStatus::UntrustedCertificate => {
+ cert_interaction
+ .apply_cert_verifier_action(CertVerifierAction::AbortConnection)
+ .expect("Failed to apply vert verification action");
+ }
+ CertVerificationStatus::TrustedCertificate => todo!(),
+ }
+ }
+ for connection_abort in cert_connection_abort_events.iter() {
+ test_data.cert_verif_connection_abort_events_received += 1;
+ test_data.last_abort_cert_status = Some(connection_abort.status.clone());
+ test_data.last_abort_cert_info = Some(connection_abort.cert_info.clone());
+ }
}
fn handle_server_events(