diff options
author | Henauxg <19689618+Henauxg@users.noreply.github.com> | 2022-11-17 17:50:19 +0100 |
---|---|---|
committer | Henauxg <19689618+Henauxg@users.noreply.github.com> | 2022-11-17 17:50:19 +0100 |
commit | e3076eb17ab306241b876c9aa5876af040d1093d (patch) | |
tree | 10c7fb00fbb05f3f872f887781c0bfebbbc259c6 /src | |
parent | f55f36ab2bf15a575e22d2fda67d6ec8dec0dc41 (diff) |
[tests] Add trust_on_first_use test (incomplete)
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 298 |
1 files changed, 296 insertions, 2 deletions
@@ -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( |