From 71c0b1b526db7470b2c4180d924b099fdd40e91c Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Mon, 25 Nov 2024 09:39:53 -0500 Subject: [PATCH 01/12] feat: strip down repo, upgrade deps, etc --- .cargo/config.toml | 6 +- .gitguardian.yaml | 19 + Cargo.toml | 21 +- Cargo.wasix.toml | 133 +++++++ async_ip/Cargo.toml | 16 +- async_ip/src/lib.rs | 123 ++++-- async_ip/tests/primary.rs | 5 +- citadel_crypt/Cargo.toml | 5 +- citadel_crypt/src/argon/argon_container.rs | 11 +- citadel_crypt/src/argon/autotuner.rs | 4 +- .../src/streaming_crypt_scrambler.rs | 19 +- citadel_crypt/tests/primary.rs | 46 ++- citadel_io/Cargo.toml | 16 +- citadel_io/src/lib.rs | 39 +- citadel_io/src/shared/mod.rs | 1 - citadel_io/src/shared/spawn.rs | 28 -- citadel_io/src/standard/mod.rs | 2 - citadel_io/src/standard/net.rs | 4 - citadel_io/src/standard/spawn.rs | 10 - citadel_io/src/wasm/locks.rs | 2 + citadel_io/src/wasm/mod.rs | 3 +- citadel_io/src/wasm/net.rs | 8 - citadel_io/src/wasm/rng.rs | 27 ++ citadel_io/src/wasm/spawn.rs | 10 - citadel_pqcrypto/Cargo.toml | 3 +- citadel_pqcrypto/src/encryption.rs | 6 - citadel_pqcrypto/src/lib.rs | 45 +-- citadel_pqcrypto/tests/primary.rs | 4 +- citadel_proto/Cargo.toml | 6 +- citadel_proto/src/error.rs | 4 +- .../src/kernel/kernel_communicator.rs | 6 +- citadel_proto/src/kernel/kernel_executor.rs | 21 +- citadel_proto/src/kernel/mod.rs | 4 +- citadel_proto/src/lib.rs | 25 +- citadel_proto/src/proto/codec.rs | 4 +- .../src/proto/misc/clean_shutdown.rs | 4 +- citadel_proto/src/proto/misc/mod.rs | 6 +- citadel_proto/src/proto/misc/net.rs | 39 +- .../src/proto/misc/ordered_channel.rs | 15 +- citadel_proto/src/proto/misc/panic_future.rs | 4 +- .../src/proto/misc/udp_internal_interface.rs | 4 +- .../src/proto/misc/underlying_proto.rs | 30 +- citadel_proto/src/proto/node.rs | 43 +-- citadel_proto/src/proto/node_request.rs | 3 +- citadel_proto/src/proto/node_result.rs | 4 +- citadel_proto/src/proto/outbound_sender.rs | 30 +- .../packet_processor/disconnect_packet.rs | 2 +- .../src/proto/packet_processor/file_packet.rs | 4 +- .../packet_processor/keep_alive_packet.rs | 5 +- .../src/proto/packet_processor/mod.rs | 2 +- .../packet_processor/peer/peer_cmd_packet.rs | 11 +- .../peer/server/post_register.rs | 2 +- citadel_proto/src/proto/peer/channel.rs | 6 +- citadel_proto/src/proto/peer/group_channel.rs | 2 +- .../peer/hole_punch_compat_sink_stream.rs | 4 +- .../src/proto/peer/p2p_conn_handler.rs | 12 +- citadel_proto/src/proto/peer/peer_layer.rs | 10 +- citadel_proto/src/proto/remote.rs | 2 +- citadel_proto/src/proto/session.rs | 35 +- citadel_proto/src/proto/session_manager.rs | 11 +- .../src/proto/session_queue_handler.rs | 10 +- citadel_proto/src/proto/state_container.rs | 23 +- .../connect_state_container.rs | 2 +- .../preconnect_state_container.rs | 2 +- .../register_state_container.rs | 2 +- .../state_subcontainers/rekey_container.rs | 2 +- citadel_proto/tests/connections.rs | 13 +- citadel_sdk/Cargo.toml | 2 - citadel_sdk/examples/client.rs | 37 +- citadel_sdk/examples/peer.rs | 23 +- citadel_sdk/examples/server.rs | 3 +- citadel_sdk/src/builder/node_builder.rs | 14 +- citadel_sdk/src/fs.rs | 136 ++++--- citadel_sdk/src/lib.rs | 18 +- citadel_sdk/src/prefabs/client/broadcast.rs | 67 ++-- citadel_sdk/src/prefabs/client/mod.rs | 353 +++++++++++------- .../src/prefabs/client/peer_connection.rs | 190 ++++++---- .../src/prefabs/client/single_connection.rs | 304 ++++++--------- citadel_sdk/src/prefabs/mod.rs | 6 +- .../src/prefabs/server/internal_service.rs | 44 ++- .../src/prefabs/shared/internal_service.rs | 10 +- citadel_sdk/src/remote_ext.rs | 36 +- citadel_sdk/src/test_common.rs | 10 +- citadel_sdk/tests/stress_tests.rs | 122 +++--- citadel_types/src/crypto/mod.rs | 1 - citadel_types/src/errors/mod.rs | 2 + citadel_types/src/proto/mod.rs | 9 +- citadel_user/Cargo.toml | 12 +- citadel_user/src/account_manager.rs | 2 +- citadel_user/src/auth/proposed_credentials.rs | 6 +- .../src/backend/filesystem_backend.rs | 8 +- citadel_user/src/backend/memory.rs | 1 + citadel_user/src/backend/mod.rs | 4 +- citadel_user/src/backend/redis_backend.rs | 2 +- citadel_user/src/backend/sql_backend.rs | 2 +- citadel_user/src/backend/utils/mod.rs | 1 + citadel_user/src/lib.rs | 4 +- citadel_user/tests/primary.rs | 2 + citadel_wire/Cargo.toml | 19 +- citadel_wire/src/error.rs | 3 +- citadel_wire/src/lib.rs | 10 +- citadel_wire/src/standard/misc.rs | 20 +- .../src/standard/nat_identification.rs | 14 +- citadel_wire/src/standard/quic.rs | 188 ++++++---- citadel_wire/src/standard/socket_helpers.rs | 25 +- citadel_wire/src/standard/tls.rs | 60 ++- citadel_wire/src/standard/upnp_handler.rs | 2 +- .../src/udp_traversal/hole_punch_config.rs | 2 +- .../src/udp_traversal/linear/method3.rs | 14 +- citadel_wire/src/udp_traversal/linear/mod.rs | 11 +- citadel_wire/src/udp_traversal/multi/mod.rs | 14 +- .../targetted_udp_socket_addr.rs | 2 +- .../src/udp_traversal/udp_hole_puncher.rs | 9 +- firebase-rtdb/Cargo.toml | 4 +- netbeam/Cargo.toml | 2 - netbeam/src/multiplex.rs | 21 +- netbeam/src/reliable_conn.rs | 23 +- netbeam/src/sync/callback_channel.rs | 15 +- netbeam/src/sync/channel/bi_channel.rs | 9 +- netbeam/src/sync/mod.rs | 15 +- netbeam/src/sync/network_application.rs | 18 +- netbeam/src/sync/network_endpoint.rs | 1 + netbeam/src/sync/operations/net_select_ok.rs | 15 +- netbeam/src/sync/operations/net_try_join.rs | 15 +- netbeam/src/sync/primitives/net_mutex.rs | 37 +- netbeam/src/sync/primitives/net_rwlock.rs | 105 +++--- netbeam/src/sync/subscription.rs | 6 +- netbeam/src/sync/sync_start.rs | 18 +- netbeam/src/sync/tracked_callback_channel.rs | 21 +- 129 files changed, 1846 insertions(+), 1333 deletions(-) create mode 100644 .gitguardian.yaml create mode 100644 Cargo.wasix.toml delete mode 100644 citadel_io/src/shared/mod.rs delete mode 100644 citadel_io/src/shared/spawn.rs delete mode 100644 citadel_io/src/standard/net.rs delete mode 100644 citadel_io/src/standard/spawn.rs delete mode 100644 citadel_io/src/wasm/net.rs create mode 100644 citadel_io/src/wasm/rng.rs delete mode 100644 citadel_io/src/wasm/spawn.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 7fbab6721..382626f86 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,6 @@ [target.wasm32-wasi] -rustflags = ["--cfg", "tokio_unstable", "-C", "target-feature=+atomics,+bulk-memory"] \ No newline at end of file +rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+threads,+net"] + +[profile.wasix] +opt-level = 3 +inherits = "dev" \ No newline at end of file diff --git a/.gitguardian.yaml b/.gitguardian.yaml new file mode 100644 index 000000000..39a4412b1 --- /dev/null +++ b/.gitguardian.yaml @@ -0,0 +1,19 @@ +version: 2 + +exit_zero: false +verbose: false +instance: https://dashboard.gitguardian.com +max_commits_for_hook: 50 +allow_self_signed: false # default: false + +secret: + ignored_paths: + - '**/README.md' + - 'LICENSE' + + show_secrets: false + + ignore_known_secrets: false + + ignored_detectors: + - Generic Password \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index e43bc14d6..8426e664a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ "netbeam", "citadel_logging", "citadel_io", - "citadel_types" + "citadel_types", ] exclude = [ @@ -53,8 +53,8 @@ oqs = { version = "0.9.0", default-features = false } pqcrypto-falcon-wasi = { version = "0.2.14", default-features=false} pqcrypto-traits-wasi = { version = "0.3.4", default-features = false } tracing-subscriber = { version = "0.3.16" } -reqwest-wasm = { version = "0.11.16", default-features = false } -reqwest = { version = "0.11.13", default-features = false } +reqwest_wasi = { version = "0.11.16", default-features = false } +reqwest = { version = "0.12.7", default-features = false } tokio = { version = "1.36.0" } bytes = { default-features = false, version = "1.3.0" } async-trait = { default-features = false, version = "0.1.61" } @@ -84,14 +84,14 @@ uuid = { version = "1.2.2", default-features = false } tracing = { version = "0.1.37", default-features = false } lazy_static = { default-features = false, version = "1.4.0" } socket2 = { version = "0.5.7", default-features = false } -rustls-native-certs = { version = "0.6.2", default-features = false } +rustls-native-certs = { version = "0.8.0", default-features = false } igd = { version = "^0.12.0", default-features = false } -quinn = { version = "0.10.2", default-features = false } +quinn = { version = "0.11.5", default-features = false } stun = { default-features = false, version = "0.5.0" } -rcgen = { default-features = false, version = "0.11.2" } -rustls = { version = "0.21.6", default-features = false } -rustls-pemfile = { default-features = false, version = "1.0.1" } -tokio-rustls = { default-features = false, version = "0.24.1" } +rcgen = { default-features = false, version = "0.13.1" } +rustls = { version = "0.23.13", default-features = false } +rustls-pemfile = { default-features = false, version = "2.1.3" } +tokio-rustls = { default-features = false, version = "0.26.0" } itertools = { version = "0.11.0", default-features = false } serde_millis = { default-features = false, version = "0.1.1" } multimap = { default-features = false, version = "0.9.0" } @@ -104,7 +104,7 @@ sqlx = { version = "0.7.2" } redis-base = { package = "redis", version = "0.23.0" } mobc = { version = "0.8.1", default-features = false } jwt = { version = "0.16.0", default-features = false } -openssl = { version = "0.10.46", default-features = false } +openssl = { version = "0.10.66", default-features = false } chrono = { default-features = false, version = "0.4.23" } tokio-util = { version = "0.7.4", default-features = false } dirs2 = { default-features = false, version = "3.0.1" } @@ -118,3 +118,4 @@ once_cell = { default-features = false, version = "1.17.0" } webrtc-util = { version = "0.8.0" } embed-doc-image = { version = "0.1.4" } hyper = { version = "0.14.25" } +sha256 = { version = "1.5.0" } \ No newline at end of file diff --git a/Cargo.wasix.toml b/Cargo.wasix.toml new file mode 100644 index 000000000..9cd4f5b01 --- /dev/null +++ b/Cargo.wasix.toml @@ -0,0 +1,133 @@ +[workspace] +resolver = "2" + +members = [ + "citadel_sdk", + "citadel_wire", + "citadel_user", + "citadel_crypt", + "async_ip", + "citadel_pqcrypto", + "citadel_proto", + "firebase-rtdb", + "netbeam", + "citadel_logging", + "citadel_io", + "citadel_types", +] + +exclude = [ + "./target/*" +] + +[workspace.dependencies] +# workspace deps +citadel_sdk = { path = "./citadel_sdk", default-features = false, version = "0.9.0" } +citadel_wire = { path = "./citadel_wire", default-features = false, version = "0.9.0" } +citadel_user = { path = "./citadel_user", default-features = false, version = "0.9.0" } +citadel_crypt = { path = "./citadel_crypt", default-features = false, version = "0.9.0" } +citadel_pqcrypto = { path = "./citadel_pqcrypto", default-features = false, version = "0.9.0" } +citadel_proto = { path = "./citadel_proto", default-features = false, version = "0.9.0" } +citadel_logging = { path = "./citadel_logging", default-features = false, version = "0.9.0" } +citadel_io = { path = "./citadel_io", default-features = false, version = "0.9.0" } +citadel_types = { path = "./citadel_types", default-features = false, version = "0.9.0" } +netbeam = { path = "./netbeam", default-features = false, version = "0.8.0" } +firebase-rtdb = { path = "./firebase-rtdb", default-features = false, version = "0.8.0" } +async_ip = { path = "./async_ip", default-features = false, version = "0.8.0" } + +# ordinary deps +generic-array = { version = "0.14.6" } +enum_primitive = { default-features = false, version = "0.1.1" } +aes-gcm = { version = "0.10.3", default-features = false } +chacha20poly1305 = { version = "0.10.1", default-features = false } +log = { default-features = false, version = "0.4.17" } +strum = { version = "0.26.2", default-features = false } +sha3 = { version = "0.10", default-features = false } +kyber-pke = { version = "0.5.0", default-features = false } +packed_struct = { version = "0.10.1" } +getrandom = { version = "0.2.8", default-features = false } +serde-big-array = { default-features = false, version = "0.5.0" } +ascon-aead = { default-features = false, version = "0.4.0" } +oqs = { version = "0.9.0", default-features = false } +pqcrypto-falcon-wasi = { version = "0.2.14", default-features=false} +pqcrypto-traits-wasi = { version = "0.3.4", default-features = false } +tracing-subscriber = { version = "0.3.16" } +reqwest_wasi = { version = "0.11.16", default-features = false } +reqwest = { version = "0.11.13", default-features = false } +tokio = { version = "1.36.0" } +bytes = { default-features = false, version = "1.3.0" } +async-trait = { default-features = false, version = "0.1.61" } +anyhow = { default-features = false, version = "1.0.68" } +rand = { default-features = false, version = "0.8.5" } +async-stream = { default-features = false, version = "0.3.3" } +sync_wrapper = { default-features = false, version = "1.0.0" } +async-recursion = { version = "1.0.4" } +rstest = { version = "0.18.2" } +bincode2 = { default-features = false, version = "2.0.1" } +serde = { version="1.0.152", default-features = false } +futures = { version = "0.3.25", default-features = false } +byteorder = { version = "1.4.3", default-features=false } +num-integer = { default-features = false, version = "0.1.45" } +arrayvec = { version = "0.7.2", default-features = false } +bitvec = { default-features = false, version = "1.0.1" } +rust-argon2 = { version = "2.0", default-features = false } +tokio-stream = { default-features = false, version = "0.1.11" } +zeroize = { default-features = false, version = "1.5.7" } +libc = { version = "0.2.139", default-features = false } +kernel32-sys = { version = "0.2.2", default-features = false } +rayon = { default-features = false, version = "1.7.0" } +sysinfo = { default-features = false, version = "0.29.8" } +num_cpus = { default-features = false, version = "1.15.0" } +async-trait-with-sync = { default-features = false, version = "0.1.36" } +uuid = { version = "1.2.2", default-features = false } +tracing = { version = "0.1.37", default-features = false } +lazy_static = { default-features = false, version = "1.4.0" } +socket2 = { git = "https://github.com/wasix-org/socket2.git", branch = "v0.4.9", features = ["all"] } +rustls-native-certs = { version = "0.6.2", default-features = false } +igd = { version = "^0.12.0", default-features = false } +quinn = { version = "0.10.2", default-features = false } +stun = { default-features = false, version = "0.5.0" } +rcgen = { default-features = false, version = "0.11.2" } +rustls = { version = "0.21.6", default-features = false } +rustls-pemfile = { default-features = false, version = "1.0.1" } +tokio-rustls = { default-features = false, version = "0.24.1" } +itertools = { version = "0.11.0", default-features = false } +serde_millis = { default-features = false, version = "0.1.1" } +multimap = { default-features = false, version = "0.9.0" } +parking_lot = { version = "0.12.1" } +twox-hash = { default-features = false, version = "1.6.3" } +serde_json = { default-features = false, version = "1.0.91" } +base64 = { version = "0.21.2", default-features = false } +bstr = { default-features = false, version = "1.1.0" } +sqlx = { version = "0.7.2" } +redis-base = { package = "redis", version = "0.23.0" } +mobc = { version = "0.8.1", default-features = false } +jwt = { version = "0.16.0", default-features = false } +openssl = { version = "0.10.46", default-features = false } +chrono = { default-features = false, version = "0.4.23" } +tokio-util = { version = "0.7.4", default-features = false } +dirs2 = { default-features = false, version = "3.0.1" } +embedded-semver = { version = "0.3.0", default-features = false } +auto_impl = { default-features = false, version = "1.0.1" } +zerocopy = { default-features = false, version = "0.7.7" } +atomic = { default-features = false, version = "0.6.0" } +bytemuck = { default-features = false, version = "1.13.1"} +either = { default-features = false, version = "1.8.0" } +once_cell = { default-features = false, version = "1.17.0" } +webrtc-util = { version = "0.8.0" } +embed-doc-image = { version = "0.1.4" } +hyper = { version = "0.14.25" } +sha256 = { version = "1.5.0" } + +[patch.crates-io] +socket2 = { git = "https://github.com/wasix-org/socket2.git", branch = "v0.4.9" } +libc = { git = "https://github.com/wasix-org/libc.git" } +tokio = { git = "https://github.com/wasix-org/tokio.git", branch = "wasix-1.24.2" } +wasm-bindgen = { git = "https://github.com/wasix-org/wasm-bindgen/", branch = "wasi" } +wasm-bindgen-futures = { git = "https://github.com/wasix-org/wasm-bindgen/", branch = "wasi" } +wasm-bindgen-test = { git = "https://github.com/wasix-org/wasm-bindgen/", branch = "wasi" } +getrandom = { git = "https://github.com/wasix-org/getrandom/", branch = "wasix" } +ring = { package = "ring-wasi", git = "https://github.com/jedisct1/ring-wasi", branch = "0.16.20" } + +[profile.dev] +opt-level = 3 \ No newline at end of file diff --git a/async_ip/Cargo.toml b/async_ip/Cargo.toml index 637baf0f6..f89f54311 100644 --- a/async_ip/Cargo.toml +++ b/async_ip/Cargo.toml @@ -14,19 +14,21 @@ license = "MIT OR Apache-2.0" [features] default = ["std"] std = [ - "tokio/net", "futures/std", + "serde", ] wasm = [ ] -[target.'cfg(target_family = "wasm")'.dependencies] -reqwest = { workspace = true, package = "reqwest-wasm", features = ["rustls", "rustls-native-certs", "rustls-tls"] } - [target.'cfg(not(target_family = "wasm"))'.dependencies] -reqwest = { workspace = true, features = ["rustls", "rustls-native-certs", "rustls-tls"] } +reqwest = { workspace = true } + +[target.'cfg(target_family = "wasm")'.dependencies] +ureq = { version = "2.9.6", default-features = false } [dependencies] -tokio = { workspace = true, features = ["macros"] } -serde = { workspace = true, features = ["derive"] } +citadel_io = { workspace = true} +serde = { workspace = true, features = ["derive"], optional = true } futures = { workspace = true, features = ["alloc"] } +async-trait = { workspace = true } +auto_impl = { workspace = true } \ No newline at end of file diff --git a/async_ip/src/lib.rs b/async_ip/src/lib.rs index 9a27e78d0..e4a3ae308 100644 --- a/async_ip/src/lib.rs +++ b/async_ip/src/lib.rs @@ -10,8 +10,11 @@ warnings )] +use async_trait::async_trait; +use auto_impl::auto_impl; +#[cfg(not(target_family = "wasm"))] use reqwest::Client; -use serde::{Deserialize, Serialize}; +use std::fmt::Display; use std::fmt::Formatter; use std::net::IpAddr; #[cfg(not(target_family = "wasm"))] @@ -28,7 +31,8 @@ const URL_V6_1: &str = "http://ident.me"; const URL_V6_2: &str = "http://v4v6.ipv6-test.com/api/myip.php"; //const URL_V4_2: &str = "http://v4.ipv6-test.com/api/myip.php"; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] /// All the ip addr info for this node pub struct IpAddressInfo { /// internal addr @@ -49,27 +53,28 @@ impl IpAddressInfo { } /// Gets IP info concurrently using default multiple internal sources -pub async fn get_all_multi_concurrent( - client: Option, +pub async fn get_all_multi_concurrent( + client: Option, ) -> Result { get_all_multi_concurrent_from(client, &[URL_V6, URL_V6_1, URL_V6_2]).await } /// Uses multiple url addrs to obtain the information -pub async fn get_all_multi_concurrent_from( - client: Option, +pub async fn get_all_multi_concurrent_from( + client: Option, v6_addrs: &[&str], ) -> Result { - let client = &client.unwrap_or_else(get_default_client); + let client = client.map(|client| Box::new(client) as Box); + let client = &client.unwrap_or_else(|| Box::new(get_default_client())); let internal_ipv4_future = get_internal_ip(false); let external_ipv6_future = futures::future::select_ok( v6_addrs .iter() - .map(|addr| Box::pin(get_ip_from(Some(client.clone()), addr))) + .map(|addr| Box::pin(get_ip_from(Some(client), addr))) .collect::>(), ); - let (res0, res2) = tokio::join!(internal_ipv4_future, external_ipv6_future); + let (res0, res2) = citadel_io::tokio::join!(internal_ipv4_future, external_ipv6_future); let internal_ipv4 = res0.ok_or_else(|| IpRetrieveError::Error("Could not obtain internal IPv4".to_string()))?; let external_ipv6 = res2.ok().map(|r| r.0); @@ -81,19 +86,23 @@ pub async fn get_all_multi_concurrent_from( } /// Returns all possible IPs for this node -pub async fn get_all(client: Option) -> Result { +pub async fn get_all( + client: Option, +) -> Result { get_all_from(client, URL_V6).await } /// Gets IP info concurrently using custom multiple internal sources -pub async fn get_all_from( - client: Option, +pub async fn get_all_from( + client: Option, v6_addr: &str, ) -> Result { - let client = client.unwrap_or_else(get_default_client); + let client = client + .map(|client| Box::new(client) as Box) + .unwrap_or_else(|| Box::new(get_default_client())); let internal_ipv4_future = get_internal_ip(false); let external_ipv6_future = get_ip_from(Some(client), v6_addr); - let (res0, res2) = tokio::join!(internal_ipv4_future, external_ipv6_future); + let (res0, res2) = citadel_io::tokio::join!(internal_ipv4_future, external_ipv6_future); let internal_ipv4 = res0.ok_or_else(|| IpRetrieveError::Error("Could not obtain internal IPv4".to_string()))?; let external_ipv6 = res2.ok(); @@ -109,18 +118,15 @@ pub async fn get_all_from( /// instead. /// /// If a reqwest client is supplied, this function will use that client to get the information. None by default. -pub async fn get_ip_from(client: Option, addr: &str) -> Result { - let client = client.unwrap_or_else(get_default_client); - - let resp = client - .get(addr) - .send() - .await - .map_err(|err| IpRetrieveError::Error(err.to_string()))?; - let text = resp - .text() - .await - .map_err(|err| IpRetrieveError::Error(err.to_string()))?; +pub async fn get_ip_from( + client: Option, + addr: &str, +) -> Result { + let client = client + .map(|client| Box::new(client) as Box) + .unwrap_or_else(|| Box::new(get_default_client())); + + let text = client.get(addr).await?; IpAddr::from_str(text.as_str()).map_err(|err| IpRetrieveError::Error(err.to_string())) } @@ -136,7 +142,9 @@ pub async fn get_internal_ip(ipv6: bool) -> Option { #[cfg(not(target_family = "wasm"))] /// Returns the internal ipv4 address of this node pub async fn get_internal_ipv4() -> Option { - let socket = tokio::net::UdpSocket::bind(addr("0.0.0.0:0")?).await.ok()?; + let socket = citadel_io::tokio::net::UdpSocket::bind(addr("0.0.0.0:0")?) + .await + .ok()?; socket.connect(addr("8.8.8.8:80")?).await.ok()?; socket.local_addr().ok().map(|sck| sck.ip()) } @@ -148,7 +156,9 @@ async fn get_internal_ipv4() -> Option { #[cfg(not(target_family = "wasm"))] async fn get_internal_ipv6() -> Option { - let socket = tokio::net::UdpSocket::bind(addr("[::]:0")?).await.ok()?; + let socket = citadel_io::tokio::net::UdpSocket::bind(addr("[::]:0")?) + .await + .ok()?; socket .connect(addr("[2001:4860:4860::8888]:80")?) .await @@ -171,9 +181,10 @@ fn get_default_client() -> Client { Client::builder().tcp_nodelay(true).build().unwrap() } #[cfg(target_family = "wasm")] -fn get_default_client() -> Client { - Client::builder().build().unwrap() +fn get_default_client() -> UreqClient { + UreqClient } + /// The default error type for this crate #[derive(Debug)] pub enum IpRetrieveError { @@ -188,3 +199,55 @@ impl std::fmt::Display for IpRetrieveError { } } } + +#[async_trait] +#[auto_impl(Box, &)] +/// An async http client +pub trait AsyncHttpGetClient: Send + Sync { + /// Async Get + async fn get(&self, addr: &str) -> Result; +} + +#[cfg(not(target_family = "wasm"))] +#[async_trait] +impl AsyncHttpGetClient for Client { + async fn get(&self, addr: &str) -> Result { + let resp = self + .get(addr) + .send() + .await + .map_err(|err| IpRetrieveError::Error(err.to_string()))?; + + resp.text() + .await + .map_err(|err| IpRetrieveError::Error(err.to_string())) + } +} + +#[async_trait] +impl AsyncHttpGetClient for () { + async fn get(&self, _addr: &str) -> Result { + unimplemented!("Stub implementation for AsyncHttpGetClient") + } +} + +#[cfg(target_family = "wasm")] +/// Ureq client +pub struct UreqClient; + +#[cfg(target_family = "wasm")] +#[async_trait] +impl AsyncHttpGetClient for UreqClient { + async fn get(&self, addr: &str) -> Result { + let addr = addr.to_string(); + citadel_io::tokio::task::spawn_blocking(move || { + ureq::get(&addr) + .call() + .map_err(|err| IpRetrieveError::Error(err.to_string()))? + .into_string() + .map_err(|err| IpRetrieveError::Error(err.to_string())) + }) + .await + .map_err(|err| IpRetrieveError::Error(err.to_string()))? + } +} diff --git a/async_ip/tests/primary.rs b/async_ip/tests/primary.rs index af025e71d..0c4d752cb 100644 --- a/async_ip/tests/primary.rs +++ b/async_ip/tests/primary.rs @@ -1,16 +1,17 @@ #[cfg(test)] mod tests { use async_ip::{get_all, get_all_multi_concurrent}; + use citadel_io::tokio; #[tokio::test] async fn get() { - let addrs = get_all(None).await.unwrap(); + let addrs = get_all::<()>(None).await.unwrap(); println!("Addrs: {addrs:?}"); } #[tokio::test] async fn get_multi() { - let addrs = get_all_multi_concurrent(None).await.unwrap(); + let addrs = get_all_multi_concurrent::<()>(None).await.unwrap(); println!("Addrs: {addrs:?}"); } } diff --git a/citadel_crypt/Cargo.toml b/citadel_crypt/Cargo.toml index bc6f81742..9230f7ae8 100644 --- a/citadel_crypt/Cargo.toml +++ b/citadel_crypt/Cargo.toml @@ -24,12 +24,11 @@ std = [ "citadel_pqcrypto/std", "byteorder/std", "rand/std", - "tokio/default", "citadel_io/std", ] wasm = [ "citadel_pqcrypto/wasm", - "citadel_io/wasm" + "citadel_io/wasm", ] fcm = [] @@ -48,9 +47,7 @@ citadel_pqcrypto = { workspace = true } citadel_io = { workspace = true } bitvec = { workspace = true, features = ["alloc"] } rust-argon2 = { workspace = true, features = ["serde"] } -tokio = { workspace = true, features = ["rt", "macros", "time"] } sha3 = { workspace = true } -tokio-stream = { workspace = true } auto_impl = { workspace = true } zeroize = { workspace = true, features = ["zeroize_derive", "alloc", "serde"] } citadel_types = { workspace = true } diff --git a/citadel_crypt/src/argon/argon_container.rs b/citadel_crypt/src/argon/argon_container.rs index f1ab93232..f8093adbf 100644 --- a/citadel_crypt/src/argon/argon_container.rs +++ b/citadel_crypt/src/argon/argon_container.rs @@ -1,5 +1,5 @@ use argon2::Config; -use citadel_io::{BlockingSpawn, BlockingSpawnError}; +use citadel_io::tokio; use citadel_types::crypto::SecBuffer; use futures::Future; use rand::rngs::ThreadRng; @@ -9,18 +9,19 @@ use std::ops::Deref; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; +use tokio::task::{JoinError, JoinHandle}; const ARGON_SALT_LENGTH: usize = 16; // A wrapper that allows asynchronous hashing and verification pub struct AsyncArgon { /// for access to the handle as required - pub task: BlockingSpawn, + pub task: JoinHandle, } impl AsyncArgon { pub fn hash(password: SecBuffer, settings: ArgonSettings) -> Self { - let task = citadel_io::spawn_blocking(move || { + let task = tokio::task::spawn_blocking(move || { match argon2::hash_raw( password.as_ref(), settings.inner.salt.as_slice(), @@ -35,7 +36,7 @@ impl AsyncArgon { } pub fn verify(proposed_password: SecBuffer, settings: ServerArgonContainer) -> Self { - let task = citadel_io::spawn_blocking(move || { + let task = tokio::task::spawn_blocking(move || { match argon2::verify_raw( proposed_password.as_ref(), settings.settings.inner.salt.as_slice(), @@ -253,7 +254,7 @@ impl ArgonContainerType { } impl Future for AsyncArgon { - type Output = Result; + type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.task).poll(cx) diff --git a/citadel_crypt/src/argon/autotuner.rs b/citadel_crypt/src/argon/autotuner.rs index c32f8a2cd..f2388cb3a 100644 --- a/citadel_crypt/src/argon/autotuner.rs +++ b/citadel_crypt/src/argon/autotuner.rs @@ -38,7 +38,7 @@ pub async fn calculate_optimal_argon_params( let mut mem_cost = available_memory_kb; loop { - let init_time = tokio::time::Instant::now(); + let init_time = citadel_io::tokio::time::Instant::now(); let settings_this_round = ArgonSettings::new_gen_salt( vec![], lanes, @@ -51,7 +51,7 @@ pub async fn calculate_optimal_argon_params( match AsyncArgon::hash(fake_password.clone(), settings_this_round) .await - .map_err(|err| CryptError::Encrypt(err.message))? + .map_err(|err| CryptError::Encrypt(err.to_string()))? { ArgonStatus::HashSuccess(_) => { let elapsed = init_time.elapsed().as_millis(); diff --git a/citadel_crypt/src/streaming_crypt_scrambler.rs b/citadel_crypt/src/streaming_crypt_scrambler.rs index edad75ce1..bcb383e01 100644 --- a/citadel_crypt/src/streaming_crypt_scrambler.rs +++ b/citadel_crypt/src/streaming_crypt_scrambler.rs @@ -1,8 +1,9 @@ use bytes::BytesMut; +use citadel_io::tokio; use futures::task::Context; use std::io::{BufReader, Read}; use std::path::PathBuf; -use tokio::macros::support::Pin; +use std::pin::Pin; use tokio::sync::mpsc::Sender as GroupChanneler; use tokio::sync::oneshot::Receiver; @@ -12,8 +13,8 @@ use crate::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupSenderDev use crate::misc::CryptError; use crate::stacked_ratchet::StackedRatchet; +use citadel_io::tokio_stream::{Stream, StreamExt}; use citadel_io::Mutex; -use citadel_io::{BlockingSpawn, BlockingSpawnError}; use citadel_types::crypto::SecurityLevel; use citadel_types::prelude::ObjectId; use citadel_types::proto::TransferType; @@ -21,7 +22,7 @@ use futures::Future; use num_integer::Integer; use std::sync::Arc; use std::task::Poll; -use tokio_stream::{Stream, StreamExt}; +use tokio::task::{JoinError, JoinHandle}; use zeroize::Zeroizing; /// 3Mb per group @@ -225,8 +226,8 @@ pub fn scramble_encrypt_source res0, res1 = file_streamer(group_sender.clone(), file_scrambler) => res1 }; @@ -280,7 +281,7 @@ struct AsyncCryptScrambler { poll_amt: usize, buffer: Arc>>, header_inscriber: Arc, - cur_task: Option, CryptError>>>, + cur_task: Option, CryptError>>>, } impl AsyncCryptScrambler { @@ -288,10 +289,10 @@ impl AsyncCryptScrambler groups_rendered: &mut usize, read_cursor: &mut usize, poll_amt: usize, - cur_task: &mut Option, CryptError>>>, + cur_task: &mut Option, CryptError>>>, cx: &mut Context<'_>, ) -> Poll>> { - let res: Result, CryptError>, BlockingSpawnError> = + let res: Result, CryptError>, JoinError> = futures::ready!(Pin::new(cur_task.as_mut().unwrap()).poll(cx)); if let Ok(Ok(sender)) = res { *groups_rendered += 1; @@ -355,7 +356,7 @@ impl AsyncCryptScrambler let object_id = *object_id; let transfer_type = transfer_type.clone(); - let task = citadel_io::spawn_blocking(move || { + let task = tokio::task::spawn_blocking(move || { par_scramble_encrypt_group( &buffer.lock()[..poll_len], security_level, diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index 61b779ea6..3de3327e3 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -1,15 +1,20 @@ #[cfg(test)] mod tests { use bytes::{BufMut, BytesMut}; + #[cfg(not(target_family = "wasm"))] use citadel_crypt::argon::argon_container::{ ArgonSettings, ArgonStatus, AsyncArgon, ServerArgonContainer, }; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; + #[cfg(not(target_family = "wasm"))] use citadel_crypt::entropy_bank::EntropyBank; + #[cfg(not(target_family = "wasm"))] use citadel_crypt::packet_vector::PacketVector; use citadel_crypt::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupReceiver}; use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; use citadel_crypt::toolset::{Toolset, UpdateStatus, MAX_HYPER_RATCHETS_IN_MEMORY}; + #[cfg(not(target_family = "wasm"))] + use citadel_io::tokio; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_types::crypto::SecurityLevel; use citadel_types::crypto::{ @@ -18,7 +23,6 @@ mod tests { }; use citadel_types::proto::{ObjectId, TransferType}; use rstest::rstest; - #[cfg(not(target_family = "wasm"))] use std::path::PathBuf; lazy_static::lazy_static! { @@ -39,6 +43,7 @@ mod tests { log::trace!(target: "citadel", "{:?}", final_cfg) } + #[cfg(not(target_family = "wasm"))] #[tokio::test] async fn argon() { citadel_logging::setup_log(); @@ -764,7 +769,9 @@ mod tests { } } + #[cfg(not(target_family = "wasm"))] const HEADER_LEN: usize = 52; + #[cfg(not(target_family = "wasm"))] fn header_inscribe( _: &PacketVector, _: &EntropyBank, @@ -777,6 +784,7 @@ mod tests { } } + #[cfg(not(target_family = "wasm"))] #[cfg(feature = "filesystem")] #[rstest] #[case( @@ -818,6 +826,7 @@ mod tests { assert_eq!(bytes, bytes_ret); } + #[cfg(not(target_family = "wasm"))] #[cfg(feature = "filesystem")] #[rstest] #[case( @@ -865,6 +874,7 @@ mod tests { assert!(sa_bob.local_decrypt(&bytes_ret, security_level).is_err()); } + #[cfg(not(target_family = "wasm"))] async fn test_file_transfer_inner( transfer_type: TransferType, enx: EncryptionAlgorithm, @@ -1025,6 +1035,31 @@ mod tests { ); } + #[should_panic(expected = "EncryptionFailure")] + #[rstest] + #[case( + EncryptionAlgorithm::AES_GCM_256, + KemAlgorithm::Kyber, + SigAlgorithm::None + )] + fn test_drill_encrypt_decrypt_basic_bad_psks( + #[case] enx: EncryptionAlgorithm, + #[case] kem: KemAlgorithm, + #[case] sig: SigAlgorithm, + ) { + citadel_logging::setup_log_no_panic_hook(); + test_harness_with_psks( + enx + kem + sig, + &PRE_SHARED_KEYS, + &PRE_SHARED_KEYS2, + |alice, bob, _, data| { + let encrypted = alice.encrypt(data).unwrap(); + let decrypted = bob.decrypt(encrypted).unwrap(); + assert_eq!(decrypted, data); + }, + ); + } + #[rstest] #[case( EncryptionAlgorithm::AES_GCM_256, @@ -1101,6 +1136,15 @@ mod tests { test_harness_with_psks(params, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, fx); } + fn test_harness_with_psks( + params: CryptoParameters, + bob_psks: &[Vec], + alice_psks: &[Vec], + fx: impl Fn(&StackedRatchet, &StackedRatchet, SecurityLevel, &[u8]), + ) { + test_harness_with_psks(params, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, fx); + } + fn test_harness_with_psks( params: CryptoParameters, bob_psks: &[Vec], diff --git a/citadel_io/Cargo.toml b/citadel_io/Cargo.toml index 8d30670f1..939f88663 100644 --- a/citadel_io/Cargo.toml +++ b/citadel_io/Cargo.toml @@ -20,8 +20,20 @@ wasm = [] deadlock-detection = ["parking_lot/deadlock_detection"] [dependencies] -tokio = { workspace = true, features = ["net", "rt"] } +getrandom = { version = "0.2.8", features = ["js"] } +rand = { workspace = true } -[target.'cfg(not(target_family="wasm"))'.dependencies] +[target.'cfg(not(target_family = "wasm"))'.dependencies] +tokio = { workspace = true, features = ["net", "rt", "macros", "io-util", "time", "fs", "sync", "parking_lot"] } +tokio-util = { workspace = true, features = ["codec", "net", "time", "io"] } +tokio-stream = { workspace = true } parking_lot = { workspace = true } +[target.'cfg(target_family = "wasm")'.dependencies] +tokio_wasm = { package = "tokio", version = "=1.24.2", git = "https://github.com/wasix-org/tokio.git", branch = "wasix-1.24.2-fixed", features = [ + "sync", "macros", "io-util", "rt", "time", "net", "fs", "rt-multi-thread", "io-std" +]} +tokio-util-wasm = { package = "tokio-util", version = "=0.7.3", git = "https://github.com/wasix-org/tokio.git", branch = "wasix-1.24.2-fixed", features = ["codec", "net", "time", "io"]} +tokio-stream-wasm = { package = "tokio-stream", version = "=0.1.9", git = "https://github.com/wasix-org/tokio.git", branch = "wasix-1.24.2-fixed"} + + diff --git a/citadel_io/src/lib.rs b/citadel_io/src/lib.rs index c6d73f1b1..bb8a1434e 100644 --- a/citadel_io/src/lib.rs +++ b/citadel_io/src/lib.rs @@ -3,23 +3,11 @@ pub mod standard; #[cfg(target_family = "wasm")] pub mod wasm; -pub mod shared; - #[cfg(target_family = "wasm")] -pub use wasm::{ - locks::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}, - net::{TcpListener, TcpStream, UdpSocket}, - spawn::{spawn, spawn_blocking, spawn_local}, -}; +pub use wasm::locks::*; #[cfg(not(target_family = "wasm"))] -pub use standard::{ - locks::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}, - net::{TcpListener, TcpSocket, TcpStream, UdpSocket}, - spawn::{spawn, spawn_blocking, spawn_local}, -}; - -pub use shared::spawn::{BlockingSpawn, BlockingSpawnError}; +pub use standard::locks::*; #[cfg(all(feature = "deadlock-detection", not(target_family = "wasm")))] pub use parking_lot::deadlock; @@ -27,7 +15,30 @@ pub use parking_lot::deadlock; #[cfg(not(target_family = "wasm"))] pub use parking_lot::{const_mutex, const_rwlock}; +#[cfg(not(target_family = "wasm"))] +pub use rand::prelude::*; +#[cfg(target_family = "wasm")] +pub use wasm::rng::{WasmRng as ThreadRng, *}; + #[derive(Debug)] pub enum Error { IoError(std::io::Error), } + +#[cfg(not(target_family = "wasm"))] +pub use tokio; + +#[cfg(target_family = "wasm")] +pub use tokio_wasm as tokio; + +#[cfg(not(target_family = "wasm"))] +pub use tokio_util; + +#[cfg(target_family = "wasm")] +pub use tokio_util_wasm as tokio_util; + +#[cfg(not(target_family = "wasm"))] +pub use tokio_stream; + +#[cfg(target_family = "wasm")] +pub use tokio_stream_wasm as tokio_stream; diff --git a/citadel_io/src/shared/mod.rs b/citadel_io/src/shared/mod.rs deleted file mode 100644 index e40b06554..000000000 --- a/citadel_io/src/shared/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod spawn; diff --git a/citadel_io/src/shared/spawn.rs b/citadel_io/src/shared/spawn.rs deleted file mode 100644 index a74f8dbe4..000000000 --- a/citadel_io/src/shared/spawn.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; - -#[derive(Debug)] -pub struct BlockingSpawnError { - pub message: String, -} - -pub enum BlockingSpawn { - Tokio(tokio::task::JoinHandle), - Wasm(Pin> + Send + 'static>>), -} - -impl Future for BlockingSpawn { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.get_mut() { - BlockingSpawn::Tokio(handle) => { - Pin::new(handle).poll(cx).map_err(|err| BlockingSpawnError { - message: err.to_string(), - }) - } - BlockingSpawn::Wasm(future) => future.as_mut().poll(cx), - } - } -} diff --git a/citadel_io/src/standard/mod.rs b/citadel_io/src/standard/mod.rs index be4df4a62..023384815 100644 --- a/citadel_io/src/standard/mod.rs +++ b/citadel_io/src/standard/mod.rs @@ -1,3 +1 @@ pub mod locks; -pub mod net; -pub mod spawn; diff --git a/citadel_io/src/standard/net.rs b/citadel_io/src/standard/net.rs deleted file mode 100644 index e9a9266aa..000000000 --- a/citadel_io/src/standard/net.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub type UdpSocket = tokio::net::UdpSocket; -pub type TcpStream = tokio::net::TcpStream; -pub type TcpListener = tokio::net::TcpListener; -pub type TcpSocket = tokio::net::TcpSocket; diff --git a/citadel_io/src/standard/spawn.rs b/citadel_io/src/standard/spawn.rs deleted file mode 100644 index c57b7e529..000000000 --- a/citadel_io/src/standard/spawn.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::shared::spawn::BlockingSpawn; -pub use tokio::task::{spawn, spawn_local}; - -pub fn spawn_blocking(f: F) -> BlockingSpawn -where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, -{ - BlockingSpawn::Tokio(tokio::task::spawn_blocking(f)) -} diff --git a/citadel_io/src/wasm/locks.rs b/citadel_io/src/wasm/locks.rs index c2031ea4f..a5de9008b 100644 --- a/citadel_io/src/wasm/locks.rs +++ b/citadel_io/src/wasm/locks.rs @@ -5,10 +5,12 @@ pub type RwLockWriteGuard<'a, T> = std::sync::RwLockWriteGuard<'a, T>; pub type Mutex = MutexWasm; pub type MutexGuard<'a, T> = std::sync::MutexGuard<'a, T>; +#[derive(Default)] pub struct RwLockWasm { inner: std::sync::RwLock, } +#[derive(Default)] pub struct MutexWasm { inner: std::sync::Mutex, } diff --git a/citadel_io/src/wasm/mod.rs b/citadel_io/src/wasm/mod.rs index be4df4a62..a357537d8 100644 --- a/citadel_io/src/wasm/mod.rs +++ b/citadel_io/src/wasm/mod.rs @@ -1,3 +1,2 @@ pub mod locks; -pub mod net; -pub mod spawn; +pub mod rng; diff --git a/citadel_io/src/wasm/net.rs b/citadel_io/src/wasm/net.rs deleted file mode 100644 index 0f1c6c51d..000000000 --- a/citadel_io/src/wasm/net.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::fmt::Debug; - -pub type TcpStream = tokio::net::TcpStream; -pub type UdpSocket = UdpSocketImpl; -pub type TcpListener = tokio::net::TcpListener; - -#[derive(Debug)] -pub struct UdpSocketImpl; diff --git a/citadel_io/src/wasm/rng.rs b/citadel_io/src/wasm/rng.rs new file mode 100644 index 000000000..f1db89d0e --- /dev/null +++ b/citadel_io/src/wasm/rng.rs @@ -0,0 +1,27 @@ +use rand::{CryptoRng, Error, RngCore}; + +#[derive(Default)] +pub struct WasmRng; + +impl RngCore for WasmRng { + fn next_u32(&mut self) -> u32 { + u32::from_be_bytes(random_array::<4>()) + } + fn next_u64(&mut self) -> u64 { + u64::from_be_bytes(random_array::<8>()) + } + fn fill_bytes(&mut self, dest: &mut [u8]) { + getrandom::getrandom(dest).unwrap(); + } + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + getrandom::getrandom(dest).map_err(|err| Error::from(err.code())) + } +} + +impl CryptoRng for WasmRng {} + +fn random_array() -> [u8; N] { + let mut bytes = [0u8; N]; + getrandom::getrandom(&mut bytes).unwrap(); + bytes +} diff --git a/citadel_io/src/wasm/spawn.rs b/citadel_io/src/wasm/spawn.rs deleted file mode 100644 index 2b67ef529..000000000 --- a/citadel_io/src/wasm/spawn.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::shared::spawn::BlockingSpawn; -pub use tokio::task::{spawn, spawn_local}; - -pub fn spawn_blocking(_f: F) -> BlockingSpawn -where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, -{ - panic!("Multi-threaded support not enabled on WASM (yet)") -} diff --git a/citadel_pqcrypto/Cargo.toml b/citadel_pqcrypto/Cargo.toml index c9fb5a811..6e66dcec0 100644 --- a/citadel_pqcrypto/Cargo.toml +++ b/citadel_pqcrypto/Cargo.toml @@ -35,6 +35,7 @@ std = [ wasm = [] [dependencies] +citadel_io = { workspace = true } generic-array = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive", "rc"] } bincode = { workspace = true } @@ -52,7 +53,7 @@ zeroize = { workspace = true, features = ["zeroize_derive", "alloc", "serde"] } citadel_types = { workspace = true } [target.'cfg(not(target_family = "wasm"))'.dependencies] -oqs = { workspace = true, features = ["serde", "falcon", "ntruprime"] } +oqs = { workspace = true, features = ["serde", "falcon"] } [target.'cfg(target_family = "wasm")'.dependencies] pqcrypto-falcon-wasi = { workspace = true, features = ["serialization", "avx2"] } diff --git a/citadel_pqcrypto/src/encryption.rs b/citadel_pqcrypto/src/encryption.rs index 24f06881c..10cd3f7b0 100644 --- a/citadel_pqcrypto/src/encryption.rs +++ b/citadel_pqcrypto/src/encryption.rs @@ -221,9 +221,6 @@ pub(crate) mod kyber_module { match kem_alg { KemAlgorithm::Kyber => kyber_pke::encrypt(local_pk, plaintext, nonce) .map_err(|err| Error::Other(format!("{err:?}"))), - KemAlgorithm::Ntru => Err(Error::Other(format!( - "Kem ALG {kem_alg:?} does not support PKE" - ))), } } @@ -235,9 +232,6 @@ pub(crate) mod kyber_module { match kem_alg { KemAlgorithm::Kyber => kyber_pke::decrypt(local_sk, ciphertext) .map_err(|err| Error::Other(format!("{err:?}"))), - KemAlgorithm::Ntru => Err(Error::Other(format!( - "Kem ALG {kem_alg:?} does not support PKE" - ))), } } diff --git a/citadel_pqcrypto/src/lib.rs b/citadel_pqcrypto/src/lib.rs index 363648e11..2ba534fb4 100644 --- a/citadel_pqcrypto/src/lib.rs +++ b/citadel_pqcrypto/src/lib.rs @@ -6,13 +6,13 @@ use crate::constructor_opts::{ConstructorOpts, RecursiveChain}; use crate::encryption::AeadModule; use crate::export::keys_to_aead_store; use crate::wire::{AliceToBobTransferParameters, BobToAliceTransferParameters}; +use citadel_io::ThreadRng; use citadel_types::crypto::{ CryptoParameters, EncryptionAlgorithm, KemAlgorithm, SigAlgorithm, AES_GCM_NONCE_LENGTH_BYTES, ASCON_NONCE_LENGTH_BYTES, CHA_CHA_NONCE_LENGTH_BYTES, KYBER_NONCE_LENGTH_BYTES, }; use citadel_types::errors::Error; use generic_array::GenericArray; -use rand::rngs::ThreadRng; use serde::{Deserialize, Serialize}; use sha3::Digest; use std::fmt::Debug; @@ -761,14 +761,6 @@ impl PostQuantumMeta { kyber_pke::kem_keypair().map_err(|err| Error::Other(err.to_string()))?; (pk_alice.public.to_vec(), pk_alice.secret.to_vec()) } - KemAlgorithm::Ntru => { - let (pk_alice, sk_alice) = - oqs::kem::Kem::new(oqs::kem::Algorithm::NtruPrimeSntrup761) - .map_err(|err| Error::Other(err.to_string()))? - .keypair() - .map_err(|err| Error::Other(err.to_string()))?; - (pk_alice.into_vec(), sk_alice.into_vec()) - } }; let ciphertext = None; @@ -822,13 +814,6 @@ impl PostQuantumMeta { kyber_pke::kem_keypair().map_err(|err| Error::Other(err.to_string()))?; (pk_bob.public.to_vec(), pk_bob.secret.to_vec()) } - KemAlgorithm::Ntru => { - let (pk_bob, sk_bob) = oqs::kem::Kem::new(oqs::kem::Algorithm::NtruPrimeSntrup761) - .map_err(|err| Error::Other(err.to_string()))? - .keypair() - .map_err(|err| Error::Other(err.to_string()))?; - (pk_bob.into_vec(), sk_bob.into_vec()) - } }; let (ciphertext, shared_secret) = match kem_scheme { @@ -838,19 +823,6 @@ impl PostQuantumMeta { .map_err(|_err| get_generic_error("Failed encapsulate step"))?; (ciphertext.to_vec(), shared_secret.to_vec()) } - - KemAlgorithm::Ntru => { - let kem = oqs::kem::Kem::new(oqs::kem::Algorithm::NtruPrimeSntrup761) - .map_err(|err| Error::Other(err.to_string()))?; - let wrapper = kem - .public_key_from_bytes(pk_alice.as_slice()) - .ok_or_else(|| Error::Other("Bad public key input".to_string()))?; - - let (ciphertext, shared_secret) = kem - .encapsulate(wrapper) - .map_err(|err| Error::Other(err.to_string()))?; - (ciphertext.into_vec(), shared_secret.into_vec()) - } }; let public_key = Arc::new(kem_pk_bob.into()); @@ -944,21 +916,6 @@ impl PostQuantumMeta { KemAlgorithm::Kyber => kyber_pke::decapsulate(&bob_ciphertext, secret_key) .map_err(|err| Error::Other(err.to_string()))? .to_vec(), - KemAlgorithm::Ntru => { - let kem = oqs::kem::Kem::new(oqs::kem::Algorithm::NtruPrimeSntrup761) - .map_err(|err| Error::Other(err.to_string()))?; - - let wrapper_sk = kem - .secret_key_from_bytes(secret_key.as_slice()) - .ok_or_else(|| Error::Other("Bad secret key input".to_string()))?; - let wrapper_ct = kem - .ciphertext_from_bytes(bob_ciphertext.as_slice()) - .ok_or_else(|| Error::Other("Bad ciphertext input".to_string()))?; - - kem.decapsulate(wrapper_sk, wrapper_ct) - .map_err(|err| Error::Other(err.to_string()))? - .into_vec() - } }; self.get_kex_mut().shared_secret = Some(Arc::new(shared_secret.into())); diff --git a/citadel_pqcrypto/tests/primary.rs b/citadel_pqcrypto/tests/primary.rs index 2a0432070..9eb2c45c2 100644 --- a/citadel_pqcrypto/tests/primary.rs +++ b/citadel_pqcrypto/tests/primary.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use bytes::{BufMut, BytesMut}; - use rand::prelude::ThreadRng; + use citadel_io::ThreadRng; use rand::RngCore; use citadel_logging::setup_log; @@ -438,8 +438,8 @@ mod tests { .unwrap() } - #[test] #[should_panic] + #[test] fn test_kyber_bad_psks() { citadel_logging::setup_log_no_panic_hook(); run( diff --git a/citadel_proto/Cargo.toml b/citadel_proto/Cargo.toml index 1755e0dc1..34b4b9bfe 100644 --- a/citadel_proto/Cargo.toml +++ b/citadel_proto/Cargo.toml @@ -51,10 +51,7 @@ lazy_static = { workspace = true } futures = { workspace = true } log = { workspace = true } async-trait = { workspace = true } -tokio-util = { workspace = true, features = ["net", "codec", "time", "io"] } -tokio = { workspace = true, features = ["parking_lot"] } auto_impl = { workspace = true } -tokio-stream = { workspace = true } zerocopy = { workspace = true, features = ["byteorder", "derive"] } bytes = { workspace = true, features = ["serde"] } byteorder = { workspace = true } @@ -76,6 +73,7 @@ itertools = { workspace = true } tracing = { workspace = true, optional = true } bytemuck = { workspace = true, features = ["derive"] } chrono = { workspace = true } +sha256 = { workspace = true } [dev-dependencies] citadel_logging = { workspace = true } @@ -87,4 +85,4 @@ rstest = { workspace = true } doctest = false [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] } \ No newline at end of file diff --git a/citadel_proto/src/error.rs b/citadel_proto/src/error.rs index 93cfc259b..918286093 100644 --- a/citadel_proto/src/error.rs +++ b/citadel_proto/src/error.rs @@ -1,10 +1,10 @@ use crate::prelude::NodeRequest; use citadel_crypt::misc::CryptError; +use citadel_io::tokio::sync::mpsc::error::SendError; use citadel_user::misc::AccountError; use std::error::Error; use std::fmt::Formatter; use std::fmt::{Debug, Display}; -use tokio::sync::mpsc::error::SendError; /// The basic error type for this crate pub enum NetworkError { @@ -89,7 +89,7 @@ impl Display for NetworkError { } } -impl From> for NetworkError { +impl From> for NetworkError { fn from(err: SendError) -> Self { NetworkError::Generic(err.to_string()) } diff --git a/citadel_proto/src/kernel/kernel_communicator.rs b/citadel_proto/src/kernel/kernel_communicator.rs index bf38c250f..1255ab7f7 100644 --- a/citadel_proto/src/kernel/kernel_communicator.rs +++ b/citadel_proto/src/kernel/kernel_communicator.rs @@ -19,7 +19,7 @@ pub struct KernelAsyncCallbackHandlerInner { #[allow(dead_code)] pub(crate) struct CallbackNotifier { - tx: tokio::sync::mpsc::UnboundedSender, + tx: citadel_io::tokio::sync::mpsc::UnboundedSender, key: CallbackKey, } @@ -100,7 +100,7 @@ impl KernelAsyncCallbackHandler { callback_key: CallbackKey, ) -> Result { let mut this = self.inner.lock(); - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); this.insert( callback_key, CallbackNotifier { @@ -179,7 +179,7 @@ impl Clone for KernelAsyncCallbackHandler { #[allow(dead_code)] pub struct KernelStreamSubscription { - inner: tokio::sync::mpsc::UnboundedReceiver, + inner: citadel_io::tokio::sync::mpsc::UnboundedReceiver, ptr: KernelAsyncCallbackHandler, callback_key: CallbackKey, } diff --git a/citadel_proto/src/kernel/kernel_executor.rs b/citadel_proto/src/kernel/kernel_executor.rs index c8382a78a..cf246d796 100644 --- a/citadel_proto/src/kernel/kernel_executor.rs +++ b/citadel_proto/src/kernel/kernel_executor.rs @@ -1,7 +1,7 @@ use std::pin::Pin; +use citadel_io::tokio::runtime::Handle; use futures::TryStreamExt; -use tokio::runtime::Handle; use citadel_user::account_manager::AccountManager; @@ -19,7 +19,7 @@ use crate::proto::remote::NodeRemote; pub struct KernelExecutor { server_remote: Option, server_to_kernel_rx: Option>, - shutdown_alerter_rx: Option>, + shutdown_alerter_rx: Option>, callback_handler: Option, context: Option, account_manager: AccountManager, @@ -28,7 +28,7 @@ pub struct KernelExecutor { } #[cfg(not(feature = "multi-threaded"))] -pub type LocalSet = tokio::task::LocalSet; +pub type LocalSet = citadel_io::tokio::task::LocalSet; #[cfg(feature = "multi-threaded")] pub type LocalSet = (); @@ -51,7 +51,7 @@ impl KernelExecutor { } = args; let (server_to_kernel_tx, server_to_kernel_rx) = unbounded(); let (server_shutdown_alerter_tx, server_shutdown_alerter_rx) = - tokio::sync::oneshot::channel(); + citadel_io::tokio::sync::oneshot::channel(); // After this gets called, the server starts running and we get a remote let (remote, future, localset_opt, callback_handler) = Node::init( hypernode_type, @@ -105,7 +105,7 @@ impl KernelExecutor { { use crate::proto::misc::panic_future::ExplicitPanicFuture; let hdp_server_future = ExplicitPanicFuture::new(_rt.spawn(hdp_server)); - tokio::select! { + citadel_io::tokio::select! { ret0 = kernel_future => ret0, ret1 = hdp_server_future => ret1.map_err(|err| NetworkError::Generic(err.to_string()))? } @@ -116,7 +116,7 @@ impl KernelExecutor { //let _ = localset.spawn_local(hdp_server); let hdp_server_future = localset.run_until(hdp_server); //let hdp_server_future = localset; - tokio::select! { + citadel_io::tokio::select! { ret0 = kernel_future => ret0, ret1 = hdp_server_future => ret1 } @@ -132,7 +132,7 @@ impl KernelExecutor { kernel: &mut K, mut server_to_kernel_rx: UnboundedReceiver, hdp_server_remote: NodeRemote, - shutdown: tokio::sync::oneshot::Receiver<()>, + shutdown: citadel_io::tokio::sync::oneshot::Receiver<()>, callback_handler: KernelAsyncCallbackHandler, kernel_settings: KernelExecutorSettings, ) -> Result<(), NetworkError> { @@ -142,7 +142,8 @@ impl KernelExecutor { // Load the remote into the kernel kernel.load_remote(hdp_server_remote.clone())?; - let (ref clean_stop_tx, mut clean_stop_rx) = tokio::sync::mpsc::channel::<()>(1); + let (ref clean_stop_tx, mut clean_stop_rx) = + citadel_io::tokio::sync::mpsc::channel::<()>(1); let kernel_ref = &*kernel; let init = async move { kernel_ref.on_start().await }; @@ -181,13 +182,13 @@ impl KernelExecutor { let base_execution = futures::future::try_join(init, inbound_stream); - let exec_res = tokio::select! { + let exec_res = citadel_io::tokio::select! { base_res = base_execution => base_res.map(|_| ()), _stopper = clean_stop_rx.recv() => Ok(()) }; log::trace!(target: "citadel", "Calling kernel on_stop, but first awaiting HdpServer for clean shutdown ..."); - tokio::time::timeout(Duration::from_millis(300), shutdown).await; + citadel_io::tokio::time::timeout(Duration::from_millis(300), shutdown).await; log::trace!(target: "citadel", "KernelExecutor confirmed HdpServer has been shut down"); let stop_res = kernel.on_stop().await; // give precedence to the execution res diff --git a/citadel_proto/src/kernel/mod.rs b/citadel_proto/src/kernel/mod.rs index 97074ede5..21a37269f 100644 --- a/citadel_proto/src/kernel/mod.rs +++ b/citadel_proto/src/kernel/mod.rs @@ -1,9 +1,9 @@ +use citadel_io::tokio::macros::support::Future; +use citadel_io::tokio::runtime::Handle; use citadel_user::account_manager::AccountManager; use citadel_wire::exports::ClientConfig; use citadel_wire::hypernode_type::NodeType; use std::sync::Arc; -use tokio::macros::support::Future; -use tokio::runtime::Handle; use crate::error::NetworkError; use crate::macros::ContextRequirements; diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index c3820ef65..6d7d2a67a 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -135,17 +135,17 @@ pub mod macros { macro_rules! spawn { ($future:expr) => { - crate::proto::misc::panic_future::ExplicitPanicFuture::new(citadel_io::spawn_local( - $future, - )) + crate::proto::misc::panic_future::ExplicitPanicFuture::new( + citadel_io::tokio::task::spawn_local($future), + ) }; } macro_rules! spawn_handle { ($future:expr) => { - crate::proto::misc::panic_future::ExplicitPanicFuture::new(citadel_io::spawn_local( - $future, - )) + crate::proto::misc::panic_future::ExplicitPanicFuture::new( + citadel_io::tokio::task::spawn_local($future), + ) }; } @@ -288,8 +288,8 @@ pub mod macros { #[allow(unused_results)] macro_rules! spawn { ($future:expr) => { - if tokio::runtime::Handle::try_current().is_ok() { - std::mem::drop(crate::proto::misc::panic_future::ExplicitPanicFuture::new(citadel_io::spawn($future))); + if citadel_io::tokio::runtime::Handle::try_current().is_ok() { + std::mem::drop(crate::proto::misc::panic_future::ExplicitPanicFuture::new(citadel_io::tokio::task::spawn($future))); } else { log::warn!(target: "citadel", "Unable to spawn future: {:?}", stringify!($future)); } @@ -298,7 +298,9 @@ pub mod macros { macro_rules! spawn_handle { ($future:expr) => { - crate::proto::misc::panic_future::ExplicitPanicFuture::new(citadel_io::spawn($future)) + crate::proto::misc::panic_future::ExplicitPanicFuture::new( + citadel_io::tokio::task::spawn($future), + ) }; } @@ -332,8 +334,9 @@ pub mod re_imports { pub use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; pub use futures::future::try_join3; + pub use citadel_io::tokio_stream::wrappers::UnboundedReceiverStream; + pub use citadel_io::tokio_util::io::{SinkWriter, StreamReader}; pub use citadel_pqcrypto::build_tag; - pub use citadel_wire::exports::openssl; pub use citadel_wire::exports::rustls_pemfile; pub use citadel_wire::exports::ClientConfig as RustlsClientConfig; pub use citadel_wire::hypernode_type::NodeType; @@ -341,8 +344,6 @@ pub mod re_imports { pub use citadel_wire::tls::{ cert_vec_to_secure_client_config, create_rustls_client_config, load_native_certs_async, }; - pub use tokio_stream::wrappers::UnboundedReceiverStream; - pub use tokio_util::io::{SinkWriter, StreamReader}; } pub mod prelude { diff --git a/citadel_proto/src/proto/codec.rs b/citadel_proto/src/proto/codec.rs index 69d3956ff..8a52449c4 100644 --- a/citadel_proto/src/proto/codec.rs +++ b/citadel_proto/src/proto/codec.rs @@ -2,7 +2,7 @@ use std::io; use bytes::BufMut; use bytes::{Bytes, BytesMut}; -use tokio_util::codec::{Decoder, Encoder}; +use citadel_io::tokio_util::codec::{Decoder, Encoder}; use crate::constants::CODEC_MIN_BUFFER; @@ -49,7 +49,7 @@ mod tests { use crate::constants::CODEC_BUFFER_CAPACITY; use crate::proto::codec::BytesCodec; use bytes::{BufMut, Bytes, BytesMut}; - use tokio_util::codec::{Decoder, Encoder}; + use citadel_io::tokio_util::codec::{Decoder, Encoder}; #[test] fn test_bytes_codec() { diff --git a/citadel_proto/src/proto/misc/clean_shutdown.rs b/citadel_proto/src/proto/misc/clean_shutdown.rs index 8b2adeaab..707ef7447 100644 --- a/citadel_proto/src/proto/misc/clean_shutdown.rs +++ b/citadel_proto/src/proto/misc/clean_shutdown.rs @@ -1,12 +1,12 @@ use crate::macros::ContextRequirements; +use citadel_io::tokio::io::{AsyncRead, AsyncWrite}; +use citadel_io::tokio_util::codec::{Decoder, Encoder, Framed}; use futures::stream::{SplitSink, SplitStream}; use futures::{Sink, SinkExt, StreamExt}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio_util::codec::{Decoder, Encoder, Framed}; pub fn clean_framed_shutdown< S: AsyncWrite + AsyncRead + Unpin + ContextRequirements, diff --git a/citadel_proto/src/proto/misc/mod.rs b/citadel_proto/src/proto/misc/mod.rs index b56b62807..78567c25c 100644 --- a/citadel_proto/src/proto/misc/mod.rs +++ b/citadel_proto/src/proto/misc/mod.rs @@ -1,11 +1,11 @@ use crate::error::NetworkError; use bytes::Bytes; +use citadel_io::tokio::io::{AsyncRead, AsyncWrite}; +use citadel_io::tokio_stream::StreamExt; +use citadel_io::tokio_util::codec::LengthDelimitedCodec; use futures::SinkExt; use serde::de::DeserializeOwned; use serde::Serialize; -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio_stream::StreamExt; -use tokio_util::codec::LengthDelimitedCodec; pub mod clean_shutdown; pub mod dual_cell; diff --git a/citadel_proto/src/proto/misc/net.rs b/citadel_proto/src/proto/misc/net.rs index 7be64c7b3..2b10bed50 100644 --- a/citadel_proto/src/proto/misc/net.rs +++ b/citadel_proto/src/proto/misc/net.rs @@ -6,6 +6,10 @@ use crate::proto::misc::clean_shutdown::{ use crate::proto::node::TlsDomain; use crate::proto::peer::p2p_conn_handler::generic_error; use bytes::Bytes; +use citadel_io::tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use citadel_io::tokio::net::{TcpListener, TcpStream}; +use citadel_io::tokio_stream::{Stream, StreamExt}; +use citadel_io::tokio_util::codec::LengthDelimitedCodec; use citadel_user::re_exports::__private::Formatter; use citadel_user::serialization::SyncIO; use citadel_wire::exports::tokio_rustls::{server::TlsStream, TlsAcceptor}; @@ -21,10 +25,6 @@ use std::ops::DerefMut; use std::path::Path; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tokio::net::{TcpListener, TcpStream}; -use tokio_stream::{Stream, StreamExt}; -use tokio_util::codec::LengthDelimitedCodec; /// Wraps a stream into a split interface for I/O that safely shuts-down the interface /// upon drop @@ -130,7 +130,7 @@ impl AsyncWrite for GenericNetworkStream { match self.deref_mut() { Self::Tcp(stream) => Pin::new(stream).poll_write(cx, buf), Self::Tls(stream) => Pin::new(stream).poll_write(cx, buf), - Self::Quic(sink, ..) => Pin::new(sink).poll_write(cx, buf), + Self::Quic(sink, ..) => Pin::new(sink).poll_write(cx, buf).map_err(|err| err.into()), } } @@ -153,7 +153,9 @@ impl AsyncWrite for GenericNetworkStream { pub struct GenericNetworkListener { future: Pin>, - recv: tokio::sync::mpsc::Receiver>, + recv: citadel_io::tokio::sync::mpsc::Receiver< + std::io::Result<(GenericNetworkStream, SocketAddr)>, + >, local_addr: SocketAddr, #[allow(dead_code)] quic_endpoint: Option, @@ -168,7 +170,7 @@ impl GenericNetworkListener { let local_addr = quic_node.endpoint.local_addr()?; let tls_domain = quic_node.tls_domain_opt.clone(); let mut listener = QuicListener::new(quic_node, is_self_signed); - let (send, recv) = tokio::sync::mpsc::channel(1024); + let (send, recv) = citadel_io::tokio::sync::mpsc::channel(1024); let future = async move { while let Some(stream) = listener.next().await { @@ -202,7 +204,7 @@ impl GenericNetworkListener { listener: TcpListener, redirect_to_quic: Option<(TlsDomain, bool)>, ) -> std::io::Result { - let (send, recv) = tokio::sync::mpsc::channel(1024); + let (send, recv) = citadel_io::tokio::sync::mpsc::channel(1024); let local_addr = listener.local_addr()?; let tls_domain = redirect_to_quic.as_ref().and_then(|r| r.0.clone()); @@ -259,7 +261,7 @@ impl GenericNetworkListener { } pub fn new_tls(mut listener: TlsListener) -> std::io::Result { - let (send, recv) = tokio::sync::mpsc::channel(1024); + let (send, recv) = citadel_io::tokio::sync::mpsc::channel(1024); let local_addr = listener.local_addr; let tls_domain = listener.tls_domain.clone(); @@ -324,7 +326,9 @@ impl Stream for GenericNetworkListener { pub struct TlsListener { future: Pin>, - recv: tokio::sync::mpsc::Receiver, SocketAddr)>>, + recv: citadel_io::tokio::sync::mpsc::Receiver< + std::io::Result<(TlsStream, SocketAddr)>, + >, local_addr: SocketAddr, tls_domain: TlsDomain, } @@ -337,7 +341,7 @@ impl TlsListener { is_self_signed: bool, ) -> std::io::Result { // TODO: add channel capacity for acceptors - let (send, recv) = tokio::sync::mpsc::channel(1024); + let (send, recv) = citadel_io::tokio::sync::mpsc::channel(1024); let local_addr = inner.local_addr()?; let tls_domain = domain.clone(); @@ -410,7 +414,7 @@ impl Stream for TlsListener { pub struct QuicListener { future: Pin>, - recv: tokio::sync::mpsc::Receiver>, + recv: citadel_io::tokio::sync::mpsc::Receiver>, #[allow(dead_code)] is_self_signed: bool, } @@ -419,7 +423,7 @@ type IncomingQuicConnection = (Connection, SendStream, RecvStream, SocketAddr, E impl QuicListener { pub fn new(mut server: QuicNode, is_self_signed: bool) -> Self { - let (send, recv) = tokio::sync::mpsc::channel(1024); + let (send, recv) = citadel_io::tokio::sync::mpsc::channel(1024); let endpoint = server.endpoint.clone(); let future = async move { @@ -509,7 +513,9 @@ pub enum FirstPacket { pub struct DualListener { future: Pin>, - recv: tokio::sync::mpsc::Receiver>, + recv: citadel_io::tokio::sync::mpsc::Receiver< + std::io::Result<(GenericNetworkStream, SocketAddr)>, + >, } impl DualListener { @@ -517,7 +523,7 @@ impl DualListener { mut tcp_or_tls_listener: GenericNetworkListener, quic_listener: Option, ) -> Self { - let (tx, recv) = tokio::sync::mpsc::channel(1024); + let (tx, recv) = citadel_io::tokio::sync::mpsc::channel(1024); let tx2 = tx.clone(); let redirects_to_quic = quic_listener.is_some(); @@ -555,7 +561,8 @@ impl DualListener { } }; - tokio::try_join!(tcp_or_tls_listener_future, quic_listener_future).map(|_| ()) + citadel_io::tokio::try_join!(tcp_or_tls_listener_future, quic_listener_future) + .map(|_| ()) }; Self { diff --git a/citadel_proto/src/proto/misc/ordered_channel.rs b/citadel_proto/src/proto/misc/ordered_channel.rs index 33eec4d49..319c2daaf 100644 --- a/citadel_proto/src/proto/misc/ordered_channel.rs +++ b/citadel_proto/src/proto/misc/ordered_channel.rs @@ -87,6 +87,8 @@ impl OrderedChannel { mod tests { use crate::proto::misc::ordered_channel::OrderedChannel; use crate::proto::outbound_sender::unbounded; + use citadel_io::tokio; + use citadel_io::tokio::sync::RwLock; use citadel_types::crypto::SecBuffer; use futures::StreamExt; use rand::prelude::SliceRandom; @@ -95,7 +97,6 @@ mod tests { use std::error::Error; use std::sync::Arc; use std::time::Duration; - use tokio::sync::RwLock; #[tokio::test] async fn smoke_ordered() -> Result<(), Box> { @@ -119,7 +120,7 @@ mod tests { } }; - let recv_handle = citadel_io::spawn(recv_task); + let recv_handle = citadel_io::tokio::task::spawn(recv_task); for (id, packet) in values_ordered { ordered_channel.on_packet_received(id, packet)?; @@ -162,7 +163,7 @@ mod tests { } }; - let recv_handle = citadel_io::spawn(recv_task); + let recv_handle = citadel_io::tokio::task::spawn(recv_task); for (id, packet) in values_unordered { ordered_channel.on_packet_received(id, packet)?; @@ -173,7 +174,7 @@ mod tests { Ok(()) } - #[tokio::test] + #[citadel_io::tokio::test] async fn smoke_unordered_concurrent() -> Result<(), Box> { const COUNT: usize = 10000; let (tx, mut rx) = unbounded(); @@ -206,12 +207,12 @@ mod tests { } }; - let recv_handle = citadel_io::spawn(recv_task); + let recv_handle = citadel_io::tokio::task::spawn(recv_task); - tokio_stream::iter(values_unordered) + citadel_io::tokio_stream::iter(values_unordered) .for_each_concurrent(None, |(id, packet)| async move { let rnd = ThreadRng::default().gen_range(1..10); - tokio::time::sleep(Duration::from_millis(rnd)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(rnd)).await; ordered_channel .write() .await diff --git a/citadel_proto/src/proto/misc/panic_future.rs b/citadel_proto/src/proto/misc/panic_future.rs index 0048ee9e4..a7e975819 100644 --- a/citadel_proto/src/proto/misc/panic_future.rs +++ b/citadel_proto/src/proto/misc/panic_future.rs @@ -1,7 +1,7 @@ +use citadel_io::tokio::macros::support::{Pin, Poll}; +use citadel_io::tokio::task::{JoinError, JoinHandle}; use futures::task::Context; use std::future::Future; -use tokio::macros::support::{Pin, Poll}; -use tokio::task::{JoinError, JoinHandle}; /// Ensures that if a panic occurs in a task, the panic backtrace prints and halts the program pub struct ExplicitPanicFuture { diff --git a/citadel_proto/src/proto/misc/udp_internal_interface.rs b/citadel_proto/src/proto/misc/udp_internal_interface.rs index ff5705315..f1a4ed022 100644 --- a/citadel_proto/src/proto/misc/udp_internal_interface.rs +++ b/citadel_proto/src/proto/misc/udp_internal_interface.rs @@ -5,6 +5,8 @@ use crate::macros::ContextRequirements; use crate::proto::codec::BytesCodec; use crate::proto::peer::p2p_conn_handler::generic_error; use bytes::{Bytes, BytesMut}; +use citadel_io::tokio::net::UdpSocket; +use citadel_io::tokio_util::udp::UdpFramed; use citadel_wire::exports::Connection; use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; use futures::stream::{SplitSink, SplitStream}; @@ -12,8 +14,6 @@ use futures::{Sink, Stream, StreamExt}; use std::net::SocketAddr; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio::net::UdpSocket; -use tokio_util::udp::UdpFramed; pub(crate) trait UdpSink: Sink + Unpin + ContextRequirements diff --git a/citadel_proto/src/proto/misc/underlying_proto.rs b/citadel_proto/src/proto/misc/underlying_proto.rs index ce1f3f80a..c2bc6a822 100644 --- a/citadel_proto/src/proto/misc/underlying_proto.rs +++ b/citadel_proto/src/proto/misc/underlying_proto.rs @@ -9,12 +9,32 @@ use std::net::{SocketAddr, TcpListener, ToSocketAddrs}; use std::path::Path; use std::sync::Arc; -#[derive(Clone)] #[allow(variant_size_differences)] pub enum ServerUnderlyingProtocol { - Tcp(Option>>>), + Tcp(Option>>>), Tls(TLSQUICInterop, TlsDomain, bool), - Quic(Option<(Vec, PrivateKey)>, TlsDomain, bool), + Quic( + Option<(Vec>, PrivateKey<'static>)>, + TlsDomain, + bool, + ), +} + +impl Clone for ServerUnderlyingProtocol { + fn clone(&self) -> Self { + match self { + Self::Tcp(listener) => Self::Tcp(listener.clone()), + Self::Tls(interop, domain, self_signed) => { + Self::Tls(interop.clone(), domain.clone(), *self_signed) + } + Self::Quic(keys, domain, self_signed) => Self::Quic( + keys.as_ref() + .map(|(cert, key)| (cert.clone(), key.clone_key())), + domain.clone(), + *self_signed, + ), + } + } } impl ServerUnderlyingProtocol { @@ -34,9 +54,9 @@ impl ServerUnderlyingProtocol { Self::from_tokio_tcp_listener(tokio::net::TcpListener::from_std(listener)?) } - /// Creates a new [`ServerUnderlyingProtocol`] with a preset [`tokio::net::TcpListener`] + /// Creates a new [`ServerUnderlyingProtocol`] with a preset [`citadel_io::tokio::net::TcpListener`] pub fn from_tokio_tcp_listener( - listener: tokio::net::TcpListener, + listener: citadel_io::tokio::net::TcpListener, ) -> Result { Ok(Self::Tcp(Some(Arc::new(Mutex::new(Some(listener)))))) } diff --git a/citadel_proto/src/proto/node.rs b/citadel_proto/src/proto/node.rs index 57f4e2863..a13347bc4 100644 --- a/citadel_proto/src/proto/node.rs +++ b/citadel_proto/src/proto/node.rs @@ -4,20 +4,24 @@ use std::net::ToSocketAddrs; use std::pin::Pin; use std::sync::Arc; -use crate::kernel::kernel_executor::LocalSet; use futures::StreamExt; -use tokio::io::AsyncRead; +use citadel_io::tokio::io::AsyncRead; use citadel_types::crypto::SecurityLevel; use citadel_user::account_manager::AccountManager; +use citadel_wire::exports::tokio_rustls::rustls::{pki_types, ClientConfig}; +use citadel_wire::exports::Endpoint; use citadel_wire::hypernode_type::NodeType; use citadel_wire::nat_identification::NatType; +use citadel_wire::quic::{QuicEndpointConnector, QuicNode, QuicServer, SELF_SIGNED_DOMAIN}; +use citadel_wire::tls::client_config_to_tls_connector; use netbeam::time_tracker::TimeTracker; use crate::constants::{MAX_OUTGOING_UNPROCESSED_REQUESTS, TCP_CONN_TIMEOUT}; use crate::error::NetworkError; use crate::functional::PairMap; use crate::kernel::kernel_communicator::KernelAsyncCallbackHandler; +use crate::kernel::kernel_executor::LocalSet; use crate::kernel::RuntimeFuture; use crate::prelude::{DeleteObject, PreSharedKey, PullObject}; use crate::proto::misc::net::{ @@ -35,10 +39,6 @@ use crate::proto::peer::p2p_conn_handler::generic_error; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::{CitadelSession, HdpSessionInitMode}; use crate::proto::session_manager::HdpSessionManager; -use citadel_wire::exports::tokio_rustls::rustls::{ClientConfig, ServerName}; -use citadel_wire::exports::Endpoint; -use citadel_wire::quic::{QuicEndpointConnector, QuicNode, QuicServer, SELF_SIGNED_DOMAIN}; -use citadel_wire::tls::client_config_to_tls_connector; pub type TlsDomain = Option; @@ -70,7 +70,7 @@ impl Node { local_node_type: NodeType, to_kernel: UnboundedSender, account_manager: AccountManager, - shutdown: tokio::sync::oneshot::Sender<()>, + shutdown: citadel_io::tokio::sync::oneshot::Sender<()>, underlying_proto: ServerUnderlyingProtocol, client_config: Option>, stun_servers: Option>, @@ -144,7 +144,7 @@ impl Node { fn load( this: Node, account_manager: AccountManager, - shutdown: tokio::sync::oneshot::Sender<()>, + shutdown: citadel_io::tokio::sync::oneshot::Sender<()>, ) -> ( NodeRemote, Pin>, @@ -248,7 +248,7 @@ impl Node { let server_future = async move { let res = if let Some(primary_stream_listener) = primary_stream_listener { - tokio::select! { + citadel_io::tokio::select! { res0 = outbound_kernel_request_handler => { log::trace!(target: "citadel", "OUTBOUND KERNEL REQUEST HANDLER ENDED: {:?}", &res0); res0 @@ -259,7 +259,7 @@ impl Node { res3 = session_spawner => res3 } } else { - tokio::select! { + citadel_io::tokio::select! { res0 = outbound_kernel_request_handler => { log::trace!(target: "citadel", "OUTBOUND KERNEL REQUEST HANDLER ENDED: {:?}", &res0); res0 @@ -277,7 +277,7 @@ impl Node { // the kernel will wait until the server shuts down to prevent cleanup tasks from being killed too early shutdown.send(()); - tokio::time::timeout(Duration::from_millis(1000), sess_mgr.shutdown()) + citadel_io::tokio::time::timeout(Duration::from_millis(1000), sess_mgr.shutdown()) .await .map_err(|err| NetworkError::Generic(err.to_string()))?; @@ -287,7 +287,6 @@ impl Node { }; //handle.load_server_future(server_future); - ( remote, Box::pin(server_future), @@ -450,7 +449,7 @@ impl Node { log::trace!(target: "citadel", "Connecting to QUIC node {:?}", remote); // when using p2p quic, if domain is some, then we will use the default cfg let cfg = if domain.is_some() { - citadel_wire::quic::rustls_client_config_to_quinn_config(secure_client_config) + citadel_wire::quic::rustls_client_config_to_quinn_config(secure_client_config)? } else { // if there is no domain specified, assume self-signed (For now) // this is non-blocking since native certs won't be loaded @@ -460,7 +459,7 @@ impl Node { log::trace!(target: "citadel", "Using cfg={:?} to connect to {:?}", cfg, remote); // we MUST use the connect_biconn_WITH below since we are using the server quic instance to make this outgoing connection - let (conn, sink, stream) = tokio::time::timeout( + let (conn, sink, stream) = citadel_io::tokio::time::timeout( timeout.unwrap_or(TCP_CONN_TIMEOUT), quic_endpoint.connect_biconn_with( remote, @@ -529,14 +528,16 @@ impl Node { let stream = connector .connect( - ServerName::try_from(domain.as_deref().unwrap_or(SELF_SIGNED_DOMAIN)) - .map_err(|err| generic_error(err.to_string()))?, + pki_types::ServerName::try_from( + domain + .clone() + .unwrap_or_else(|| SELF_SIGNED_DOMAIN.to_string()), + ) + .map_err(|err| generic_error(err.to_string()))?, stream, ) .await - .map_err(|err| { - std::io::Error::new(std::io::ErrorKind::ConnectionRefused, err) - })?; + .map_err(|err| io::Error::new(io::ErrorKind::ConnectionRefused, err))?; Ok((GenericNetworkStream::Tls(stream.into()), None)) } FirstPacket::Quic { @@ -551,7 +552,7 @@ impl Node { citadel_wire::quic::QuicClient::new_no_verify(udp_socket) .map_err(generic_error)? } else { - citadel_wire::quic::QuicClient::new_with_config( + citadel_wire::quic::QuicClient::new_with_rustls_config( udp_socket, default_client_config.clone(), ) @@ -577,7 +578,7 @@ impl Node { stream: R, timeout: Option, ) -> std::io::Result { - let (_stream, ret) = tokio::time::timeout( + let (_stream, ret) = citadel_io::tokio::time::timeout( timeout.unwrap_or(TCP_CONN_TIMEOUT), super::misc::read_one_packet_as_framed(stream), ) diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs index cde2419d5..b93be8a06 100644 --- a/citadel_proto/src/proto/node_request.rs +++ b/citadel_proto/src/proto/node_request.rs @@ -146,7 +146,8 @@ impl PreSharedKey { /// must have matching passwords in order to establish a connection. /// Note: The password is hashed using SHA-256 before being added to the list to increase security. pub fn add_password>(mut self, password: T) -> Self { - self.passwords.push(sha256(password.as_ref()).to_vec()); + self.passwords + .push(sha256::digest(password.as_ref()).into_bytes()); self } } diff --git a/citadel_proto/src/proto/node_result.rs b/citadel_proto/src/proto/node_result.rs index ca2a3328d..a04f48a8f 100644 --- a/citadel_proto/src/proto/node_result.rs +++ b/citadel_proto/src/proto/node_result.rs @@ -40,7 +40,7 @@ pub struct ConnectSuccess { pub services: citadel_user::external_services::ServicesObject, pub welcome_message: String, pub channel: PeerChannel, - pub udp_rx_opt: Option>, + pub udp_rx_opt: Option>, pub session_security_settings: SessionSecuritySettings, } @@ -126,7 +126,7 @@ pub struct InternalServerError { pub struct PeerChannelCreated { pub ticket: Ticket, pub channel: PeerChannel, - pub udp_rx_opt: Option>, + pub udp_rx_opt: Option>, } #[derive(Debug)] diff --git a/citadel_proto/src/proto/outbound_sender.rs b/citadel_proto/src/proto/outbound_sender.rs index 6ab579abe..0bf42be14 100644 --- a/citadel_proto/src/proto/outbound_sender.rs +++ b/citadel_proto/src/proto/outbound_sender.rs @@ -1,14 +1,14 @@ use crate::error::NetworkError; use crate::proto::packet::packet_flags; use bytes::BytesMut; +pub use citadel_io::tokio::sync::mpsc::{ + error::SendError, Receiver, Sender, UnboundedReceiver, UnboundedSender as UnboundedSenderInner, +}; use citadel_user::re_exports::__private::Formatter; use futures::task::{Context, Poll}; use futures::Sink; use std::net::SocketAddr; use std::pin::Pin; -pub use tokio::sync::mpsc::{ - error::SendError, Receiver, Sender, UnboundedReceiver, UnboundedSender as UnboundedSenderInner, -}; pub struct UnboundedSender(pub(crate) UnboundedSenderInner); @@ -19,7 +19,7 @@ impl Clone for UnboundedSender { } pub fn unbounded() -> (UnboundedSender, UnboundedReceiver) { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); (UnboundedSender(tx), rx) } @@ -31,7 +31,7 @@ impl UnboundedSender { } pub fn channel(len: usize) -> (Sender, Receiver) { - tokio::sync::mpsc::channel(len) + citadel_io::tokio::sync::mpsc::channel(len) } #[derive(Clone)] @@ -51,12 +51,12 @@ impl From> for OutboundPrimaryStreamSender { } pub struct OutboundPrimaryStreamReceiver( - pub tokio_stream::wrappers::UnboundedReceiverStream, + pub citadel_io::tokio_stream::wrappers::UnboundedReceiverStream, ); impl From> for OutboundPrimaryStreamReceiver { fn from(inner: UnboundedReceiver) -> Self { - Self(tokio_stream::wrappers::UnboundedReceiverStream::new(inner)) + Self(citadel_io::tokio_stream::wrappers::UnboundedReceiverStream::new(inner)) } } @@ -145,24 +145,30 @@ impl std::fmt::Debug for OutboundUdpSender { } /// As asynchronous channel meant to rate-limit input -pub struct BoundedSender(tokio::sync::mpsc::Sender); +pub struct BoundedSender(citadel_io::tokio::sync::mpsc::Sender); -pub type BoundedReceiver = tokio::sync::mpsc::Receiver; +pub type BoundedReceiver = citadel_io::tokio::sync::mpsc::Receiver; impl BoundedSender { /// Creates a new bounded channel pub fn new(limit: usize) -> (BoundedSender, BoundedReceiver) { - let (tx, rx) = tokio::sync::mpsc::channel(limit); + let (tx, rx) = citadel_io::tokio::sync::mpsc::channel(limit); (Self(tx), rx) } /// Attempts to send a value through the stream non-blocking and synchronously - pub fn try_send(&self, t: T) -> Result<(), tokio::sync::mpsc::error::TrySendError> { + pub fn try_send( + &self, + t: T, + ) -> Result<(), citadel_io::tokio::sync::mpsc::error::TrySendError> { self.0.try_send(t) } /// Sends a value through the channel - pub async fn send(&self, t: T) -> Result<(), tokio::sync::mpsc::error::SendError> { + pub async fn send( + &self, + t: T, + ) -> Result<(), citadel_io::tokio::sync::mpsc::error::SendError> { self.0.send(t).await } } diff --git a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs index 165073c33..1cede5d4c 100644 --- a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs @@ -50,7 +50,7 @@ pub async fn process_disconnect( ) .unbounded_send(packet)?; // give some time for the outbound task to send the DC message to the adjacent node - tokio::time::sleep(Duration::from_millis(100)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(100)).await; Ok(PrimaryProcessorResult::EndSession(SUCCESS_DISCONNECT)) } diff --git a/citadel_proto/src/proto/packet_processor/file_packet.rs b/citadel_proto/src/proto/packet_processor/file_packet.rs index 39f32cdfe..c1ce943ba 100644 --- a/citadel_proto/src/proto/packet_processor/file_packet.rs +++ b/citadel_proto/src/proto/packet_processor/file_packet.rs @@ -223,7 +223,9 @@ pub fn process_file_packet( Some(metadata), move |source| { if delete_on_pull { - spawn!(tokio::fs::remove_file(source)); + spawn!(citadel_io::tokio::fs::remove_file( + source + )); } }, ) { diff --git a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs index 30a467e91..a1a06208c 100644 --- a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs +++ b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs @@ -57,7 +57,10 @@ pub async fn process_keep_alive( // ever since creating the anti-replay attack, we can no longer withhold packets; they must be sent outbound // immediately, otherwise other packets will fail, invalidating the session async move { - tokio::time::sleep(Duration::from_millis(KEEP_ALIVE_INTERVAL_MS)).await; + citadel_io::tokio::time::sleep(Duration::from_millis( + KEEP_ALIVE_INTERVAL_MS, + )) + .await; accessor.borrow_hr(None, |hr, _| { let next_ka = packet_crafter::keep_alive::craft_keep_alive_packet( hr, diff --git a/citadel_proto/src/proto/packet_processor/mod.rs b/citadel_proto/src/proto/packet_processor/mod.rs index 845364e2d..7c8d630af 100644 --- a/citadel_proto/src/proto/packet_processor/mod.rs +++ b/citadel_proto/src/proto/packet_processor/mod.rs @@ -7,8 +7,8 @@ use citadel_user::re_exports::__private::Formatter; pub mod includes { pub use std::net::SocketAddr; + pub use citadel_io::tokio::time::{Duration, Instant}; pub use log::{trace, warn}; - pub use tokio::time::{Duration, Instant}; pub use zerocopy::Ref; pub use citadel_types::crypto::SecBuffer; diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index a9fadb32b..8acb81143 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -29,7 +29,7 @@ use crate::proto::peer::hole_punch_compat_sink_stream::ReliableOrderedCompatStre use crate::proto::peer::p2p_conn_handler::attempt_simultaneous_hole_punch; use crate::proto::peer::peer_crypt::{KeyExchangeProcess, PeerNatInfo}; use crate::proto::peer::peer_layer::{ - HyperNodePeerLayerInner, NodeConnectionType, PeerConnectionType, PeerResponse, PeerSignal, + NodeConnectionType, PeerConnectionType, PeerResponse, PeerSignal, }; use crate::proto::remote::Ticket; use crate::proto::session_manager::HdpSessionManager; @@ -139,7 +139,8 @@ pub async fn process_peer_cmd( break; } - tokio::time::sleep(Duration::from_millis(1500)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(1500)) + .await; } log::trace!(target: "citadel", "[Peer Vconn] No packets received in the last 1500ms; will drop the connection cleanly"); @@ -1057,6 +1058,7 @@ async fn process_signal_command_as_server( let to_primary_stream = return_if_none!(session.to_primary_stream.clone()); let sess_mgr = session.session_manager.clone(); + drop(peer_layer); route_signal_and_register_ticket_forwards( PeerSignal::PostRegister { peer_conn_type, @@ -1255,6 +1257,7 @@ async fn process_signal_command_as_server( .await?; Ok(PrimaryProcessorResult::Void) } else { + drop(peer_layer); route_signal_and_register_ticket_forwards( PeerSignal::PostConnect { peer_conn_type, @@ -1319,7 +1322,7 @@ async fn process_signal_command_as_server( break; } - tokio::time::sleep(Duration::from_millis(1500)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(1500)).await; } log::trace!(target: "citadel", "[Peer Vconn @ Server] No packets received in the last 1500ms; will drop the virtual connection cleanly"); @@ -1671,7 +1674,7 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( let to_primary_stream = to_primary_stream.clone(); // Give the target_cid 10 seconds to respond - let res = sess_mgr.route_signal_primary(peer_layer, implicated_cid, target_cid, ticket, signal.clone(), move |peer_hyper_ratchet| { + let res = sess_mgr.route_signal_primary(implicated_cid, target_cid, ticket, signal.clone(), move |peer_hyper_ratchet| { packet_crafter::peer_cmd::craft_peer_signal(peer_hyper_ratchet, signal.clone(), ticket, timestamp, security_level) }, timeout, move |stale_signal| { // on timeout, run this diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs index 7c663b823..8d5f4fcaa 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs @@ -41,7 +41,7 @@ pub async fn handle_response_phase_post_register( } }; - let handle = citadel_io::spawn(task); + let handle = citadel_io::tokio::task::spawn(task); // dont process the handle std::mem::drop(handle); } diff --git a/citadel_proto/src/proto/peer/channel.rs b/citadel_proto/src/proto/peer/channel.rs index c1ac4e56c..cdba78161 100644 --- a/citadel_proto/src/proto/peer/channel.rs +++ b/citadel_proto/src/proto/peer/channel.rs @@ -7,6 +7,7 @@ use crate::proto::peer::peer_layer::{PeerConnectionType, PeerSignal}; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::SessionRequest; use crate::proto::state_container::VirtualConnectionType; +use citadel_io::tokio::macros::support::Pin; use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecurityLevel; use citadel_user::re_exports::__private::Formatter; @@ -15,7 +16,6 @@ use futures::Stream; use std::fmt::Debug; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use tokio::macros::support::Pin; // 1 peer channel per virtual connection. This enables high-level communication between the [HdpServer] and the API-layer. #[derive(Debug)] @@ -280,7 +280,7 @@ impl UdpChannel { #[cfg_attr(docsrs, doc(cfg(feature = "webrtc")))] pub struct WebRTCCompatChannel { send_half: OutboundUdpSender, - recv_half: tokio::sync::Mutex, + recv_half: citadel_io::tokio::sync::Mutex, } #[cfg(feature = "webrtc")] @@ -295,7 +295,7 @@ mod rtc_impl { fn from(this: UdpChannel) -> Self { Self { send_half: this.send_half, - recv_half: tokio::sync::Mutex::new(this.recv_half), + recv_half: citadel_io::tokio::sync::Mutex::new(this.recv_half), } } } diff --git a/citadel_proto/src/proto/peer/group_channel.rs b/citadel_proto/src/proto/peer/group_channel.rs index 144733f73..c9fc77bda 100644 --- a/citadel_proto/src/proto/peer/group_channel.rs +++ b/citadel_proto/src/proto/peer/group_channel.rs @@ -3,6 +3,7 @@ use crate::proto::outbound_sender::{Sender, UnboundedReceiver}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::SessionRequest; +use citadel_io::tokio_stream::StreamExt; use citadel_types::crypto::SecBuffer; use citadel_types::proto::MessageGroupKey; use citadel_user::re_exports::__private::Formatter; @@ -11,7 +12,6 @@ use std::fmt::Debug; use std::ops::Deref; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio_stream::StreamExt; #[derive(Debug)] pub struct GroupChannel { diff --git a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs index 6a6152fc8..abc6a1fdb 100644 --- a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs +++ b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs @@ -4,11 +4,11 @@ use crate::proto::state_container::StateContainerInner; use async_trait::async_trait; use bytes::Bytes; use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_io::tokio::sync::Mutex; use citadel_types::crypto::SecurityLevel; use netbeam::reliable_conn::{ConnAddr, ReliableOrderedStreamToTarget}; use std::net::SocketAddr; use std::str::FromStr; -use tokio::sync::Mutex; pub(crate) struct ReliableOrderedCompatStream { to_primary_stream: OutboundPrimaryStreamSender, @@ -31,7 +31,7 @@ impl ReliableOrderedCompatStream { hr: StackedRatchet, security_level: SecurityLevel, ) -> Self { - let (from_stream_tx, from_stream_rx) = tokio::sync::mpsc::unbounded_channel(); + let (from_stream_tx, from_stream_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); // insert from_stream_tx into state container so that the protocol can deliver packets to the hole puncher // NOTE: The protocol must strip the header when passing packets to the from_stream function! diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index a878f524d..52f7231c3 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -1,5 +1,5 @@ -use tokio::sync::oneshot::{channel, Receiver, Sender}; -use tokio_stream::StreamExt; +use citadel_io::tokio::sync::oneshot::{channel, Receiver, Sender}; +use citadel_io::tokio_stream::StreamExt; use crate::error::NetworkError; use crate::functional::IfTrueConditional; @@ -217,7 +217,7 @@ fn handle_p2p_stream( std::mem::drop(state_container); let future = async move { - let res = tokio::select! { + let res = citadel_io::tokio::select! { res0 = writer_future => res0, res1 = reader_future => res1, res2 = stopper_future => res2 @@ -303,7 +303,7 @@ pub(crate) async fn attempt_simultaneous_hole_punch( let v_conn = peer_connection_type.as_virtual_connection(); let process = async move { - tokio::time::sleep_until(sync_time).await; + citadel_io::tokio::time::sleep_until(sync_time).await; let hole_punched_socket = app .begin_udp_hole_punch(encrypted_config_container) @@ -320,10 +320,10 @@ pub(crate) async fn attempt_simultaneous_hole_punch( if is_initiator { // give time for non-initiator to setup local bind // TODO: Replace with biconn channel logic - tokio::time::sleep(Duration::from_millis(200)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(200)).await; let socket = hole_punched_socket.into_socket(); let quic_endpoint = - citadel_wire::quic::QuicClient::new_with_config(socket, client_config.clone()) + citadel_wire::quic::QuicClient::new_with_rustls_config(socket, client_config.clone()) .map_err(generic_error)?; let p2p_stream = Node::quic_p2p_connect_defaults( quic_endpoint.endpoint, diff --git a/citadel_proto/src/proto/peer/peer_layer.rs b/citadel_proto/src/proto/peer/peer_layer.rs index 308ee2dc6..a39f23bc8 100644 --- a/citadel_proto/src/proto/peer/peer_layer.rs +++ b/citadel_proto/src/proto/peer/peer_layer.rs @@ -6,6 +6,9 @@ use crate::proto::peer::message_group::{MessageGroup, MessageGroupPeer}; use crate::proto::peer::peer_crypt::KeyExchangeProcess; use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualConnectionType; +use citadel_io::tokio::time::error::Error; +use citadel_io::tokio::time::Duration; +use citadel_io::tokio_util::time::{delay_queue, delay_queue::DelayQueue}; use citadel_types::prelude::PeerInfo; use citadel_types::proto::{ GroupType, MessageGroupKey, MessageGroupOptions, SessionSecuritySettings, UdpMode, @@ -22,9 +25,6 @@ use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::pin::Pin; use std::sync::Arc; -use tokio::time::error::Error; -use tokio::time::Duration; -use tokio_util::time::{delay_queue, delay_queue::DelayQueue}; use uuid::Uuid; pub trait PeerLayerTimeoutFunction: FnOnce(PeerSignal) + SyncContextRequirements {} @@ -54,7 +54,7 @@ const MAILBOX: &str = "mailbox"; #[derive(Clone)] pub struct HyperNodePeerLayer { - pub(crate) inner: std::sync::Arc>, + pub(crate) inner: std::sync::Arc>, waker: std::sync::Arc, } @@ -96,7 +96,7 @@ impl HyperNodePeerLayer { persistence_handler, message_groups: HashMap::new(), }; - let inner = Arc::new(tokio::sync::RwLock::new(inner)); + let inner = Arc::new(citadel_io::tokio::sync::RwLock::new(inner)); Self { inner, waker } } diff --git a/citadel_proto/src/proto/remote.rs b/citadel_proto/src/proto/remote.rs index a2123fee8..e6350d1c7 100644 --- a/citadel_proto/src/proto/remote.rs +++ b/citadel_proto/src/proto/remote.rs @@ -6,12 +6,12 @@ use crate::prelude::NodeRequest; use crate::proto::node::HdpServerRemoteInner; use crate::proto::outbound_sender::BoundedSender; use bytemuck::NoUninit; +use citadel_io::tokio::sync::mpsc::error::TrySendError; use citadel_user::account_manager::AccountManager; use citadel_wire::hypernode_type::NodeType; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; -use tokio::sync::mpsc::error::TrySendError; /// allows convenient communication with the server #[derive(Clone)] diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index 2cb316130..c932533ec 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -6,8 +6,8 @@ use std::sync::Arc; //use async_std::prelude::*; use crate::proto::packet_processor::includes::Instant; use bytes::{Bytes, BytesMut}; +use citadel_io::tokio_util::codec::LengthDelimitedCodec; use futures::{SinkExt, StreamExt, TryFutureExt, TryStreamExt}; -use tokio_util::codec::LengthDelimitedCodec; use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; use citadel_crypt::stacked_ratchet::Ratchet; @@ -197,7 +197,7 @@ pub struct CitadelSessionInner { pub(super) do_static_hr_refresh_atexit: DualCell, pub(super) dc_signal_sender: DualRwLock>>, pub(super) is_server: bool, - pub(super) stopper_tx: DualRwLock>, + pub(super) stopper_tx: DualRwLock>, pub(super) queue_handle: DualLateInit, pub(super) peer_only_connect_protocol: DualRwLock>, pub(super) primary_stream_quic_conn: DualRwLock>, @@ -276,8 +276,8 @@ pub(crate) struct ClientOnlySessionInitSettings { impl CitadelSession { pub(crate) fn new( session_init_params: SessionInitParams, - ) -> Result<(tokio::sync::broadcast::Sender<()>, Self), NetworkError> { - let (stopper_tx, _stopper_rx) = tokio::sync::broadcast::channel(10); + ) -> Result<(citadel_io::tokio::sync::broadcast::Sender<()>, Self), NetworkError> { + let (stopper_tx, _stopper_rx) = citadel_io::tokio::sync::broadcast::channel(10); let client_only_settings = &session_init_params.client_only_settings; let is_server = client_only_settings.is_none(); let (cnac, state, implicated_cid) = @@ -473,7 +473,7 @@ impl CitadelSession { ); let session_future = spawn_handle!(async move { - tokio::select! { + citadel_io::tokio::select! { res0 = writer_future => res0, res1 = reader_future => res1, res2 = stopper_future => res2 @@ -521,7 +521,7 @@ impl CitadelSession { } async fn stopper( - mut receiver: tokio::sync::broadcast::Receiver<()>, + mut receiver: citadel_io::tokio::sync::broadcast::Receiver<()>, ) -> Result<(), NetworkError> { receiver .recv() @@ -721,7 +721,7 @@ impl CitadelSession { udp_conn: UdpSplittableTypes, addr: TargettedSocketAddr, ticket: Ticket, - tcp_conn_awaiter: Option>, + tcp_conn_awaiter: Option>, ) { let this_weak = this.as_weak(); std::mem::drop(this); @@ -747,7 +747,7 @@ impl CitadelSession { hole_punched_socket, needs_manual_ka, ); - let (stopper_tx, stopper_rx) = tokio::sync::oneshot::channel::<()>(); + let (stopper_tx, stopper_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); let is_server = sess.is_server; std::mem::drop(sess); @@ -868,7 +868,7 @@ impl CitadelSession { .map_err(|err| NetworkError::Generic(err.to_string())) }; - tokio::select! { + citadel_io::tokio::select! { res0 = listener => res0, res1 = udp_sender_future => res1, res2 = stopper => res2 @@ -1423,8 +1423,9 @@ impl CitadelSession { let time_tracker = this.time_tracker; let timestamp = this.time_tracker.get_global_time_ns(); let (group_sender, group_sender_rx) = channel(5); - let mut group_sender_rx = tokio_stream::wrappers::ReceiverStream::new(group_sender_rx); - let (stop_tx, stop_rx) = tokio::sync::oneshot::channel(); + let mut group_sender_rx = + citadel_io::tokio_stream::wrappers::ReceiverStream::new(group_sender_rx); + let (stop_tx, stop_rx) = citadel_io::tokio::sync::oneshot::channel(); // the above are the same for all vtarget types. Now, we need to get the proper drill and pqc let mut state_container = inner_mut_state!(this.state_container); @@ -1553,7 +1554,6 @@ impl CitadelSession { .as_ref() .map(|r| r.object_id) .unwrap_or_else(|| endpoint_container.endpoint_crypto.get_next_object_id()); - // reserve group ids let start_group_id = endpoint_container .endpoint_crypto @@ -1653,8 +1653,8 @@ impl CitadelSession { let kernel_tx = state_container.kernel_tx.clone(); let (next_gs_alerter, next_gs_alerter_rx) = unbounded(); let mut next_gs_alerter_rx = - tokio_stream::wrappers::UnboundedReceiverStream::new(next_gs_alerter_rx); - let (start, start_rx) = tokio::sync::oneshot::channel(); + citadel_io::tokio_stream::wrappers::UnboundedReceiverStream::new(next_gs_alerter_rx); + let (start, start_rx) = citadel_io::tokio::sync::oneshot::channel(); let outbound_file_transfer_container = OutboundFileTransfer { stop_tx: Some(stop_tx), metadata, @@ -1879,7 +1879,7 @@ impl CitadelSession { // TODO: Make a generic version to allow requests the ability to bypass the session manager pub(crate) fn spawn_message_sender_function( this: CitadelSession, - mut rx: tokio::sync::mpsc::Receiver, + mut rx: citadel_io::tokio::sync::mpsc::Receiver, ) { let task = async move { let this = &this; @@ -1945,7 +1945,7 @@ impl CitadelSession { Ok(()) }; - tokio::select! { + citadel_io::tokio::select! { res0 = stopper => res0, res1 = receiver => res1 } @@ -2109,7 +2109,8 @@ impl CitadelSession { mut sink: S, peer_session_accessor: EndpointCryptoAccessor, ) -> Result<(), NetworkError> { - let mut receiver = tokio_stream::wrappers::UnboundedReceiverStream::new(receiver); + let mut receiver = + citadel_io::tokio_stream::wrappers::UnboundedReceiverStream::new(receiver); let target_cid = peer_session_accessor.get_target_cid(); while let Some((cmd_aux, packet)) = receiver.next().await { diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index 368c06d0a..e687abab2 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -31,8 +31,7 @@ use crate::proto::packet_processor::includes::{Duration, Instant}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::peer::peer_layer::{ - HyperNodePeerLayer, HyperNodePeerLayerInner, MailboxTransfer, PeerConnectionType, PeerResponse, - PeerSignal, + HyperNodePeerLayer, MailboxTransfer, PeerConnectionType, PeerResponse, PeerSignal, }; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::{ @@ -40,6 +39,7 @@ use crate::proto::session::{ }; use crate::proto::state_container::{VirtualConnectionType, VirtualTargetType}; use citadel_crypt::streaming_crypt_scrambler::ObjectSource; +use citadel_io::tokio::sync::broadcast::Sender; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ConnectMode; use citadel_types::proto::SessionSecuritySettings; @@ -50,7 +50,6 @@ use citadel_types::proto::{ use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::exports::tokio_rustls::rustls::ClientConfig; use std::sync::Arc; -use tokio::sync::broadcast::Sender; define_outer_struct_wrapper!(HdpSessionManager, HdpSessionManagerInner); @@ -1110,7 +1109,11 @@ impl HdpSessionManager { // get the target cid's session if let Some(ref sess_ref) = sess { - peer_layer + sess_ref + .hypernode_peer_layer + .inner + .write() + .await .insert_tracked_posting(implicated_cid, timeout, ticket, signal, on_timeout) .await; let peer_sender = sess_ref.to_primary_stream.as_ref().unwrap(); diff --git a/citadel_proto/src/proto/session_queue_handler.rs b/citadel_proto/src/proto/session_queue_handler.rs index 8b87f962f..115219e1e 100644 --- a/citadel_proto/src/proto/session_queue_handler.rs +++ b/citadel_proto/src/proto/session_queue_handler.rs @@ -1,19 +1,19 @@ use crate::error::NetworkError; use crate::proto::packet_processor::includes::Duration; use crate::proto::session::SessionState; +use citadel_io::tokio::sync::broadcast::Sender; +use citadel_io::tokio::time::error::Error; +use citadel_io::tokio_util::time::{delay_queue, DelayQueue}; use futures::Stream; use std::collections::HashMap; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll, Waker}; -use tokio::sync::broadcast::Sender; -use tokio::time::error::Error; -use tokio_util::time::{delay_queue, DelayQueue}; use crate::inner_arg::ExpectedInnerTargetMut; use crate::proto::state_container::{StateContainer, StateContainerInner}; +use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use std::sync::atomic::{AtomicUsize, Ordering}; -use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; /// any index below 10 are reserved for the session. Inbound GROUP timeouts will begin at 10 or high pub const QUEUE_WORKER_RESERVED_INDEX: usize = 10; @@ -118,7 +118,7 @@ pub enum QueueWorkerResult { impl SessionQueueWorker { pub fn new(sess_shutdown: Sender<()>) -> (Self, SessionQueueWorkerHandle) { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let handle = SessionQueueWorkerHandle { tx, rolling_idx: Arc::new(Default::default()), diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index 83a4e0024..e978e5da0 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -119,8 +119,9 @@ pub struct StateContainerInner { pub(crate) keep_alive_timeout_ns: i64, pub(crate) state: Arc>, // whenever a c2s or p2p channel is loaded, this is fired to signal any UDP loaders that it is safe to store the UDP conn in the corresponding v_conn - pub(super) tcp_loaded_status: Option>, - pub(super) hole_puncher_pipes: HashMap>, + pub(super) tcp_loaded_status: Option>, + pub(super) hole_puncher_pipes: + HashMap>, pub(super) cnac: Option, pub(super) time_tracker: TimeTracker, pub(super) session_security_settings: Option, @@ -159,7 +160,7 @@ pub(crate) struct InboundFileTransfer { pub virtual_target: VirtualTargetType, pub metadata: VirtualObjectMetadata, pub stream_to_hd: UnboundedSender>, - pub reception_complete_tx: tokio::sync::oneshot::Sender, + pub reception_complete_tx: citadel_io::tokio::sync::oneshot::Sender, pub local_encryption_level: Option, } @@ -170,9 +171,9 @@ pub(crate) struct OutboundFileTransfer { // for alerting the group sender to begin sending the next group pub next_gs_alerter: UnboundedSender<()>, // for alerting the async task to begin creating GroupSenders - pub start: Option>, + pub start: Option>, // This sends a shutdown signal to the async cryptscambler - pub stop_tx: Option>, + pub stop_tx: Option>, } impl GroupKey { @@ -238,7 +239,7 @@ pub struct C2SChannelContainer { pub(crate) struct UnorderedChannelContainer { to_channel: UnboundedSender, - stopper_tx: tokio::sync::oneshot::Sender<()>, + stopper_tx: citadel_io::tokio::sync::oneshot::Sender<()>, } impl EndpointChannelContainer { @@ -700,7 +701,7 @@ impl StateContainerInner { v_conn: VirtualConnectionType, ticket: Ticket, to_udp_stream: OutboundUdpSender, - stopper_tx: tokio::sync::oneshot::Sender<()>, + stopper_tx: citadel_io::tokio::sync::oneshot::Sender<()>, ) -> Option { if target_cid == 0 { if let Some(c2s_container) = self.c2s_channel_container.as_mut() { @@ -820,6 +821,7 @@ impl StateContainerInner { endpoint_crypto: PeerSessionCrypto, sess: &CitadelSession, file_transfer_compatible: bool, + file_transfer_compatible: bool, ) -> PeerChannel { let (channel_tx, channel_rx) = unbounded(); let (tx, rx) = crate::proto::outbound_sender::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); @@ -913,8 +915,8 @@ impl StateContainerInner { peer_channel } - pub fn setup_tcp_alert_if_udp_c2s(&mut self) -> tokio::sync::oneshot::Receiver<()> { - let (tx, rx) = tokio::sync::oneshot::channel(); + pub fn setup_tcp_alert_if_udp_c2s(&mut self) -> citadel_io::tokio::sync::oneshot::Receiver<()> { + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); self.tcp_loaded_status = Some(tx); rx } @@ -1131,7 +1133,8 @@ impl StateContainerInner { let pers = pers.clone(); let metadata = metadata_orig.clone(); let tt = self.time_tracker; - let (reception_complete_tx, success_receiving_rx) = tokio::sync::oneshot::channel(); + let (reception_complete_tx, success_receiving_rx) = + citadel_io::tokio::sync::oneshot::channel(); let entry = InboundFileTransfer { last_group_finish_time: Instant::now(), last_group_window_len: 0, diff --git a/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs index 8f9873a4b..0b6674e7d 100644 --- a/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs @@ -1,4 +1,4 @@ -use tokio::time::Instant; +use citadel_io::tokio::time::Instant; use crate::proto::packet::packet_flags; use citadel_types::proto::ConnectMode; diff --git a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs index 31a198340..11bc100ac 100644 --- a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs @@ -3,8 +3,8 @@ use crate::proto::peer::channel::UdpChannel; use crate::proto::remote::Ticket; use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_io::tokio::sync::oneshot::{channel, Receiver, Sender}; use citadel_wire::hypernode_type::NodeType; -use tokio::sync::oneshot::{channel, Receiver, Sender}; /// For keeping track of the pre-connect state pub struct PreConnectState { diff --git a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs index b64243729..8b89112a9 100644 --- a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs @@ -1,4 +1,4 @@ -use tokio::time::Instant; +use citadel_io::tokio::time::Instant; use crate::proto::packet::packet_flags; use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; diff --git a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs index 93375e48e..19955e9fe 100644 --- a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs @@ -1,4 +1,4 @@ -use tokio::time::Duration; +use citadel_io::tokio::time::Duration; use crate::constants::{ DRILL_UPDATE_FREQUENCY_DIVINE_BASE, DRILL_UPDATE_FREQUENCY_HIGH_BASE, diff --git a/citadel_proto/tests/connections.rs b/citadel_proto/tests/connections.rs index 7e29d65f8..85df2dfb1 100644 --- a/citadel_proto/tests/connections.rs +++ b/citadel_proto/tests/connections.rs @@ -1,6 +1,7 @@ #[cfg(test)] pub mod tests { use bytes::BytesMut; + use citadel_io::tokio; use citadel_proto::prelude::*; use citadel_wire::exports::tokio_rustls::rustls::ClientConfig; use citadel_wire::socket_helpers::is_ipv6_enabled; @@ -58,7 +59,7 @@ pub mod tests { #[case("127.0.0.1:0")] #[case("[::1]:0")] #[timeout(Duration::from_secs(60))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_tcp_or_tls( #[case] addr: SocketAddr, protocols: &Vec, @@ -97,9 +98,11 @@ pub mod tests { on_client_received_stream(stream).await }; - let res = tokio::try_join!(server, client); + let res = citadel_io::tokio::try_join!(server, client); log::trace!("RES: {:?}", res); - let _ = res.unwrap(); + if let Err(err) = res { + log::error!(target: "citadel", "Error: {:?}", err); + } log::trace!(target: "citadel", "Ended"); } @@ -110,7 +113,7 @@ pub mod tests { #[case("127.0.0.1:0")] #[case("[::1]:0")] #[timeout(Duration::from_secs(60))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "current_thread")] async fn test_many_proto_conns( #[case] addr: SocketAddr, protocols: &Vec, @@ -165,7 +168,7 @@ pub mod tests { let client = client.try_collect::>(); // if server ends, bad. If client ends, maybe good - let res = tokio::select! { + let res = citadel_io::tokio::select! { res0 = server => { res0 }, diff --git a/citadel_sdk/Cargo.toml b/citadel_sdk/Cargo.toml index 3ab239a18..1d50afb7e 100644 --- a/citadel_sdk/Cargo.toml +++ b/citadel_sdk/Cargo.toml @@ -36,7 +36,6 @@ citadel_proto = { workspace = true } citadel_io = { workspace = true } citadel_user = { workspace = true } embed-doc-image = { workspace = true, optional = true } -tokio = { workspace = true, features = ["parking_lot"] } futures = { workspace = true } log = { workspace = true } lazy_static = { workspace = true } @@ -50,7 +49,6 @@ citadel_types = { workspace = true } citadel_wire = { workspace = true } [dev-dependencies] -tokio = { workspace = true, features = ["rt"] } citadel_io = { workspace = true } dirs2 = { workspace = true } rstest = { workspace = true } diff --git a/citadel_sdk/examples/client.rs b/citadel_sdk/examples/client.rs index bfa98d5c5..cb9f51f0b 100644 --- a/citadel_sdk/examples/client.rs +++ b/citadel_sdk/examples/client.rs @@ -1,3 +1,5 @@ +use citadel_io::tokio; +use citadel_sdk::prefabs::client::ServerConnectionSettingsBuilder; use citadel_sdk::prelude::*; use std::sync::atomic::{AtomicBool, Ordering}; @@ -10,15 +12,32 @@ async fn main() { let stun2 = get_env("STUN_2_ADDR"); let finished = &AtomicBool::new(false); - let client = citadel_sdk::prefabs::client::single_connection - ::SingleClientServerConnectionKernel::new_register("Dummy user", "dummyusername", "notsecurepassword", addr, UdpMode::Enabled, Default::default(), None, |mut connection, remote| async move { - let chan = connection.udp_channel_rx.take(); - tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions(UdpMode::Enabled, chan)) - .await.map_err(|err| NetworkError::Generic(err.to_string()))?; - finished.store(true, Ordering::SeqCst); - remote.shutdown_kernel().await?; - Ok(()) - }).unwrap(); + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + addr, + "test-username", + "Test user", + "notsecurepassword", + ) + .with_udp_mode(UdpMode::Enabled) + .build() + .unwrap(); + + let client = + citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel::new( + server_connection_settings, + |mut connection, remote| async move { + let chan = connection.udp_channel_rx.take(); + citadel_io::tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions( + UdpMode::Enabled, + chan, + )) + .await + .map_err(|err| NetworkError::Generic(err.to_string()))?; + finished.store(true, Ordering::SeqCst); + remote.shutdown_kernel().await?; + Ok(()) + }, + ); let _ = NodeBuilder::default() .with_node_type(NodeType::Peer) diff --git a/citadel_sdk/examples/peer.rs b/citadel_sdk/examples/peer.rs index a9e1512a1..1b46f93b8 100644 --- a/citadel_sdk/examples/peer.rs +++ b/citadel_sdk/examples/peer.rs @@ -1,3 +1,4 @@ +use citadel_io::tokio; use citadel_sdk::prelude::*; use std::sync::atomic::{AtomicBool, Ordering}; @@ -18,20 +19,23 @@ async fn main() { .ensure_registered() .add(); - let finished = &AtomicBool::new(false); - let peer = citadel_sdk::prefabs::client::peer_connection::PeerConnectionKernel::new_register( - "dummy name", + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + addr, my_peer_id, + "dunny name", "password", + ) + .build() + .unwrap(); + + let finished = &AtomicBool::new(false); + let peer = citadel_sdk::prefabs::client::peer_connection::PeerConnectionKernel::new( + server_connection_settings, agg, - addr, - UdpMode::Enabled, - Default::default(), - None, |mut connection, remote| async move { let mut connection = connection.recv().await.unwrap()?; let chan = connection.udp_channel_rx.take(); - tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions( + citadel_io::tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions( UdpMode::Enabled, chan, )) @@ -43,8 +47,7 @@ async fn main() { } Ok(()) }, - ) - .unwrap(); + ); let _ = NodeBuilder::default() .with_node_type(NodeType::Peer) diff --git a/citadel_sdk/examples/server.rs b/citadel_sdk/examples/server.rs index 5f0fc12d8..84f180794 100644 --- a/citadel_sdk/examples/server.rs +++ b/citadel_sdk/examples/server.rs @@ -1,3 +1,4 @@ +use citadel_io::tokio; use citadel_sdk::prelude::*; use std::net::SocketAddr; use std::str::FromStr; @@ -20,7 +21,7 @@ async fn main() { citadel_sdk::prefabs::server::client_connect_listener::ClientConnectListenerKernel::new( |mut conn, _c2s_remote| async move { let chan = conn.udp_channel_rx.take(); - tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions( + citadel_io::tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions( UdpMode::Enabled, chan, )) diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs index 4a9b45ff1..5967718bc 100644 --- a/citadel_sdk/src/builder/node_builder.rs +++ b/citadel_sdk/src/builder/node_builder.rs @@ -111,7 +111,7 @@ impl NodeBuilder { _pd: Default::default(), inner: Box::pin(async move { log::trace!(target: "citadel", "[NodeBuilder] Checking Tokio runtime ..."); - let rt = tokio::runtime::Handle::try_current() + let rt = citadel_io::tokio::runtime::Handle::try_current() .map_err(|err| NetworkError::Generic(err.to_string()))?; log::trace!(target: "citadel", "[NodeBuilder] Creating account manager ..."); let account_manager = AccountManager::new( @@ -261,10 +261,15 @@ impl NodeBuilder { /// The file should be a DER formatted certificate pub async fn with_pem_file>(&mut self, path: P) -> anyhow::Result<&mut Self> { - let mut der = std::io::Cursor::new(tokio::fs::read(path).await?); - let certs = citadel_proto::re_imports::rustls_pemfile::certs(&mut der)?; + let mut der = std::io::Cursor::new(citadel_io::tokio::fs::read(path).await?); + let certs = citadel_proto::re_imports::rustls_pemfile::certs(&mut der).collect::>(); + // iter certs and try collecting on the results + let mut filtered_certs = Vec::new(); + for cert in certs { + filtered_certs.push(cert?); + } self.client_tls_config = Some(citadel_proto::re_imports::create_rustls_client_config( - &certs, + &filtered_certs, )?); Ok(self) } @@ -311,6 +316,7 @@ mod tests { use crate::builder::node_builder::NodeBuilder; use crate::prefabs::server::empty::EmptyKernel; use crate::prelude::{BackendType, NodeType}; + use citadel_io::tokio; use citadel_proto::prelude::{KernelExecutorSettings, ServerUnderlyingProtocol}; use rstest::rstest; use std::str::FromStr; diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index 4831b6775..516dff347 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -83,8 +83,11 @@ mod tests { }; use crate::prefabs::client::peer_connection::{FileTransferHandleRx, PeerConnectionKernel}; + use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prelude::*; use crate::test_common::wait_for_peers; + use citadel_io::tokio; + use futures::StreamExt; use rstest::rstest; use std::net::SocketAddr; use std::path::PathBuf; @@ -108,7 +111,7 @@ mod tests { SigAlgorithm::Falcon1024 )] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test] + #[citadel_io::tokio::test] async fn test_c2s_file_transfer_revfs( #[case] enx: EncryptionAlgorithm, #[case] kem: KemAlgorithm, @@ -128,13 +131,16 @@ mod tests { .build() .unwrap(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - UdpMode::Disabled, - session_security_settings, - None, - |_success, remote| async move { + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .disable_udp() + .with_session_security_settings(session_security_settings) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, + |_channel, remote| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf"); // write to file to the RE-VFS @@ -150,19 +156,18 @@ mod tests { let save_dir = crate::fs::read(&remote, virtual_path).await?; // now, compare bytes log::info!(target: "citadel", "***CLIENT REVFS PULL SUCCESS"); - let original_bytes = tokio::fs::read(&source_dir).await.unwrap(); - let revfs_pulled_bytes = tokio::fs::read(&save_dir).await.unwrap(); + let original_bytes = citadel_io::tokio::fs::read(&source_dir).await.unwrap(); + let revfs_pulled_bytes = citadel_io::tokio::fs::read(&save_dir).await.unwrap(); assert_eq!(original_bytes, revfs_pulled_bytes); log::info!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS"); client_success.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); - let result = tokio::select! { + let result = citadel_io::tokio::select! { res0 = client => res0.map(|_| ()), res1 = server => res1.map(|_| ()) }; @@ -179,7 +184,7 @@ mod tests { SigAlgorithm::None )] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test] + #[citadel_io::tokio::test] async fn test_c2s_file_transfer_revfs_take( #[case] enx: EncryptionAlgorithm, #[case] kem: KemAlgorithm, @@ -199,12 +204,15 @@ mod tests { .build() .unwrap(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - UdpMode::Disabled, - session_security_settings, - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .disable_udp() + .with_session_security_settings(session_security_settings) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |_channel, remote| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf"); @@ -221,8 +229,8 @@ mod tests { let save_dir = crate::fs::take(&remote, &virtual_path).await?; // now, compare bytes log::trace!(target: "citadel", "***CLIENT REVFS PULL SUCCESS"); - let original_bytes = tokio::fs::read(&source_dir).await.unwrap(); - let revfs_pulled_bytes = tokio::fs::read(&save_dir).await.unwrap(); + let original_bytes = citadel_io::tokio::fs::read(&source_dir).await.unwrap(); + let revfs_pulled_bytes = citadel_io::tokio::fs::read(&save_dir).await.unwrap(); assert_eq!(original_bytes, revfs_pulled_bytes); log::trace!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS"); // prove we can no longer read from this virtual file @@ -230,12 +238,11 @@ mod tests { client_success.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); - let result = tokio::select! { + let result = citadel_io::tokio::select! { res0 = client => res0.map(|_| ()), res1 = server => res1.map(|_| ()) }; @@ -252,7 +259,7 @@ mod tests { SigAlgorithm::None )] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test] + #[citadel_io::tokio::test] async fn test_c2s_file_transfer_revfs_delete( #[case] enx: EncryptionAlgorithm, #[case] kem: KemAlgorithm, @@ -272,12 +279,15 @@ mod tests { .build() .unwrap(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - UdpMode::Disabled, - session_security_settings, - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .disable_udp() + .with_session_security_settings(session_security_settings) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |_channel, remote| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf"); @@ -294,8 +304,8 @@ mod tests { let save_dir = crate::fs::read(&remote, &virtual_path).await?; // now, compare bytes log::trace!(target: "citadel", "***CLIENT REVFS PULL SUCCESS"); - let original_bytes = tokio::fs::read(&source_dir).await.unwrap(); - let revfs_pulled_bytes = tokio::fs::read(&save_dir).await.unwrap(); + let original_bytes = citadel_io::tokio::fs::read(&source_dir).await.unwrap(); + let revfs_pulled_bytes = citadel_io::tokio::fs::read(&save_dir).await.unwrap(); assert_eq!(original_bytes, revfs_pulled_bytes); log::trace!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS"); crate::fs::delete(&remote, &virtual_path).await?; @@ -304,12 +314,11 @@ mod tests { client_success.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); - let result = tokio::select! { + let result = citadel_io::tokio::select! { res0 = client => res0.map(|_| ()), res1 = server => res1.map(|_| ()) }; @@ -322,7 +331,7 @@ mod tests { #[rstest] #[case(SecrecyMode::BestEffort)] #[timeout(Duration::from_secs(60))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_p2p_file_transfer_revfs( #[case] secrecy_mode: SecrecyMode, #[values(KemAlgorithm::Kyber)] kem: KemAlgorithm, @@ -347,15 +356,17 @@ mod tests { let source_dir = &PathBuf::from("../resources/TheBridge.pdf"); + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid0) + .disable_udp() + .with_session_security_settings(session_security) + .build() + .unwrap(); + // TODO: SinglePeerConnectionKernel - // to not hold up all conns - let client_kernel0 = PeerConnectionKernel::new_authless( - uuid0, - server_addr, - vec![uuid1.into()], - UdpMode::Disabled, - session_security, - None, + let client_kernel0 = PeerConnectionKernel::new( + server_connection_settings, + uuid1, move |mut connection, remote_outer| async move { wait_for_peers().await; let mut connection = connection.recv().await.unwrap()?; @@ -391,16 +402,18 @@ mod tests { client0_success.store(true, Ordering::Relaxed); remote_outer.shutdown_kernel().await }, - ) - .unwrap(); + ); - let client_kernel1 = PeerConnectionKernel::new_authless( - uuid1, - server_addr, - vec![uuid0.into()], - UdpMode::Disabled, - session_security, - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid1) + .disable_udp() + .with_session_security_settings(session_security) + .build() + .unwrap(); + + let client_kernel1 = PeerConnectionKernel::new( + server_connection_settings, + uuid0, move |mut connection, remote_outer| async move { wait_for_peers().await; let mut connection = connection.recv().await.unwrap()?; @@ -428,29 +441,28 @@ mod tests { let save_dir = crate::fs::read(&remote, virtual_path).await?; // now, compare bytes log::info!(target: "citadel", "***CLIENT B {cid} REVFS PULL SUCCESS"); - let original_bytes = tokio::fs::read(&source_dir).await.unwrap(); - let revfs_pulled_bytes = tokio::fs::read(&save_dir).await.unwrap(); + let original_bytes = citadel_io::tokio::fs::read(&source_dir).await.unwrap(); + let revfs_pulled_bytes = citadel_io::tokio::fs::read(&save_dir).await.unwrap(); assert_eq!(original_bytes, revfs_pulled_bytes); log::info!(target: "citadel", "***CLIENT B {cid} REVFS PULL COMPARE SUCCESS"); wait_for_peers().await; client1_success.store(true, Ordering::Relaxed); remote_outer.shutdown_kernel().await }, - ) - .unwrap(); + ); let client0 = NodeBuilder::default().build(client_kernel0).unwrap(); let client1 = NodeBuilder::default().build(client_kernel1).unwrap(); let clients = futures::future::try_join(client0, client1); let task = async move { - tokio::select! { + citadel_io::tokio::select! { server_res = server => Err(NetworkError::msg(format!("Server ended prematurely: {:?}", server_res.map(|_| ())))), client_res = clients => client_res.map(|_| ()) } }; - let _ = tokio::time::timeout(Duration::from_secs(120), task) + let _ = citadel_io::tokio::time::timeout(Duration::from_secs(120), task) .await .unwrap(); @@ -459,7 +471,7 @@ mod tests { } fn accept_all(mut rx: FileTransferHandleRx) { - let handle = tokio::task::spawn(async move { + let handle = citadel_io::tokio::task::spawn(async move { while let Some(mut handle) = rx.recv().await { if let Err(err) = handle.accept() { log::error!(target: "citadel", "Failed to accept file transfer: {err:?}"); diff --git a/citadel_sdk/src/lib.rs b/citadel_sdk/src/lib.rs index 316f3228d..96b7a4a62 100644 --- a/citadel_sdk/src/lib.rs +++ b/citadel_sdk/src/lib.rs @@ -130,9 +130,11 @@ //! ``` //! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel; //! use futures::StreamExt; -//! use citadel_sdk::prelude::NodeBuilder; +//! use citadel_sdk::prelude::*; +//! +//! let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?; //! -//! let client_kernel = SingleClientServerConnectionKernel::new_register_defaults("John Doe", "john.doe", "password", "127.0.0.1:25021", |connect_success, remote| async move { +//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |connect_success, remote| async move { //! // handle program logic here //! let (sink, mut stream) = connect_success.channel.split(); //! while let Some(message) = stream.next().await { @@ -140,7 +142,7 @@ //! } //! //! Ok(()) -//! })?; +//! }); //! //! let client = NodeBuilder::default().build(client_kernel)?; //! # async move { @@ -184,10 +186,11 @@ //! ``` //! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel; //! use futures::StreamExt; -//! use citadel_proto::prelude::*; -//! use citadel_sdk::prelude::NodeBuilder; +//! use citadel_sdk::prelude::*; +//! +//! let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?; //! -//! let client_kernel = SingleClientServerConnectionKernel::new_register_defaults("John Doe", "john.doe", "password", "127.0.0.1:25021", |connect_success, mut remote| async move { +//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |connect_success, mut remote| async move { //! let virtual_path = "/home/virtual_user/output.pdf"; //! // write the contents with reinforced security. //! citadel_sdk::fs::write_with_security_level(&mut remote, "../path/to/input.pdf", SecurityLevel::Reinforced, virtual_path).await?; @@ -195,7 +198,7 @@ //! let stored_local_path = citadel_sdk::fs::read(&mut remote, virtual_path).await?; //! //! Ok(()) -//! })?; +//! }); //! //! let client = NodeBuilder::default().build(client_kernel)?; //! # async move { @@ -233,6 +236,7 @@ pub mod prelude { pub use crate::builder::node_builder::*; pub use crate::prefabs::client::peer_connection::PeerConnectionSetupAggregator; pub use crate::prefabs::client::PrefabFunctions; + pub use crate::prefabs::client::{ServerConnectionSettings, ServerConnectionSettingsBuilder}; pub use crate::remote_ext::user_ids::*; pub use crate::remote_ext::*; pub use crate::responses; diff --git a/citadel_sdk/src/prefabs/client/broadcast.rs b/citadel_sdk/src/prefabs/client/broadcast.rs index 2609fc8ed..679220722 100644 --- a/citadel_sdk/src/prefabs/client/broadcast.rs +++ b/citadel_sdk/src/prefabs/client/broadcast.rs @@ -1,13 +1,13 @@ use crate::prefabs::ClientServerRemote; use crate::prelude::*; use crate::test_common::wait_for_peers; +use citadel_io::tokio::sync::Mutex; use citadel_user::prelude::UserIdentifierExt; use futures::{Future, StreamExt}; use std::marker::PhantomData; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use tokio::sync::Mutex; use uuid::Uuid; /// A kernel that streamlines creating, connecting, and interacting with groups @@ -23,8 +23,9 @@ pub struct BroadcastKernel<'a, F, Fut> { pub struct BroadcastShared { route_registers: AtomicBool, - register_rx: citadel_io::Mutex>>, - register_tx: tokio::sync::mpsc::UnboundedSender, + register_rx: + citadel_io::Mutex>>, + register_tx: citadel_io::tokio::sync::mpsc::UnboundedSender, } /// Before running the [`BroadcastKernel`], each peer must send this request @@ -73,7 +74,7 @@ where self.shared.clone() } - #[allow(unreachable_code)] + #[allow(unreachable_code, clippy::blocks_in_conditions)] #[cfg_attr( feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err(Debug)) @@ -175,7 +176,10 @@ where if owned_groups.contains(&expected_message_group_key) { break; } else { - tokio::time::sleep(std::time::Duration::from_secs(2u64.pow(retries))).await; + citadel_io::tokio::time::sleep(std::time::Duration::from_secs( + 2u64.pow(retries), + )) + .await; retries += 1; if retries > 4 { @@ -213,7 +217,7 @@ where // Merge the reg_rx stream and the subscription stream let mut count_registered = 0; loop { - let post_register = tokio::select! { + let post_register = citadel_io::tokio::select! { reg_request = reg_rx.recv() => { reg_request.ok_or_else(|| NetworkError::InternalError("reg_rx ended unexpectedly"))? }, @@ -297,7 +301,7 @@ where // Drop the lock to allow the acceptor task to gain access to the subscription drop(lock); return if is_owner { - tokio::try_join!(fx(channel, remote), acceptor_task).map(|_| ()) + citadel_io::tokio::try_join!(fx(channel, remote), acceptor_task).map(|_| ()) } else { fx(channel, remote).await.map(|_| ()) }; @@ -321,7 +325,7 @@ where } fn construct(kernel: Box) -> Self { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); Self { shared: Arc::new(BroadcastShared { route_registers: AtomicBool::new(false), @@ -372,15 +376,17 @@ impl NetKernel for BroadcastKernel<'_, F, Fut> { mod tests { use crate::prefabs::client::broadcast::{BroadcastKernel, GroupInitRequestType}; use crate::prefabs::client::peer_connection::PeerConnectionKernel; + use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prelude::*; use crate::test_common::{server_info, wait_for_peers, TestBarrier}; + use citadel_io::tokio; use futures::prelude::stream::FuturesUnordered; use futures::TryStreamExt; use rstest::rstest; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use uuid::Uuid; - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn group_connect_list_members() -> Result<(), Box> { let peer_count = 3; assert!(peer_count > 1); @@ -416,9 +422,13 @@ mod tests { } }; - let client_kernel = BroadcastKernel::new_authless_defaults( - uuid, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .build() + .unwrap(); + + let client_kernel = BroadcastKernel::new( + server_connection_settings, request, move |channel, remote| async move { wait_for_peers().await; @@ -439,7 +449,7 @@ mod tests { drop(channel); remote.shutdown_kernel().await }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); @@ -463,7 +473,7 @@ mod tests { #[rstest] #[case(2)] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_manual_group_connect( #[case] peer_count: usize, ) -> Result<(), Box> { @@ -494,9 +504,13 @@ mod tests { .map(UserIdentifier::from) .collect::>(); - let client_kernel = PeerConnectionKernel::new_authless_defaults( - uuid, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .build() + .unwrap(); + + let client_kernel = PeerConnectionKernel::new( + server_connection_settings, peers, move |mut results, remote| async move { let _sender = remote.conn_type.get_implicated_cid(); @@ -524,20 +538,21 @@ mod tests { NodeResult::GroupEvent(GroupEvent { implicated_cid: _, ticket: _, - event: GroupBroadcast::Invitation { sender: _, key: _key }, + event: + GroupBroadcast::Invitation { + sender: _, + key: _key, + }, }) => { - let _ = crate::responses::group_invite( - evt, - true, - &remote.inner, - ) - .await?; + let _ = + crate::responses::group_invite(evt, true, &remote.inner) + .await?; } NodeResult::GroupChannelCreated(GroupChannelCreated { ticket: _, channel: _chan, - implicated_cid: _ + implicated_cid: _, }) => { receiver_success.store(true, Ordering::Relaxed); log::trace!(target: "citadel", "***PEER {} CONNECT***", uuid); @@ -556,7 +571,7 @@ mod tests { "signals_recv ended unexpectedly", )) }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); diff --git a/citadel_sdk/src/prefabs/client/mod.rs b/citadel_sdk/src/prefabs/client/mod.rs index 25c5cec08..906f7b6b7 100644 --- a/citadel_sdk/src/prefabs/client/mod.rs +++ b/citadel_sdk/src/prefabs/client/mod.rs @@ -1,7 +1,7 @@ use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; use crate::prefabs::ClientServerRemote; use crate::prelude::*; -use std::net::ToSocketAddrs; +use std::net::{SocketAddr, ToSocketAddrs}; use uuid::Uuid; /// A kernel that assists in creating and/or connecting to a group @@ -30,21 +30,14 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a>: Sized + 'a { fn construct(kernel: Box) -> Self; /// Creates a new connection with a central server entailed by the user information - fn new_connect, P: Into>( - username: T, - password: P, + fn new( + server_connection_settings: ServerConnectionSettings, arg: Arg, - udp_mode: UdpMode, - session_security_settings: SessionSecuritySettings, - server_password: Option, on_channel_received: Self::UserLevelInputFunction, ) -> Self { - let (tx, rx) = tokio::sync::oneshot::channel(); - let server_conn_kernel = SingleClientServerConnectionKernel::new_connect( - username, - password, - udp_mode, - session_security_settings, + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); + let server_conn_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, server_password, |connect_success, remote| { on_channel_received_fn::<_, Self>( @@ -61,151 +54,229 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a>: Sized + 'a { assert!(tx.send(this.get_shared_bundle()).is_ok()); this } +} + +async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg>>( + connect_success: ConnectionSuccess, + remote: ClientServerRemote, + rx_bundle: citadel_io::tokio::sync::oneshot::Receiver, + arg: Arg, + on_channel_received: T::UserLevelInputFunction, +) -> Result<(), NetworkError> { + let shared = rx_bundle + .await + .map_err(|err| NetworkError::Generic(err.to_string()))?; + T::on_c2s_channel_received(connect_success, remote, arg, on_channel_received, shared).await +} + +/// Used to instantiate a client to server connection +pub struct ServerConnectionSettingsBuilder { + password: Option, + username: Option, + name: Option, + psk: Option, + address: Option, + udp_mode: Option, + session_security_settings: Option, + authless_uuid: Option, + is_connect: bool, +} + +impl ServerConnectionSettingsBuilder { + /// Creates a new connection to a central server that uses no credentialed authentication, only a secure channel + pub fn no_credentials(addr: T, id: Uuid) -> Self { + Self { + password: None, + username: None, + udp_mode: None, + session_security_settings: None, + name: None, + psk: None, + authless_uuid: Some(id), + address: Some(addr), + is_connect: false, + } + } - /// Crates a new connection with a central server entailed by the user information and default configuration - fn new_connect_defaults, P: Into>( - username: T, + /// Creates a new connection to a central server that uses a username and password for authentication. This should be used directly when + /// constructing a registration request. If you are logging in, use the `credentialed_login` function instead. + pub fn credentialed_registration, N: Into, P: Into>( + addr: T, + username: U, + alias: N, password: P, - arg: Arg, - on_channel_received: Self::UserLevelInputFunction, ) -> Self { - Self::new_connect( - username, - password, - arg, - Default::default(), - Default::default(), - Default::default(), - on_channel_received, - ) + Self { + password: Some(password.into()), + username: Some(username.into()), + name: Some(alias.into()), + psk: None, + authless_uuid: None, + address: Some(addr), + udp_mode: None, + session_security_settings: None, + is_connect: false, + } } - /// First registers with a central server with the proposed credentials, and thereafter, establishes a connection with custom parameters - #[allow(clippy::too_many_arguments)] - fn new_register, R: Into, P: Into, V: ToSocketAddrs>( - full_name: T, - username: R, + /// Creates a new connection to a central server that uses a username and password for authentication. This should be used for the login process + pub fn credentialed_login, P: Into>( + addr: T, + username: U, password: P, - arg: Arg, - server_addr: V, - udp_mode: UdpMode, - session_security_settings: SessionSecuritySettings, - server_password: Option, - on_channel_received: Self::UserLevelInputFunction, - ) -> Result { - let (tx, rx) = tokio::sync::oneshot::channel(); - let server_conn_kernel = SingleClientServerConnectionKernel::new_register( - full_name, - username, - password, - server_addr, - udp_mode, - session_security_settings, - server_password, - |connect_success, remote| { - on_channel_received_fn::<_, Self>( - connect_success, - remote, - rx, - arg, - on_channel_received, - ) - }, - )?; + ) -> Self { + Self { + password: Some(password.into()), + username: Some(username.into()), + name: None, + psk: None, + authless_uuid: None, + address: Some(addr), + udp_mode: None, + session_security_settings: None, + is_connect: true, + } + } - let this = Self::construct(Box::new(server_conn_kernel)); - assert!(tx.send(this.get_shared_bundle()).is_ok()); - Ok(this) + /// Adds a pre-shared key to the client-to-server connection. If the server expects a PSK, this is necessary. + pub fn with_session_password>(mut self, psk: V) -> Self { + self.psk = Some(psk.into()); + self } - /// First registers with a central server with the proposed credentials, and thereafter, establishes a connection with default parameters - fn new_register_defaults< - T: Into, - R: Into, - P: Into, - V: ToSocketAddrs, - >( - full_name: T, - username: R, - password: P, - arg: Arg, - server_addr: V, - on_channel_received: Self::UserLevelInputFunction, - ) -> Result { - Self::new_register( - full_name, - username, - password, - arg, - server_addr, - Default::default(), - Default::default(), - Default::default(), - on_channel_received, - ) + /// Sets the UDP mode for the client-to-server connection + pub fn with_udp_mode(mut self, mode: UdpMode) -> Self { + self.udp_mode = Some(mode); + self } - /// Creates a new authless connection with custom arguments - fn new_authless( + /// Disables the UDP mode for the client-to-server connection. The default setting is Disabled + pub fn disable_udp(self) -> Self { + self.with_udp_mode(UdpMode::Disabled) + } + + /// Adds a session security settings to the client-to-server connection. This is necessary for the server to know how to handle the connection. + pub fn with_session_security_settings>( + mut self, + settings: V, + ) -> Self { + self.session_security_settings = Some(settings.into()); + self + } + + /// Builds the client-to-server connection settings + pub fn build(self) -> Result { + let server_addr = if let Some(addr) = self.address { + let addr = addr + .to_socket_addrs() + .map_err(|err| NetworkError::Generic(err.to_string()))? + .next() + .ok_or(NetworkError::Generic("No address found".to_string()))?; + Some(addr) + } else { + None + }; + + if let Some(uuid) = self.authless_uuid { + Ok(ServerConnectionSettings::NoCredentials { + server_addr: server_addr + .ok_or(NetworkError::Generic("No address found".to_string()))?, + uuid, + udp_mode: self.udp_mode.unwrap_or_default(), + session_security_settings: self.session_security_settings.unwrap_or_default(), + pre_shared_key: self.psk, + }) + } else if self.is_connect { + Ok(ServerConnectionSettings::CredentialedConnect { + username: self + .username + .ok_or(NetworkError::Generic("No username found".to_string()))?, + password: self + .password + .ok_or(NetworkError::Generic("No password found".to_string()))?, + udp_mode: self.udp_mode.unwrap_or_default(), + session_security_settings: self.session_security_settings.unwrap_or_default(), + pre_shared_key: self.psk, + }) + } else { + Ok(ServerConnectionSettings::CredentialedRegister { + address: server_addr + .ok_or(NetworkError::Generic("No address found".to_string()))?, + username: self + .username + .ok_or(NetworkError::Generic("No username found".to_string()))?, + alias: self + .name + .ok_or(NetworkError::Generic("No alias found".to_string()))?, + password: self + .password + .ok_or(NetworkError::Generic("No password found".to_string()))?, + pre_shared_key: self.psk, + udp_mode: self.udp_mode.unwrap_or_default(), + session_security_settings: self.session_security_settings.unwrap_or_default(), + }) + } + } +} + +/// The settings for a client-to-server connection +pub enum ServerConnectionSettings { + NoCredentials { + server_addr: SocketAddr, uuid: Uuid, - server_addr: V, - arg: Arg, udp_mode: UdpMode, session_security_settings: SessionSecuritySettings, - server_password: Option, - on_channel_received: Self::UserLevelInputFunction, - ) -> Result { - let (tx, rx) = tokio::sync::oneshot::channel(); - let server_conn_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - udp_mode, - session_security_settings, - server_password, - |connect_success, remote| { - on_channel_received_fn::<_, Self>( - connect_success, - remote, - rx, - arg, - on_channel_received, - ) - }, - )?; + pre_shared_key: Option, + }, + CredentialedConnect { + username: String, + password: SecBuffer, + udp_mode: UdpMode, + session_security_settings: SessionSecuritySettings, + pre_shared_key: Option, + }, + CredentialedRegister { + address: SocketAddr, + username: String, + alias: String, + password: SecBuffer, + pre_shared_key: Option, + udp_mode: UdpMode, + session_security_settings: SessionSecuritySettings, + }, +} - let this = Self::construct(Box::new(server_conn_kernel)); - assert!(tx.send(this.get_shared_bundle()).is_ok()); - Ok(this) +impl ServerConnectionSettings { + pub(crate) fn udp_mode(&self) -> UdpMode { + match self { + Self::NoCredentials { udp_mode, .. } => *udp_mode, + Self::CredentialedRegister { udp_mode, .. } => *udp_mode, + Self::CredentialedConnect { udp_mode, .. } => *udp_mode, + } } - /// Creates a new authless connection with default arguments - fn new_authless_defaults( - uuid: Uuid, - server_addr: V, - arg: Arg, - on_channel_received: Self::UserLevelInputFunction, - ) -> Result { - Self::new_authless( - uuid, - server_addr, - arg, - Default::default(), - Default::default(), - Default::default(), - on_channel_received, - ) + pub(crate) fn session_security_settings(&self) -> SessionSecuritySettings { + match self { + Self::NoCredentials { + session_security_settings, + .. + } => *session_security_settings, + Self::CredentialedRegister { + session_security_settings, + .. + } => *session_security_settings, + Self::CredentialedConnect { + session_security_settings, + .. + } => *session_security_settings, + } } -} -async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg>>( - connect_success: ConnectionSuccess, - remote: ClientServerRemote, - rx_bundle: tokio::sync::oneshot::Receiver, - arg: Arg, - on_channel_received: T::UserLevelInputFunction, -) -> Result<(), NetworkError> { - let shared = rx_bundle - .await - .map_err(|err| NetworkError::Generic(err.to_string()))?; - T::on_c2s_channel_received(connect_success, remote, arg, on_channel_received, shared).await + pub(crate) fn pre_shared_key(&self) -> Option<&PreSharedKey> { + match self { + Self::NoCredentials { pre_shared_key, .. } => pre_shared_key.as_ref(), + Self::CredentialedRegister { pre_shared_key, .. } => pre_shared_key.as_ref(), + Self::CredentialedConnect { pre_shared_key, .. } => pre_shared_key.as_ref(), + } + } } diff --git a/citadel_sdk/src/prefabs/client/peer_connection.rs b/citadel_sdk/src/prefabs/client/peer_connection.rs index 8cd487206..d8d0c7be6 100644 --- a/citadel_sdk/src/prefabs/client/peer_connection.rs +++ b/citadel_sdk/src/prefabs/client/peer_connection.rs @@ -2,6 +2,7 @@ use crate::prefabs::ClientServerRemote; use crate::prelude::results::PeerConnectSuccess; use crate::prelude::*; use crate::test_common::wait_for_peers; +use citadel_io::tokio::sync::mpsc::{Receiver, UnboundedSender}; use citadel_io::Mutex; use citadel_proto::re_imports::async_trait; use citadel_user::hypernode_account::UserIdentifierExt; @@ -10,12 +11,9 @@ use futures::{Future, TryStreamExt}; use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; -use tokio::sync::mpsc::{Receiver, UnboundedSender}; +use uuid::Uuid; -/// A kernel that connects with the given credentials. If the credentials are not yet registered, then the [`Self::new_register`] function may be used, which will register the account before connecting. -/// This kernel will only allow outbound communication for the provided account -/// -/// After establishing a connection to the central node, this kernel then begins connecting to the desired +/// After establishing a connection to the central node, this kernel begins connecting to the desired /// peer(s) pub struct PeerConnectionKernel<'a, F, Fut> { inner_kernel: Box, @@ -38,12 +36,12 @@ struct PeerContext { #[derive(Debug)] pub struct FileTransferHandleRx { - pub inner: tokio::sync::mpsc::UnboundedReceiver, + pub inner: citadel_io::tokio::sync::mpsc::UnboundedReceiver, pub conn_type: VirtualTargetType, } impl std::ops::Deref for FileTransferHandleRx { - type Target = tokio::sync::mpsc::UnboundedReceiver; + type Target = citadel_io::tokio::sync::mpsc::UnboundedReceiver; fn deref(&self) -> &Self::Target { &self.inner @@ -282,6 +280,34 @@ impl From for PeerConnectionSetupAggregator { } } +impl From for PeerConnectionSetupAggregator { + fn from(user: Uuid) -> Self { + let user_identifier: UserIdentifier = user.into(); + user_identifier.into() + } +} + +impl From for PeerConnectionSetupAggregator { + fn from(this: String) -> Self { + let user_identifier: UserIdentifier = this.into(); + user_identifier.into() + } +} + +impl From<&str> for PeerConnectionSetupAggregator { + fn from(this: &str) -> Self { + let user_identifier: UserIdentifier = this.into(); + user_identifier.into() + } +} + +impl From for PeerConnectionSetupAggregator { + fn from(this: u64) -> Self { + let user_identifier: UserIdentifier = this.into(); + user_identifier.into() + } +} + #[async_trait] impl<'a, F, Fut, T: Into + Send + 'a> PrefabFunctions<'a, T> for PeerConnectionKernel<'a, F, Fut> @@ -298,6 +324,7 @@ where self.shared.clone() } + #[allow(clippy::blocks_in_conditions)] #[cfg_attr( feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err(Debug)) @@ -326,7 +353,7 @@ where } let remote = cls_remote.inner.clone(); - let (ref tx, rx) = tokio::sync::mpsc::channel(peers_to_connect.len()); + let (ref tx, rx) = citadel_io::tokio::sync::mpsc::channel(peers_to_connect.len()); let requests = FuturesUnordered::new(); for (mutually_registered, peer_to_connect) in @@ -346,7 +373,7 @@ where let task = async move { let inner_task = async move { let (file_transfer_tx, file_transfer_rx) = - tokio::sync::mpsc::unbounded_channel(); + citadel_io::tokio::sync::mpsc::unbounded_channel(); let handle = if let Some(_already_registered) = mutually_registered { remote.find_target(implicated_cid, id).await? } else { @@ -359,7 +386,10 @@ where if handle.is_peer_registered().await? { break; } - tokio::time::sleep(std::time::Duration::from_millis(200)).await; + citadel_io::tokio::time::sleep(std::time::Duration::from_millis( + 200, + )) + .await; } } @@ -405,7 +435,7 @@ where // TODO: What should be done if a peer conn fails? No room for error here let collection_task = async move { requests.try_collect::<()>().await }; - tokio::try_join!(collection_task, f(rx, cls_remote)).map(|_| ()) + citadel_io::tokio::try_join!(collection_task, f(rx, cls_remote)).map(|_| ()) } fn construct(kernel: Box) -> Self { @@ -422,8 +452,10 @@ where #[cfg(test)] mod tests { use crate::prefabs::client::peer_connection::PeerConnectionKernel; + use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prelude::*; use crate::test_common::{server_info, wait_for_peers, TestBarrier}; + use citadel_io::tokio; use citadel_user::prelude::UserIdentifierExt; use futures::stream::FuturesUnordered; use futures::TryStreamExt; @@ -444,7 +476,7 @@ mod tests { #[rstest] #[case(2, UdpMode::Enabled)] #[case(3, UdpMode::Disabled)] - #[timeout(std::time::Duration::from_secs(90))] + #[timeout(Duration::from_secs(90))] #[tokio::test(flavor = "multi_thread")] async fn peer_to_peer_connect(#[case] peer_count: usize, #[case] udp_mode: UdpMode) { assert!(peer_count > 1); @@ -478,14 +510,19 @@ mod tests { .add(); } - let username = username.clone(); - - let client_kernel = PeerConnectionKernel::new_register_defaults( - full_name.as_str(), - username.clone().as_str(), - password.as_str(), + let server_connection_settings = + ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + username, + full_name, + password.as_str(), + ) + .build() + .unwrap(); + + let client_kernel = PeerConnectionKernel::new( + server_connection_settings, agg.clone(), - server_addr, move |mut results, mut remote| async move { let mut success = 0; let mut p2p_remotes = HashMap::new(); @@ -493,9 +530,14 @@ mod tests { while let Some(conn) = results.recv().await { log::trace!(target: "citadel", "User {} received {:?}", username, conn); let mut conn = conn?; - crate::test_common::udp_mode_assertions(udp_mode, conn.udp_channel_rx.take()).await; + crate::test_common::udp_mode_assertions( + udp_mode, + conn.udp_channel_rx.take(), + ) + .await; success += 1; - let _ = p2p_remotes.insert(conn.channel.get_peer_cid(), conn.remote.clone()); + let _ = + p2p_remotes.insert(conn.channel.get_peer_cid(), conn.remote.clone()); if success == peer_count - 1 { break; } @@ -526,7 +568,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); @@ -542,7 +584,7 @@ mod tests { #[rstest] #[case(2)] #[case(3)] - #[timeout(std::time::Duration::from_secs(90))] + #[timeout(Duration::from_secs(90))] #[tokio::test(flavor = "multi_thread")] async fn peer_to_peer_connect_passwordless( #[case] peer_count: usize, @@ -570,9 +612,13 @@ mod tests { .map(UserIdentifier::from) .collect::>(); - let client_kernel = PeerConnectionKernel::new_authless_defaults( - uuid, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .build() + .unwrap(); + + let client_kernel = PeerConnectionKernel::new( + server_connection_settings, peers, move |mut results, remote| async move { let mut success = 0; @@ -614,7 +660,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); @@ -636,7 +682,7 @@ mod tests { #[rstest] #[case(2)] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_peer_to_peer_file_transfer( #[case] peer_count: usize, ) -> Result<(), Box> { @@ -663,9 +709,13 @@ mod tests { .map(UserIdentifier::from) .collect::>(); - let client_kernel = PeerConnectionKernel::new_authless_defaults( - uuid, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .build() + .unwrap(); + + let client_kernel = PeerConnectionKernel::new( + server_connection_settings, peers, move |mut results, remote| async move { let mut success = 0; @@ -685,7 +735,7 @@ mod tests { .send_file_with_custom_opts( "../resources/TheBridge.pdf", 32 * 1024, - TransferType::FileTransfer + TransferType::FileTransfer, ) .await?; @@ -701,8 +751,8 @@ mod tests { .unwrap(); handle.accept().unwrap(); - use futures::StreamExt; use citadel_types::proto::ObjectTransferStatus; + use futures::StreamExt; let mut path = None; while let Some(status) = handle.next().await { match status { @@ -711,7 +761,9 @@ mod tests { let cmp = include_bytes!("../../../../resources/TheBridge.pdf"); let streamed_data = - tokio::fs::read(path.clone().unwrap()).await.unwrap(); + citadel_io::tokio::fs::read(path.clone().unwrap()) + .await + .unwrap(); assert_eq!( cmp, streamed_data.as_slice(), @@ -743,7 +795,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); @@ -766,7 +818,7 @@ mod tests { #[rstest] #[case(2)] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_peer_to_peer_rekey( #[case] peer_count: usize, ) -> Result<(), Box> { @@ -791,9 +843,13 @@ mod tests { .map(UserIdentifier::from) .collect::>(); - let client_kernel = PeerConnectionKernel::new_authless_defaults( - uuid, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .build() + .unwrap(); + + let client_kernel = PeerConnectionKernel::new( + server_connection_settings, peers, move |mut results, remote| async move { let mut success = 0; @@ -821,7 +877,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); @@ -843,7 +899,7 @@ mod tests { #[rstest] #[case(2)] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_peer_to_peer_disconnect( #[case] peer_count: usize, ) -> Result<(), Box> { @@ -868,9 +924,13 @@ mod tests { .map(UserIdentifier::from) .collect::>(); - let client_kernel = PeerConnectionKernel::new_authless_defaults( - uuid, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .build() + .unwrap(); + + let client_kernel = PeerConnectionKernel::new( + server_connection_settings, peers, move |mut results, remote| async move { let mut success = 0; @@ -892,7 +952,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); @@ -914,7 +974,7 @@ mod tests { #[rstest] #[case(SecrecyMode::BestEffort, Some("test-p2p-password"))] #[timeout(std::time::Duration::from_secs(240))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_p2p_wrong_session_password( #[case] secrecy_mode: SecrecyMode, #[case] p2p_password: Option<&'static str>, @@ -922,7 +982,7 @@ mod tests { #[values(EncryptionAlgorithm::AES_GCM_256)] enx: EncryptionAlgorithm, ) { citadel_logging::setup_log_no_panic_hook(); - crate::test_common::TestBarrier::setup(2); + TestBarrier::setup(2); let (server, server_addr) = server_info(); let peer_0_error_received = &AtomicBool::new(false); let peer_1_error_received = &AtomicBool::new(false); @@ -955,13 +1015,23 @@ mod tests { let peer1_connection = peer1_agg.add(); - let client_kernel0 = PeerConnectionKernel::new_authless( - uuid0, - server_addr, + let server_connection_settings0 = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid0) + .with_udp_mode(UdpMode::Enabled) + .with_session_security_settings(session_security) + .build() + .unwrap(); + + let server_connection_settings1 = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid1) + .with_udp_mode(UdpMode::Enabled) + .with_session_security_settings(session_security) + .build() + .unwrap(); + + let client_kernel0 = PeerConnectionKernel::new( + server_connection_settings0, peer0_connection, - UdpMode::Enabled, - session_security, - None, move |mut connection, remote| async move { wait_for_peers().await; let conn = connection.recv().await.unwrap(); @@ -972,16 +1042,11 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ) - .unwrap(); + ); - let client_kernel1 = PeerConnectionKernel::new_authless( - uuid1, - server_addr, + let client_kernel1 = PeerConnectionKernel::new( + server_connection_settings1, peer1_connection, - UdpMode::Enabled, - session_security, - None, move |mut connection, remote| async move { wait_for_peers().await; let conn = connection.recv().await.unwrap(); @@ -992,8 +1057,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client0 = NodeBuilder::default().build(client_kernel0).unwrap(); let client1 = NodeBuilder::default().build(client_kernel1).unwrap(); diff --git a/citadel_sdk/src/prefabs/client/single_connection.rs b/citadel_sdk/src/prefabs/client/single_connection.rs index f64775cdb..972f6e677 100644 --- a/citadel_sdk/src/prefabs/client/single_connection.rs +++ b/citadel_sdk/src/prefabs/client/single_connection.rs @@ -1,24 +1,25 @@ use crate::prefabs::client::peer_connection::FileTransferHandleRx; -use crate::prefabs::{get_socket_addr, ClientServerRemote}; +use crate::prefabs::client::ServerConnectionSettings; +use crate::prefabs::ClientServerRemote; use crate::remote_ext::ConnectionSuccess; use crate::remote_ext::ProtocolRemoteExt; use citadel_io::Mutex; use citadel_proto::prelude::*; use futures::Future; use std::marker::PhantomData; -use std::net::{SocketAddr, ToSocketAddrs}; +use std::net::SocketAddr; use uuid::Uuid; -/// A kernel that connects with the given credentials. If the credentials are not yet registered, then the [`Self::new_register`] function may be used, which will register the account before connecting. -/// This kernel will only allow outbound communication for the provided account. -/// -/// This [`NetKernel`] is the base kernel type for other built-in implementations of [`NetKernel`] +/// This [`SingleClientServerConnectionKernel`] is the base kernel type for other built-in implementations of [`NetKernel`]. +/// It establishes connections to a central node for purposes of NAT traversal and peer discovery, and depending on the application layer, +/// can leverage the client to server connection for other purposes that require communication between the two. pub struct SingleClientServerConnectionKernel { handler: Mutex>, udp_mode: UdpMode, auth_info: Mutex>, session_security_settings: SessionSecuritySettings, - unprocessed_signal_filter_tx: Mutex>>, + unprocessed_signal_filter_tx: + Mutex>>, remote: Option, server_password: Option, rx_incoming_object_transfer_handle: Mutex>, @@ -62,149 +63,56 @@ where (tx, Mutex::new(Some(rx))) } - /// Creates a new connection with a central server entailed by the user information - pub fn new_connect, P: Into>( - username: T, - password: P, - udp_mode: UdpMode, - session_security_settings: SessionSecuritySettings, - server_password: Option, - on_channel_received: F, - ) -> Self { + /// Creates a new [`SingleClientServerConnectionKernel`] with the given settings. + /// The [`ServerConnectionSettings`] must be provided, and the `on_channel_received` function will be called when the connection is established. + pub fn new(settings: ServerConnectionSettings, on_channel_received: F) -> Self { + let (udp_mode, session_security_settings) = + (settings.udp_mode(), settings.session_security_settings()); + let server_password = settings.pre_shared_key().cloned(); let (tx_incoming_object_transfer_handle, rx_incoming_object_transfer_handle) = Self::generate_object_transfer_handle(); - Self { - handler: Mutex::new(Some(on_channel_received)), - udp_mode, - auth_info: Mutex::new(Some(ConnectionType::Connect { - username: username.into(), - password: password.into(), - })), - rx_incoming_object_transfer_handle, - tx_incoming_object_transfer_handle, - session_security_settings, - unprocessed_signal_filter_tx: Default::default(), - remote: None, - server_password, - _pd: Default::default(), - } - } - /// Crates a new connection with a central server entailed by the user information and default configuration - pub fn new_connect_defaults, P: Into>( - username: T, - password: P, - on_channel_received: F, - ) -> Self { - Self::new_connect( - username, - password, - Default::default(), - Default::default(), - Default::default(), - on_channel_received, - ) - } - - /// First registers with a central server with the proposed credentials, and thereafter, establishes a connection with custom parameters - #[allow(clippy::too_many_arguments)] - pub fn new_register, R: Into, P: Into, V: ToSocketAddrs>( - full_name: T, - username: R, - password: P, - server_addr: V, - udp_mode: UdpMode, - session_security_settings: SessionSecuritySettings, - server_password: Option, - on_channel_received: F, - ) -> Result { - let server_addr = get_socket_addr(server_addr)?; - let (tx_incoming_object_transfer_handle, rx_incoming_object_transfer_handle) = - Self::generate_object_transfer_handle(); - Ok(Self { - handler: Mutex::new(Some(on_channel_received)), - udp_mode, - auth_info: Mutex::new(Some(ConnectionType::Register { - full_name: full_name.into(), - server_addr, - username: username.into(), - password: password.into(), - })), - session_security_settings, - unprocessed_signal_filter_tx: Default::default(), - rx_incoming_object_transfer_handle, - tx_incoming_object_transfer_handle, - remote: None, - server_password, - _pd: Default::default(), - }) - } + let connection_type = match settings { + ServerConnectionSettings::CredentialedConnect { + username, password, .. + } => ConnectionType::Connect { username, password }, + + ServerConnectionSettings::NoCredentials { + server_addr: address, + uuid, + .. + } => ConnectionType::Passwordless { + uuid, + server_addr: address, + }, - /// First registers with a central server with the proposed credentials, and thereafter, establishes a connection with default parameters - pub fn new_register_defaults< - T: Into, - R: Into, - P: Into, - V: ToSocketAddrs, - >( - full_name: T, - username: R, - password: P, - server_addr: V, - on_channel_received: F, - ) -> Result { - Self::new_register( - full_name, - username, - password, - server_addr, - Default::default(), - Default::default(), - Default::default(), - on_channel_received, - ) - } + ServerConnectionSettings::CredentialedRegister { + alias, + username, + password, + address, + .. + } => ConnectionType::Register { + full_name: alias, + server_addr: address, + username, + password, + }, + }; - /// Creates a new authless connection with custom arguments - pub fn new_authless( - uuid: Uuid, - server_addr: V, - udp_mode: UdpMode, - session_security_settings: SessionSecuritySettings, - server_password: Option, - on_channel_received: F, - ) -> Result { - let server_addr = get_socket_addr(server_addr)?; - let (tx_incoming_object_transfer_handle, rx_incoming_object_transfer_handle) = - Self::generate_object_transfer_handle(); - Ok(Self { + Self { handler: Mutex::new(Some(on_channel_received)), udp_mode, - auth_info: Mutex::new(Some(ConnectionType::Passwordless { uuid, server_addr })), + auth_info: Mutex::new(Some(connection_type)), session_security_settings, unprocessed_signal_filter_tx: Default::default(), rx_incoming_object_transfer_handle, tx_incoming_object_transfer_handle, server_password, remote: None, + server_password, _pd: Default::default(), - }) - } - - /// Creates a new authless connection with default arguments - pub fn new_passwordless_defaults( - uuid: Uuid, - server_addr: V, - on_channel_received: F, - ) -> Result { - Self::new_authless( - uuid, - server_addr, - Default::default(), - Default::default(), - Default::default(), - on_channel_received, - ) + } } } @@ -219,6 +127,7 @@ where Ok(()) } + #[allow(clippy::blocks_in_conditions)] #[cfg_attr( feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, err(Debug)) @@ -291,7 +200,7 @@ where handle.conn_type.set_implicated_cid(connect_success.cid); - let (reroute_tx, reroute_rx) = tokio::sync::mpsc::unbounded_channel(); + let (reroute_tx, reroute_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); *self.unprocessed_signal_filter_tx.lock() = Some(reroute_tx); handler( @@ -336,9 +245,11 @@ where #[cfg(test)] mod tests { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; + use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prefabs::ClientServerRemote; use crate::prelude::*; use crate::test_common::{server_info_reactive, wait_for_peers, TestBarrier}; + use citadel_io::tokio; use rstest::rstest; use std::sync::atomic::{AtomicBool, Ordering}; use uuid::Uuid; @@ -375,7 +286,7 @@ mod tests { #[rstest] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_single_connection_registered( #[values(UdpMode::Enabled, UdpMode::Disabled)] udp_mode: UdpMode, #[values(ServerUnderlyingProtocol::new_quic_self_signed(), ServerUnderlyingProtocol::new_tls_self_signed().unwrap())] @@ -402,13 +313,18 @@ mod tests { }, ); - let client_kernel = SingleClientServerConnectionKernel::new_register( - "Thomas P Braun", + let client_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, "nologik", + "Some Alias", "password", - server_addr, - udp_mode, - Default::default(), + ) + .with_udp_mode(udp_mode) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + client_settings, None, |channel, remote| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); @@ -418,8 +334,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); @@ -435,7 +350,7 @@ mod tests { #[case(UdpMode::Enabled, None)] #[case(UdpMode::Enabled, Some("test-password"))] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_single_connection_passwordless( #[case] udp_mode: UdpMode, #[case] server_password: Option<&'static str>, @@ -458,12 +373,19 @@ mod tests { let uuid = Uuid::new_v4(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - udp_mode, - Default::default(), - server_password.map(|x| x.into()), + let mut server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_udp_mode(udp_mode); + + if let Some(server_password) = server_password { + server_connection_settings = + server_connection_settings.with_session_password(server_password); + } + + let server_connection_settings = server_connection_settings.build().unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |channel, remote| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); wait_for_peers().await; @@ -473,8 +395,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); @@ -508,15 +429,17 @@ mod tests { let uuid = Uuid::new_v4(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - udp_mode, - Default::default(), - Some("wrong-password".into()), + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_udp_mode(udp_mode) + .with_session_password("wrong-password") + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |_channel, _remote| async move { panic!("Client should not have connected") }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); @@ -538,7 +461,7 @@ mod tests { #[rstest] #[case(UdpMode::Disabled)] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_single_connection_passwordless_deregister(#[case] udp_mode: UdpMode) { citadel_logging::setup_log(); TestBarrier::setup(2); @@ -555,12 +478,14 @@ mod tests { let uuid = Uuid::new_v4(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - udp_mode, - Default::default(), - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_udp_mode(udp_mode) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |channel, remote| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); wait_for_peers().await; @@ -570,8 +495,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); @@ -585,7 +509,7 @@ mod tests { #[rstest] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_backend_store_c2s() { citadel_logging::setup_log(); TestBarrier::setup(2); @@ -603,12 +527,14 @@ mod tests { let uuid = Uuid::new_v4(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - udp_mode, - Default::default(), - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_udp_mode(udp_mode) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |channel, remote| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); wait_for_peers().await; @@ -647,8 +573,7 @@ mod tests { wait_for_peers().await; remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); @@ -662,7 +587,7 @@ mod tests { #[rstest] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_rekey_c2s() { citadel_logging::setup_log(); TestBarrier::setup(2); @@ -680,12 +605,14 @@ mod tests { let uuid = Uuid::new_v4(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - udp_mode, - Default::default(), - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_udp_mode(udp_mode) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |_channel, remote| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); wait_for_peers().await; @@ -699,8 +626,7 @@ mod tests { remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); diff --git a/citadel_sdk/src/prefabs/mod.rs b/citadel_sdk/src/prefabs/mod.rs index 575aeed67..39cfe120a 100644 --- a/citadel_sdk/src/prefabs/mod.rs +++ b/citadel_sdk/src/prefabs/mod.rs @@ -1,10 +1,10 @@ use crate::impl_remote; use crate::prefabs::client::peer_connection::FileTransferHandleRx; +use citadel_io::tokio::sync::mpsc::UnboundedReceiver; use citadel_io::Mutex; use citadel_proto::prelude::*; use std::net::{SocketAddr, ToSocketAddrs}; use std::sync::Arc; -use tokio::sync::mpsc::UnboundedReceiver; /// Kernels for clients pub mod client; @@ -47,7 +47,9 @@ impl ClientServerRemote { } } /// Can only be called once per remote. Allows receiving events - pub fn get_unprocessed_signals_receiver(&self) -> Option> { + pub fn get_unprocessed_signals_receiver( + &self, + ) -> Option> { self.unprocessed_signals_rx.lock().take() } diff --git a/citadel_sdk/src/prefabs/server/internal_service.rs b/citadel_sdk/src/prefabs/server/internal_service.rs index 3c4c8d9bb..7f3064dbb 100644 --- a/citadel_sdk/src/prefabs/server/internal_service.rs +++ b/citadel_sdk/src/prefabs/server/internal_service.rs @@ -54,10 +54,12 @@ impl<'a, F, Fut> NetKernel for InternalServiceKernel<'a, F, Fut> { #[cfg(test)] mod test { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; + use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prefabs::server::internal_service::InternalServiceKernel; use crate::prefabs::shared::internal_service::InternalServerCommunicator; use crate::prelude::*; use crate::test_common::TestBarrier; + use citadel_io::tokio; use citadel_logging::setup_log; use hyper::client::conn::Builder; use hyper::server::conn::Http; @@ -108,12 +110,14 @@ mod test { #[rstest] #[timeout(Duration::from_secs(60))] - #[tokio::test] + #[citadel_io::tokio::test] async fn test_internal_service_basic_bytes() { setup_log(); let barrier = &TestBarrier::new(2); let success_count = &AtomicUsize::new(0); - let message = &(0..4096).map(|r| (r % 256) as u8).collect::>(); + let message = &(0..4096usize) + .map(|r| (r % u8::MAX as usize) as u8) + .collect::>(); let server_listener = citadel_wire::socket_helpers::get_tcp_listener("0.0.0.0:0") .expect("Failed to get TCP listener"); let server_bind_addr = server_listener.local_addr().unwrap(); @@ -128,9 +132,13 @@ mod test { .await }); - let client_kernel = SingleClientServerConnectionKernel::new_passwordless_defaults( - Uuid::new_v4(), - server_bind_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_bind_addr, Uuid::new_v4()) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |connect_success, remote| async move { crate::prefabs::shared::internal_service::internal_service( remote, @@ -151,7 +159,7 @@ mod test { let client = NodeBuilder::default() .with_node_type(NodeType::Peer) - .build(client_kernel.unwrap()) + .build(client_kernel) .unwrap(); let server = NodeBuilder::default() @@ -162,7 +170,7 @@ mod test { .build(server_kernel) .unwrap(); - let res = tokio::select! { + let res = citadel_io::tokio::select! { res0 = server => { citadel_logging::info!(target: "citadel", "Server exited"); res0.map(|_|()) @@ -181,7 +189,7 @@ mod test { #[rstest] #[timeout(Duration::from_secs(60))] - #[tokio::test] + #[citadel_io::tokio::test] async fn test_internal_service_http() { setup_log(); let barrier = &TestBarrier::new(2); @@ -205,9 +213,13 @@ mod test { Ok(()) }); - let client_kernel = SingleClientServerConnectionKernel::new_passwordless_defaults( - Uuid::new_v4(), - server_bind_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_bind_addr, Uuid::new_v4()) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |connect_success, remote| async move { crate::prefabs::shared::internal_service::internal_service( remote, @@ -215,14 +227,14 @@ mod test { |internal_server_communicator| async move { barrier.wait().await; // wait for the server - tokio::time::sleep(Duration::from_millis(500)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(500)).await; let (mut request_sender, connection) = Builder::new() .handshake(internal_server_communicator) .await .map_err(from_hyper_error)?; // spawn a task to poll the connection and drive the HTTP state - drop(tokio::spawn(async move { + drop(citadel_io::tokio::spawn(async move { if let Err(e) = connection.await { citadel_logging::error!(target: "citadel", "Error in connection: {e}"); std::process::exit(-1); @@ -230,7 +242,7 @@ mod test { })); // give time for task to spawn - tokio::time::sleep(Duration::from_millis(100)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(100)).await; let request = Request::builder() // We need to manually add the host header because SendRequest does not .header("Host", "example.com") @@ -256,7 +268,7 @@ mod test { let client = NodeBuilder::default() .with_node_type(NodeType::Peer) - .build(client_kernel.unwrap()) + .build(client_kernel) .unwrap(); let server = NodeBuilder::default() @@ -267,7 +279,7 @@ mod test { .build(server_kernel) .unwrap(); - let res = tokio::select! { + let res = citadel_io::tokio::select! { res0 = server => { citadel_logging::info!(target: "citadel", "Server exited"); res0.map(|_|()) diff --git a/citadel_sdk/src/prefabs/shared/internal_service.rs b/citadel_sdk/src/prefabs/shared/internal_service.rs index 4b2389ea2..aa8b3d13a 100644 --- a/citadel_sdk/src/prefabs/shared/internal_service.rs +++ b/citadel_sdk/src/prefabs/shared/internal_service.rs @@ -1,5 +1,6 @@ use crate::prelude::{ConnectionSuccess, TargetLockedRemote}; use bytes::Bytes; +use citadel_io::tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use citadel_proto::prelude::NetworkError; use citadel_proto::re_imports::{StreamReader, UnboundedReceiverStream}; use citadel_types::crypto::SecBuffer; @@ -7,7 +8,6 @@ use futures::StreamExt; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; pub async fn internal_service( remote: R, @@ -19,8 +19,8 @@ where Fut: Send + Sync + Future>, R: TargetLockedRemote, { - let (tx_to_service, rx_from_kernel) = tokio::sync::mpsc::unbounded_channel(); - let (tx_to_kernel, mut rx_from_service) = tokio::sync::mpsc::unbounded_channel(); + let (tx_to_service, rx_from_kernel) = citadel_io::tokio::sync::mpsc::unbounded_channel(); + let (tx_to_kernel, mut rx_from_service) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let internal_server_communicator = InternalServerCommunicator { tx_to_kernel, @@ -51,7 +51,7 @@ where Ok(()) }; - let res = tokio::select! { + let res = citadel_io::tokio::select! { res0 = from_proto => { res0 }, @@ -70,7 +70,7 @@ where } pub struct InternalServerCommunicator { - pub(crate) tx_to_kernel: tokio::sync::mpsc::UnboundedSender, + pub(crate) tx_to_kernel: citadel_io::tokio::sync::mpsc::UnboundedSender, pub(crate) rx_from_kernel: StreamReader>, Bytes>, } diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index 678646e61..210c86c4c 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -112,7 +112,7 @@ pub struct ConnectionSuccess { /// An interface to send ordered, reliable, and encrypted messages pub channel: PeerChannel, /// Only available if UdpMode was enabled at the beginning of a session - pub udp_channel_rx: Option>, + pub udp_channel_rx: Option>, /// Contains the Google auth minted at the central server (if the central server enabled it), as well as any other services enabled by the central server pub services: ServicesObject, pub cid: u64, @@ -288,7 +288,10 @@ pub trait ProtocolRemoteExt: Remote { /// ``` /// use citadel_sdk::prelude::*; /// # use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel; - /// # SingleClientServerConnectionKernel::new_connect_defaults("", "", |_, mut remote| async move { + /// + /// let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_login("127.0.0.1:25021", "john.doe", "password").build().unwrap(); + /// + /// # SingleClientServerConnectionKernel::new(server_connection_settings, |_, mut remote| async move { /// remote.find_target("my_account", "my_peer").await?.send_file("/path/to/file.pdf").await /// // or: remote.find_target(1234, "my_peer").await? [...] /// # }); @@ -1185,8 +1188,8 @@ pub mod results { use crate::prefabs::client::peer_connection::FileTransferHandleRx; use crate::prelude::{PeerChannel, UdpChannel}; use crate::remote_ext::remote_specialization::PeerRemote; + use citadel_io::tokio::sync::oneshot::Receiver; use citadel_proto::prelude::NetworkError; - use tokio::sync::oneshot::Receiver; #[derive(Debug)] pub struct PeerConnectSuccess { @@ -1266,7 +1269,9 @@ pub mod remote_specialization { #[cfg(test)] mod tests { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; + use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prelude::*; + use citadel_io::tokio; use rstest::rstest; use std::net::SocketAddr; use std::sync::atomic::{AtomicBool, Ordering}; @@ -1304,8 +1309,9 @@ mod tests { ObjectTransferStatus::ReceptionComplete => { log::trace!(target: "citadel", "Server has finished receiving the file!"); let cmp = include_bytes!("../../resources/TheBridge.pdf"); - let streamed_data = - tokio::fs::read(path.clone().unwrap()).await.unwrap(); + let streamed_data = citadel_io::tokio::fs::read(path.clone().unwrap()) + .await + .unwrap(); assert_eq!( cmp, streamed_data.as_slice(), @@ -1351,7 +1357,7 @@ mod tests { KemAlgorithm::Kyber, SigAlgorithm::Falcon1024 )] - #[timeout(std::time::Duration::from_secs(90))] + #[citadel_io::timeout(std::time::Duration::from_secs(90))] #[tokio::test] async fn test_c2s_file_transfer( #[case] enx: EncryptionAlgorithm, @@ -1369,12 +1375,15 @@ mod tests { .build() .unwrap(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - UdpMode::Disabled, - session_security_settings, - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_session_security_settings(session_security_settings) + .disable_udp() + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, |_channel, remote| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); remote @@ -1389,8 +1398,7 @@ mod tests { client_success.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); diff --git a/citadel_sdk/src/test_common.rs b/citadel_sdk/src/test_common.rs index ed1fe306b..0fe6c5926 100644 --- a/citadel_sdk/src/test_common.rs +++ b/citadel_sdk/src/test_common.rs @@ -5,11 +5,11 @@ use crate::prefabs::server::empty::EmptyKernel; use crate::prefabs::ClientServerRemote; use crate::prelude::results::PeerConnectSuccess; use crate::prelude::*; +use citadel_io::tokio::net::TcpListener; use futures::Future; use std::net::SocketAddr; use std::str::FromStr; use std::time::Duration; -use tokio::net::TcpListener; #[allow(dead_code)] pub fn server_test_node<'a, K: NetKernel + 'a>( @@ -109,7 +109,7 @@ pub static TEST_BARRIER: citadel_io::Mutex> = citadel_io::co #[allow(dead_code)] pub struct TestBarrier { #[allow(dead_code)] - pub inner: std::sync::Arc, + pub inner: std::sync::Arc, count: usize, } @@ -121,7 +121,7 @@ impl TestBarrier { #[allow(dead_code)] pub(crate) fn new(count: usize) -> Self { Self { - inner: std::sync::Arc::new(tokio::sync::Barrier::new(count)), + inner: std::sync::Arc::new(citadel_io::tokio::sync::Barrier::new(count)), count, } } @@ -182,7 +182,7 @@ lazy_static::lazy_static! { #[allow(dead_code)] pub async fn udp_mode_assertions( udp_mode: UdpMode, - udp_channel_rx_opt: Option>, + udp_channel_rx_opt: Option>, ) { use futures::StreamExt; citadel_logging::info!(target: "citadel", "Inside UDP mode assertions ..."); @@ -200,7 +200,7 @@ pub async fn udp_mode_assertions( tx.unbounded_send(b"Hello, world!" as &[u8]).unwrap(); assert_eq!(rx.next().await.unwrap().as_ref(), b"Hello, world!"); // wait to give time for the other side to receive the message - tokio::time::sleep(Duration::from_millis(500)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(500)).await; //wait_for_peers().await; std::mem::forget((tx, rx)); // do not run destructor to not trigger premature } diff --git a/citadel_sdk/tests/stress_tests.rs b/citadel_sdk/tests/stress_tests.rs index bed22d7f0..94deb789a 100644 --- a/citadel_sdk/tests/stress_tests.rs +++ b/citadel_sdk/tests/stress_tests.rs @@ -1,8 +1,12 @@ #[cfg(test)] mod tests { + use citadel_io::tokio; + use citadel_io::tokio::sync::Barrier; + use citadel_io::tokio::task::JoinError; use citadel_sdk::prefabs::client::broadcast::{BroadcastKernel, GroupInitRequestType}; use citadel_sdk::prefabs::client::peer_connection::PeerConnectionKernel; use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel; + use citadel_sdk::prefabs::client::ServerConnectionSettingsBuilder; use citadel_sdk::prelude::*; use citadel_sdk::test_common::{server_info, wait_for_peers}; use citadel_types::crypto::{EncryptionAlgorithm, KemAlgorithm}; @@ -18,14 +22,12 @@ mod tests { use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; - use tokio::sync::Barrier; - use tokio::task::JoinError; use uuid::Uuid; struct TestSpawner { // this may not be a real localset #[cfg(not(feature = "multi-threaded"))] - local_set: tokio::task::LocalSet, + local_set: citadel_io::tokio::task::LocalSet, #[cfg_attr(feature = "multi-threaded", allow(dead_code))] #[cfg(feature = "multi-threaded")] local_set: (), @@ -39,7 +41,10 @@ mod tests { } #[cfg(not(feature = "multi-threaded"))] - pub fn spawn(&self, future: T) -> tokio::task::JoinHandle<::Output> + pub fn spawn( + &self, + future: T, + ) -> citadel_io::tokio::task::JoinHandle<::Output> where T: Future + 'static, T::Output: 'static, @@ -48,12 +53,15 @@ mod tests { } #[cfg(feature = "multi-threaded")] - pub fn spawn(&self, future: T) -> tokio::task::JoinHandle<::Output> + pub fn spawn( + &self, + future: T, + ) -> citadel_io::tokio::task::JoinHandle<::Output> where T: Future + Send + 'static, T::Output: Send + 'static, { - tokio::task::spawn(future) + citadel_io::tokio::task::spawn(future) } #[cfg(not(feature = "multi-threaded"))] @@ -195,11 +203,11 @@ mod tests { #[case(500, SecrecyMode::Perfect)] #[case(500, SecrecyMode::BestEffort)] #[timeout(std::time::Duration::from_secs(240))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn stress_test_c2s_messaging( #[case] message_count: usize, #[case] secrecy_mode: SecrecyMode, - #[values(KemAlgorithm::Kyber, KemAlgorithm::Ntru)] kem: KemAlgorithm, + #[values(KemAlgorithm::Kyber)] kem: KemAlgorithm, #[values( EncryptionAlgorithm::AES_GCM_256, EncryptionAlgorithm::ChaCha20Poly_1305, @@ -239,12 +247,15 @@ mod tests { .build() .unwrap(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - UdpMode::Enabled, - session_security, - None, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_udp_mode(UdpMode::Enabled) + .with_session_security_settings(session_security) + .build() + .unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, move |connection, remote| async move { log::trace!(target: "citadel", "*** CLIENT RECV CHANNEL ***"); handle_send_receive_e2e(get_barrier(), connection.channel, message_count).await?; @@ -252,8 +263,7 @@ mod tests { CLIENT_SUCCESS.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = spawner.spawn(NodeBuilder::default().build(client_kernel).unwrap()); let server = spawner.spawn(server); @@ -271,7 +281,7 @@ mod tests { #[case(100, SecrecyMode::Perfect, None)] #[case(100, SecrecyMode::BestEffort, Some("test-password"))] #[timeout(std::time::Duration::from_secs(240))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn stress_test_c2s_messaging_kyber( #[case] message_count: usize, #[case] secrecy_mode: SecrecyMode, @@ -315,12 +325,19 @@ mod tests { .build() .unwrap(); - let client_kernel = SingleClientServerConnectionKernel::new_authless( - uuid, - server_addr, - UdpMode::Enabled, - session_security, - server_password.map(|p| p.into()), + let mut connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .with_udp_mode(UdpMode::Enabled) + .with_session_security_settings(session_security); + + if let Some(password) = server_password { + connection_settings = connection_settings.with_session_password(password); + } + + let connection_settings = connection_settings.build().unwrap(); + + let client_kernel = SingleClientServerConnectionKernel::new( + connection_settings, move |connection, remote| async move { log::trace!(target: "citadel", "*** CLIENT RECV CHANNEL ***"); handle_send_receive_e2e(get_barrier(), connection.channel, message_count).await?; @@ -328,8 +345,7 @@ mod tests { CLIENT_SUCCESS.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client = spawner.spawn(NodeBuilder::default().build(client_kernel).unwrap()); let server = spawner.spawn(server); @@ -347,7 +363,7 @@ mod tests { #[case(500, SecrecyMode::Perfect, None)] #[case(500, SecrecyMode::BestEffort, Some("test-p2p-password"))] #[timeout(std::time::Duration::from_secs(240))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn stress_test_p2p_messaging( #[case] message_count: usize, #[case] secrecy_mode: SecrecyMode, @@ -400,13 +416,16 @@ mod tests { let peer1_connection = peer1_agg.add(); - let client_kernel0 = PeerConnectionKernel::new_authless( - uuid0, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid0) + .with_udp_mode(UdpMode::Enabled) + .with_session_security_settings(session_security) + .build() + .unwrap(); + + let client_kernel0 = PeerConnectionKernel::new( + server_connection_settings, peer0_connection, - UdpMode::Enabled, - session_security, - None, move |mut connection, remote| async move { handle_send_receive_e2e( get_barrier(), @@ -418,16 +437,18 @@ mod tests { client0_success.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); - let client_kernel1 = PeerConnectionKernel::new_authless( - uuid1, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid1) + .with_udp_mode(UdpMode::Enabled) + .with_session_security_settings(session_security) + .build() + .unwrap(); + + let client_kernel1 = PeerConnectionKernel::new( + server_connection_settings, peer1_connection, - UdpMode::Enabled, - session_security, - None, move |mut connection, remote| async move { handle_send_receive_e2e( get_barrier(), @@ -439,21 +460,20 @@ mod tests { client1_success.store(true, Ordering::Relaxed); remote.shutdown_kernel().await }, - ) - .unwrap(); + ); let client0 = NodeBuilder::default().build(client_kernel0).unwrap(); let client1 = NodeBuilder::default().build(client_kernel1).unwrap(); let clients = futures::future::try_join(client0, client1); let task = async move { - tokio::select! { + citadel_io::tokio::select! { server_res = server => Err(NetworkError::msg(format!("Server ended prematurely: {:?}", server_res.map(|_| ())))), client_res = clients => client_res.map(|_| ()) } }; - let _ = tokio::time::timeout(Duration::from_secs(120), task) + let _ = citadel_io::tokio::time::timeout(Duration::from_secs(120), task) .await .unwrap(); @@ -464,7 +484,7 @@ mod tests { #[rstest] #[case(500, 3)] #[timeout(std::time::Duration::from_secs(90))] - #[tokio::test(flavor = "multi_thread")] + #[citadel_io::tokio::test(flavor = "multi_thread")] async fn stress_test_group_broadcast(#[case] message_count: usize, #[case] peer_count: usize) { citadel_logging::setup_log(); citadel_sdk::test_common::TestBarrier::setup(peer_count); @@ -500,9 +520,13 @@ mod tests { } }; - let client_kernel = BroadcastKernel::new_authless_defaults( - uuid, - server_addr, + let server_connection_settings = + ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + .build() + .unwrap(); + + let client_kernel = BroadcastKernel::new( + server_connection_settings, request, move |channel, remote| async move { log::trace!(target: "citadel", "***GROUP PEER {}={} CONNECT SUCCESS***", idx,uuid); @@ -514,7 +538,7 @@ mod tests { let _ = CLIENT_SUCCESS.fetch_add(1, Ordering::Relaxed); remote.shutdown_kernel().await }, - ).unwrap(); + ); let client = NodeBuilder::default().build(client_kernel).unwrap(); let task = async move { client.await.map(|_| ()) }; diff --git a/citadel_types/src/crypto/mod.rs b/citadel_types/src/crypto/mod.rs index d76410a22..3ecd7a410 100644 --- a/citadel_types/src/crypto/mod.rs +++ b/citadel_types/src/crypto/mod.rs @@ -348,7 +348,6 @@ pub enum KemAlgorithm { #[strum(ascii_case_insensitive)] #[default] Kyber = 0, - Ntru = 1, } #[derive( diff --git a/citadel_types/src/errors/mod.rs b/citadel_types/src/errors/mod.rs index 3c652ae9d..276898717 100644 --- a/citadel_types/src/errors/mod.rs +++ b/citadel_types/src/errors/mod.rs @@ -15,6 +15,8 @@ pub enum Error { Other(String), /// Bad length InvalidLength, + /// Unsupported algorithm + UnsupportedAlgorithm, } impl Display for Error { diff --git a/citadel_types/src/proto/mod.rs b/citadel_types/src/proto/mod.rs index a9056e5b9..a8d3122f3 100644 --- a/citadel_types/src/proto/mod.rs +++ b/citadel_types/src/proto/mod.rs @@ -162,18 +162,13 @@ pub struct SessionSecuritySettings { pub crypto_params: CryptoParameters, } -#[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Default)] pub enum UdpMode { Enabled, + #[default] Disabled, } -impl Default for UdpMode { - fn default() -> Self { - Self::Enabled - } -} - #[derive(Clone, Debug, Serialize, Deserialize)] pub enum MemberState { EnteredGroup { cids: Vec }, diff --git a/citadel_user/Cargo.toml b/citadel_user/Cargo.toml index 252a54f24..abc7a156c 100644 --- a/citadel_user/Cargo.toml +++ b/citadel_user/Cargo.toml @@ -17,15 +17,14 @@ license = "MIT OR Apache-2.0" default = ["filesystem", "std"] redis = ["redis-base", "mobc"] sql = ["sqlx", "itertools"] -filesystem = ["citadel_crypt/filesystem", "tokio-util", "tokio-stream"] +filesystem = ["citadel_crypt/filesystem"] std = [ "citadel_crypt/std", - "tokio/fs", "rand/std", "sha3/std", "bstr/std" ] -wasm = ["citadel_crypt/wasm"] +wasm = ["citadel_crypt/wasm", "citadel_io/wasm"] google-services = ["openssl", "jwt", "firebase-rtdb"] vendored = ["openssl/vendored"] @@ -33,9 +32,9 @@ localhost-testing = [] jwt-testing = [] [dependencies] +citadel_io = { workspace = true } itertools = { workspace = true, features = ["use_alloc"], optional = true } serde = { workspace = true, features=["rc", "derive"] } -tokio = { workspace = true, features = ["io-util"] } async-trait = { workspace = true } futures = { workspace = true, features = ["alloc", "executor"] } rand = { workspace = true } @@ -56,12 +55,9 @@ openssl = { workspace = true, optional = true } uuid = { workspace = true, features = ["v4"] } bincode = { workspace = true } chrono = { workspace = true, features = ["clock"] } -tokio-util = { workspace = true, features = ["io"], optional = true } -tokio-stream = { workspace = true, optional = true } citadel_types = { workspace = true } [dev-dependencies] -tokio = { workspace = true, features = ["macros"] } citadel_logging = { workspace = true } citadel_io = { workspace = true } dirs2 = { workspace = true } @@ -73,4 +69,4 @@ citadel_pqcrypto = { workspace = true } doctest = false [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] } \ No newline at end of file diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs index 70d075061..92325d46b 100644 --- a/citadel_user/src/account_manager.rs +++ b/citadel_user/src/account_manager.rs @@ -57,7 +57,7 @@ impl AccountManager { PersistenceHandler::create(backend).await? } - #[cfg(all(feature = "filesystem", not(target_family = "wasm")))] + #[cfg(feature = "filesystem")] BackendType::Filesystem(dir) => { use crate::backend::filesystem_backend::FilesystemBackend; let backend = FilesystemBackend::from(dir.clone()); diff --git a/citadel_user/src/auth/proposed_credentials.rs b/citadel_user/src/auth/proposed_credentials.rs index a406b7ee6..8569930fd 100644 --- a/citadel_user/src/auth/proposed_credentials.rs +++ b/citadel_user/src/auth/proposed_credentials.rs @@ -97,7 +97,7 @@ impl ProposedCredentials { settings.clone(), ) .await - .map_err(|err| AccountError::Generic(err.message))? + .map_err(|err| AccountError::Generic(err.to_string()))? { ArgonStatus::HashSuccess(ret) => Ok(ret), other => Err(AccountError::Generic(format!( @@ -218,7 +218,7 @@ impl ProposedCredentials { match AsyncArgon::hash(password_hashed, settings.clone()) .await - .map_err(|err| AccountError::Generic(err.message))? + .map_err(|err| AccountError::Generic(err.to_string()))? { ArgonStatus::HashSuccess(hash_x2) => Ok(DeclaredAuthenticationMode::Argon { username, @@ -249,7 +249,7 @@ impl ProposedCredentials { ArgonContainerType::Server(server_container) => { match AsyncArgon::verify(password_hashed, server_container) .await - .map_err(|err| AccountError::Generic(err.message))? + .map_err(|err| AccountError::Generic(err.to_string()))? { ArgonStatus::VerificationSuccess => Ok(()), diff --git a/citadel_user/src/backend/filesystem_backend.rs b/citadel_user/src/backend/filesystem_backend.rs index b58654c8a..6c3346b78 100644 --- a/citadel_user/src/backend/filesystem_backend.rs +++ b/citadel_user/src/backend/filesystem_backend.rs @@ -10,13 +10,15 @@ use async_trait::async_trait; use citadel_crypt::scramble::crypt_splitter::MAX_BYTES_PER_GROUP; use citadel_crypt::stacked_ratchet::Ratchet; use citadel_crypt::streaming_crypt_scrambler::ObjectSource; +use citadel_io::tokio; +use citadel_io::tokio_stream::StreamExt; +use citadel_types::crypto::SecurityLevel; use citadel_types::proto::{ObjectTransferStatus, TransferType, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; use std::collections::HashMap; use std::path::{Path, PathBuf}; use tokio::io::AsyncWriteExt; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; -use tokio_stream::StreamExt; /// For handling I/O with the local filesystem pub struct FilesystemBackend { @@ -371,8 +373,8 @@ impl BackendConnection for FilesystemBackend>, std::io::Error> diff --git a/citadel_user/src/backend/memory.rs b/citadel_user/src/backend/memory.rs index 825be1c30..6e469314d 100644 --- a/citadel_user/src/backend/memory.rs +++ b/citadel_user/src/backend/memory.rs @@ -3,6 +3,7 @@ use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; use async_trait::async_trait; use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_io::tokio; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; use parking_lot::RwLock; diff --git a/citadel_user/src/backend/mod.rs b/citadel_user/src/backend/mod.rs index ae8bb8649..6573bd27d 100644 --- a/citadel_user/src/backend/mod.rs +++ b/citadel_user/src/backend/mod.rs @@ -20,7 +20,7 @@ use citadel_types::user::MutualPeer; use tokio::sync::mpsc::UnboundedSender; /// Implementation for the default filesystem backend -#[cfg(all(feature = "filesystem", not(target_family = "wasm")))] +#[cfg(feature = "filesystem")] pub mod filesystem_backend; /// Implementation for an in-memory backend. No synchronization occurs. /// This is useful for no-fs environments @@ -44,7 +44,7 @@ pub enum BackendType { /// access InMemory, /// Synchronization will occur on the filesystem - #[cfg(all(feature = "filesystem", not(target_family = "wasm")))] + #[cfg(feature = "filesystem")] Filesystem(String), #[cfg(all(feature = "sql", not(coverage)))] /// Synchronization will occur on a remote SQL database diff --git a/citadel_user/src/backend/redis_backend.rs b/citadel_user/src/backend/redis_backend.rs index 2b5787fe1..0902ff981 100644 --- a/citadel_user/src/backend/redis_backend.rs +++ b/citadel_user/src/backend/redis_backend.rs @@ -5,6 +5,7 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX}; use crate::serialization::SyncIO; use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; use mobc::async_trait; @@ -14,7 +15,6 @@ use redis_base::{AsyncCommands, Client, ErrorKind, FromRedisValue, ToRedisArgs}; use std::collections::HashMap; use std::marker::PhantomData; use std::time::Duration; -use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; /// Backend struct for redis pub(crate) struct RedisBackend { diff --git a/citadel_user/src/backend/sql_backend.rs b/citadel_user/src/backend/sql_backend.rs index 2f73c0714..083c051e5 100644 --- a/citadel_user/src/backend/sql_backend.rs +++ b/citadel_user/src/backend/sql_backend.rs @@ -7,6 +7,7 @@ use crate::serialization::SyncIO; use async_trait::async_trait; use citadel_crypt::fcm::fcm_ratchet::ThinRatchet; use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; +use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; use itertools::Itertools; @@ -19,7 +20,6 @@ use std::marker::PhantomData; use std::ops::DerefMut; use std::str::FromStr; use std::time::Duration; -use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; /// A container for handling db conns pub struct SqlBackend { diff --git a/citadel_user/src/backend/utils/mod.rs b/citadel_user/src/backend/utils/mod.rs index 91e9dbce7..2574507ec 100644 --- a/citadel_user/src/backend/utils/mod.rs +++ b/citadel_user/src/backend/utils/mod.rs @@ -4,6 +4,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use crate::misc::AccountError; +use citadel_io::tokio; use citadel_types::proto::{ ObjectTransferOrientation, ObjectTransferStatus, VirtualObjectMetadata, }; diff --git a/citadel_user/src/lib.rs b/citadel_user/src/lib.rs index e647121c9..5ff9dad98 100644 --- a/citadel_user/src/lib.rs +++ b/citadel_user/src/lib.rs @@ -48,7 +48,7 @@ pub mod network_account; /// evoc_null(web 3.0) => void && let void alloc finite && set network evoc_null(!HyperWAN) pub mod client_account; -#[cfg(all(feature = "filesystem", not(target_family = "wasm")))] +#[cfg(feature = "filesystem")] /// This provides methods to load all locally-stored files pub mod account_loader; /// The server in legacy_citadel_proto requires a means of handling the user database. This module contains the means of achieving this @@ -58,7 +58,7 @@ pub mod auth; /// For handling different I/O operations pub mod backend; pub mod credentials; -#[cfg(all(feature = "filesystem", not(target_family = "wasm")))] +#[cfg(feature = "filesystem")] /// Environmental constants and subroutines for pre-checking the system pub mod directory_store; /// For services diff --git a/citadel_user/tests/primary.rs b/citadel_user/tests/primary.rs index fdf4a1c97..125ec6ec5 100644 --- a/citadel_user/tests/primary.rs +++ b/citadel_user/tests/primary.rs @@ -14,6 +14,7 @@ mod tests { use futures::Future; use std::str::FromStr; + use citadel_io::tokio; use citadel_types::crypto::EncryptionAlgorithm; use citadel_types::crypto::SecBuffer; use citadel_types::user::MutualPeer; @@ -151,6 +152,7 @@ mod tests { } } + #[cfg(any(feature = "sql", feature = "redis", feature = "filesystem"))] fn generate_random_filesystem_dir() -> BackendType { let mut home = dirs2::home_dir().unwrap(); let rand = uuid::Uuid::new_v4().to_string(); diff --git a/citadel_wire/Cargo.toml b/citadel_wire/Cargo.toml index 7bb4c4b21..834d0383f 100644 --- a/citadel_wire/Cargo.toml +++ b/citadel_wire/Cargo.toml @@ -20,7 +20,8 @@ std = [ "citadel_io/std", "openssl/default", "quinn/default", - "serde/std" + "serde/std", + "async_ip/std", ] localhost-testing = ["tracing"] wasm = [ @@ -30,7 +31,6 @@ wasm = [ vendored = ["openssl/vendored"] [dependencies] -tokio = { workspace = true, features = ["net"] } futures = { workspace = true } async-trait-with-sync = { workspace = true } bytes = { workspace = true } @@ -44,25 +44,26 @@ itertools = { workspace = true, features = ["use_alloc", "use_std"] } either = { workspace = true } netbeam = { workspace = true } uuid = { workspace = true, features = ["v4", "serde"] } -tracing = { workspace = true, optional = true } +tracing = { workspace = true, optional = true, features = ["attributes"] } lazy_static = { workspace = true } +rustls = { workspace = true } +socket2 = { workspace = true, features = ["all"] } [target.'cfg(not(target_family = "wasm"))'.dependencies] -socket2 = { workspace = true, features = ["all"] } -rustls-native-certs = { workspace = true } -igd = { workspace = true, features = ["aio"] } quinn = { workspace = true, features = ["rustls"] } openssl = { workspace = true } +rustls-native-certs = { workspace = true } +igd = { workspace = true, features = ["aio"] } stun = { workspace = true } -rcgen = { workspace = true, features = ["pem"] } -rustls = { workspace = true, features = ["quic", "dangerous_configuration"] } +rcgen = { workspace = true, features = ["pem", "crypto", "ring"] } rustls-pemfile = { workspace = true } tokio-rustls = { workspace = true } +[target.'cfg(target_family = "wasm")'.dependencies] + [dev-dependencies] citadel_logging = { workspace = true } rand = "0.8.5" -tokio-util = "0.7.4" rstest = { workspace = true } [lib] diff --git a/citadel_wire/src/error.rs b/citadel_wire/src/error.rs index 30487c570..4e68eb9b1 100644 --- a/citadel_wire/src/error.rs +++ b/citadel_wire/src/error.rs @@ -1,5 +1,6 @@ use std::fmt::Formatter; -use tokio::io::Error; +use citadel_io::tokio::io::Error; +use std::fmt::Formatter; #[derive(Debug)] pub enum FirewallError { diff --git a/citadel_wire/src/lib.rs b/citadel_wire/src/lib.rs index 20b8296b2..60d95247d 100644 --- a/citadel_wire/src/lib.rs +++ b/citadel_wire/src/lib.rs @@ -4,24 +4,16 @@ pub mod exports { pub use openssl; pub use quinn::{Accept, Connecting, Connection, Endpoint, RecvStream, SendStream}; + pub use rustls::pki_types::{CertificateDer as Certificate, PrivateKeyDer as PrivateKey}; pub use rustls::ClientConfig; - pub use rustls::{Certificate, PrivateKey}; pub use rustls_pemfile; pub use tokio_rustls; } pub mod error; -#[cfg(not(target_family = "wasm"))] pub mod udp_traversal; pub mod hypernode_type; - -#[cfg(not(target_family = "wasm"))] pub(crate) mod standard; -#[cfg(target_family = "wasm")] -pub(crate) mod wasm; -#[cfg(not(target_family = "wasm"))] pub use standard::*; -#[cfg(target_family = "wasm")] -pub use wasm::*; diff --git a/citadel_wire/src/standard/misc.rs b/citadel_wire/src/standard/misc.rs index 570e0553a..c73b11244 100644 --- a/citadel_wire/src/standard/misc.rs +++ b/citadel_wire/src/standard/misc.rs @@ -1,14 +1,14 @@ +use crate::exports::{Certificate, PrivateKey}; use openssl::pkcs12::ParsedPkcs12_2 as ParsedPkcs12; use openssl::pkey::{PKey, Private}; use openssl::stack::Stack; use openssl::x509::X509; -use rustls::{Certificate, PrivateKey}; use std::path::Path; pub fn read_pkcs_12_der_to_quinn_keys>( path: P, password: &str, -) -> Result<(Vec, PrivateKey), anyhow::Error> { +) -> Result<(Vec>, PrivateKey<'static>), anyhow::Error> { let der = std::fs::read(path)?; pkcs12_to_quinn_keys(&der, password) } @@ -31,7 +31,7 @@ pub fn pkcs12_to_components( pub fn pkcs12_to_quinn_keys( pkcs12_der: &[u8], password: &str, -) -> Result<(Vec, PrivateKey), anyhow::Error> { +) -> Result<(Vec>, PrivateKey<'static>), anyhow::Error> { let (chain, cert, pkey) = pkcs12_to_components(pkcs12_der, password)?; pkcs_12_components_to_quic_keys(chain.as_ref(), &cert, &pkey) } @@ -40,7 +40,7 @@ pub fn pkcs_12_components_to_quic_keys( chain: Option<&Stack>, cert: &X509, pkey: &PKey, -) -> Result<(Vec, PrivateKey), anyhow::Error> { +) -> Result<(Vec>, PrivateKey<'static>), anyhow::Error> { let (cert, key) = cert_and_priv_key_der_to_quic_keys(&cert.to_der()?, &pkey.private_key_to_der()?)?; @@ -49,7 +49,7 @@ pub fn pkcs_12_components_to_quic_keys( if let Some(chain) = chain { for cert in chain { - certs.push(Certificate(cert.to_der()?)); + certs.push(Certificate::from(cert.to_der()?)); } } @@ -59,8 +59,12 @@ pub fn pkcs_12_components_to_quic_keys( pub fn cert_and_priv_key_der_to_quic_keys( cert_der: &[u8], priv_key_der: &[u8], -) -> Result<(Certificate, PrivateKey), anyhow::Error> { - let key = PrivateKey(priv_key_der.to_vec()); - let cert = Certificate(cert_der.to_vec()); +) -> Result<(Certificate<'static>, PrivateKey<'static>), anyhow::Error> { + let key = PrivateKey::try_from(priv_key_der.to_vec()).map_err(|err| { + anyhow::Error::msg(format!( + "Failed to convert private key to PrivateKey: {err}" + )) + })?; + let cert = Certificate::from(cert_der.to_vec()); Ok((cert, key)) } diff --git a/citadel_wire/src/standard/nat_identification.rs b/citadel_wire/src/standard/nat_identification.rs index 8760d1e92..1663be546 100644 --- a/citadel_wire/src/standard/nat_identification.rs +++ b/citadel_wire/src/standard/nat_identification.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use std::time::Duration; use crate::udp_traversal::hole_punch_config::AddrBand; -use citadel_io::UdpSocket; +use citadel_io::tokio::net::UdpSocket; use stun::client::ClientBuilder; use stun::message::{Getter, Message, BINDING_REQUEST}; use stun::xoraddr::XorMappedAddress; @@ -114,7 +114,7 @@ impl NatType { _timeout: Duration, stun_servers: Option>, ) -> Result { - match tokio::time::timeout(_timeout, get_nat_type(stun_servers)).await { + match citadel_io::tokio::time::timeout(_timeout, get_nat_type(stun_servers)).await { Ok(res) => res .map_err(|err| FirewallError::HolePunch(err.to_string())) .inspect(|nat_type| { @@ -489,7 +489,8 @@ async fn get_nat_type(stun_servers: Option>) -> Result>) -> Result(None), ) .await { @@ -572,7 +573,7 @@ async fn get_nat_type(stun_servers: Option>) -> Result>, + client_config: Option, ) -> Result { let endpoint = make_client_endpoint(socket, client_config)?; Ok(QuicNode { @@ -146,16 +147,24 @@ impl QuicClient { /// Creates a new client that verifies certificates pub fn new_with_config( socket: UdpSocket, - client_config: Arc, + client_config: ClientConfig, ) -> Result { Self::create(socket, Some(client_config)) } + + pub fn new_with_rustls_config( + socket: UdpSocket, + client_config: Arc, + ) -> Result { + let quinn_config = rustls_client_config_to_quinn_config(client_config)?; + Self::new_with_config(socket, quinn_config) + } } impl QuicServer { pub fn create( socket: UdpSocket, - crypt: Option<(Vec, PrivateKey)>, + crypt: Option<(Vec>, PrivateKey<'static>)>, ) -> Result { let endpoint = make_server_endpoint(socket, crypt)?; Ok(QuicNode { @@ -189,10 +198,10 @@ impl QuicServer { fn make_server_endpoint( socket: UdpSocket, - crypt: Option<(Vec, PrivateKey)>, + crypt: Option<(Vec>, PrivateKey<'static>)>, ) -> Result { let mut server_cfg = match crypt { - Some((certs, key)) => configure_server_with_crypto(certs, key)?, + Some((certs, key)) => secure::server_config(certs, key)?, None => configure_server_self_signed()?.0, }; @@ -210,12 +219,9 @@ fn make_server_endpoint( fn make_client_endpoint( socket: UdpSocket, - client_config: Option>, + client_config: Option, ) -> Result { - let mut client_cfg = match client_config { - Some(cfg) => quinn::ClientConfig::new(cfg), - None => insecure::configure_client(), - }; + let mut client_cfg = client_config.unwrap_or_else(insecure::configure_client); let socket = socket.into_std()?; // Quinn handles setting nonblocking to true load_hole_punch_friendly_quic_transport_config(Either::Right(&mut client_cfg)); @@ -257,42 +263,34 @@ pub const SELF_SIGNED_DOMAIN: &str = "localhost"; /// domain is always SELF_SIGNED_DOMAIN (localhost) pub fn generate_self_signed_cert() -> Result<(Vec, Vec), anyhow::Error> { let cert = rcgen::generate_simple_self_signed(vec![SELF_SIGNED_DOMAIN.into()])?; - let cert_der = cert.serialize_der()?; - let priv_key_der = cert.serialize_private_key_der(); + let cert_der = cert.cert.der().as_ref().to_vec(); + let priv_key_der = cert.key_pair.serialize_der(); Ok((cert_der, priv_key_der)) } -fn configure_server_self_signed() -> Result<(ServerConfig, Vec), anyhow::Error> { +fn configure_server_self_signed() -> Result<(quinn::ServerConfig, Vec), anyhow::Error> { let (cert_der, priv_key) = generate_self_signed_cert()?; - let priv_key = rustls::PrivateKey(priv_key); - let cert_chain = vec![rustls::Certificate(cert_der.clone())]; + let priv_key = crate::exports::PrivateKey::try_from(priv_key) + .map_err(|err| anyhow::Error::msg(format!("Failed to create private key: {err}")))?; + let cert_chain = vec![crate::exports::Certificate::from(cert_der.clone())]; - let server_config = - quinn::ServerConfig::with_crypto(Arc::new(secure::server_config(cert_chain, priv_key)?)); + let server_config = secure::server_config(cert_chain, priv_key)?; Ok((server_config, cert_der)) } -fn configure_server_with_crypto( - cert_chain: Vec, - private_key: rustls::PrivateKey, -) -> Result { - let server_crypto = secure::server_config(cert_chain, private_key)?; - let server_config = quinn::ServerConfig::with_crypto(Arc::new(server_crypto)); - Ok(server_config) -} - -pub fn rustls_client_config_to_quinn_config(cfg: Arc) -> ClientConfig { - ClientConfig::new(cfg) +pub fn rustls_client_config_to_quinn_config( + cfg: Arc, +) -> std::io::Result { + Ok(ClientConfig::new(Arc::new( + QuicClientConfig::try_from(cfg) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?, + ))) } pub mod secure { pub fn client_config(roots: rustls::RootCertStore) -> rustls::ClientConfig { let mut cfg = rustls::ClientConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_protocol_versions(&[&rustls::version::TLS13]) - .unwrap() .with_root_certificates(roots) .with_no_client_auth(); cfg.enable_early_data = true; @@ -306,17 +304,15 @@ pub mod secure { /// `u32::MAX`. Advanced users can use any [`rustls::ServerConfig`] that satisfies these /// requirements. pub fn server_config( - cert_chain: Vec, - key: rustls::PrivateKey, - ) -> Result { - let mut cfg = rustls::ServerConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_protocol_versions(&[&rustls::version::TLS13]) - .unwrap() - .with_no_client_auth() - .with_single_cert(cert_chain, key)?; - cfg.max_early_data_size = u32::MAX; + cert_chain: Vec>, + key: crate::exports::PrivateKey<'static>, + ) -> Result { + let mut cfg = quinn::ServerConfig::with_single_cert(cert_chain, key) + .map_err(|err| anyhow::Error::msg(err.to_string()))?; + + cfg.migration(true); + cfg.max_incoming(u32::MAX as usize); + Ok(cfg) } } @@ -324,42 +320,86 @@ pub mod secure { pub mod insecure { use std::sync::Arc; + use quinn::crypto::rustls::QuicClientConfig; use quinn::ClientConfig; + use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; + use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; + use rustls::{DigitallySignedStruct, Error, SignatureScheme}; - pub(crate) struct SkipServerVerification; - - impl SkipServerVerification { - pub(crate) fn new() -> Arc { - Arc::new(Self) - } - } + #[derive(Debug)] + struct NoServerCertVerification; - impl rustls::client::ServerCertVerifier for SkipServerVerification { + impl ServerCertVerifier for NoServerCertVerification { fn verify_server_cert( &self, - _end_entity: &rustls::Certificate, - _intermediates: &[rustls::Certificate], - _server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, _ocsp_response: &[u8], - _now: std::time::SystemTime, - ) -> Result { - Ok(rustls::client::ServerCertVerified::assertion()) + _now: UnixTime, + ) -> Result { + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + SignatureScheme::RSA_PKCS1_SHA1, + SignatureScheme::ECDSA_SHA1_Legacy, + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::ED25519, + SignatureScheme::ED448, + ] } } pub fn rustls_client_config() -> rustls::ClientConfig { - let mut cfg = rustls::ClientConfig::builder() - .with_safe_defaults() - .with_custom_certificate_verifier(SkipServerVerification::new()) + let versions = &rustls::version::TLS13; + let mut rustls_config = rustls::ClientConfig::builder_with_protocol_versions(&[versions]) + .dangerous() + .with_custom_certificate_verifier(Arc::new(NoServerCertVerification)) .with_no_client_auth(); - cfg.enable_sni = true; - cfg + // Add requirements for TryFrom to succeed + rustls_config.enable_sni = true; + rustls_config.enable_early_data = true; + + //QuicClientConfig::try_from(rustls_config).expect("Failed to create QuicClientConfig") + rustls_config + } + + pub fn get_quic_client_config() -> QuicClientConfig { + QuicClientConfig::try_from(rustls_client_config()) + .expect("Failed to create QuicClientConfig") } pub fn configure_client() -> ClientConfig { - ClientConfig::new(Arc::new(rustls_client_config())) + ClientConfig::new(Arc::new(get_quic_client_config())) } } @@ -369,6 +409,7 @@ mod tests { QuicClient, QuicEndpointConnector, QuicEndpointListener, QuicServer, SELF_SIGNED_DOMAIN, }; use crate::socket_helpers::is_ipv6_enabled; + use citadel_io::tokio; use rstest::*; use std::net::SocketAddr; @@ -385,11 +426,12 @@ mod tests { return Ok(()); } let mut server = - QuicServer::new_self_signed(citadel_io::UdpSocket::bind(addr).await?).unwrap(); + QuicServer::new_self_signed(citadel_io::tokio::net::UdpSocket::bind(addr).await?) + .unwrap(); let client_bind_addr = SocketAddr::from((addr.ip(), 0)); - let (start_tx, start_rx) = tokio::sync::oneshot::channel(); - let (end_tx, end_rx) = tokio::sync::oneshot::channel::<()>(); - let addr = server.endpoint.local_addr().unwrap(); + let (start_tx, start_rx) = citadel_io::tokio::sync::oneshot::channel(); + let (end_tx, end_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let addr = server.endpoint.local_addr()?; let server = async move { log::trace!(target: "citadel", "Starting server @ {:?}", addr); @@ -406,7 +448,9 @@ mod tests { let client = async move { start_rx.await.unwrap(); let client = QuicClient::new_no_verify( - citadel_io::UdpSocket::bind(client_bind_addr).await.unwrap(), + citadel_io::tokio::net::UdpSocket::bind(client_bind_addr) + .await + .unwrap(), ) .unwrap(); let res = client.connect_biconn(addr, SELF_SIGNED_DOMAIN).await; @@ -416,7 +460,7 @@ mod tests { end_rx.await.unwrap(); }; - let (_r0, _r1) = tokio::join!(server, client); + let (_r0, _r1) = citadel_io::tokio::join!(server, client); Ok(()) } } diff --git a/citadel_wire/src/standard/socket_helpers.rs b/citadel_wire/src/standard/socket_helpers.rs index 2a2e4f289..f92a8bbcc 100644 --- a/citadel_wire/src/standard/socket_helpers.rs +++ b/citadel_wire/src/standard/socket_helpers.rs @@ -1,4 +1,4 @@ -use citadel_io::{TcpListener, TcpStream, UdpSocket}; +use citadel_io::tokio::net::{TcpListener, TcpStream, UdpSocket}; use socket2::{Domain, SockAddr, Socket, Type}; use std::net::{IpAddr, SocketAddr, SocketAddrV6}; use std::time::Duration; @@ -44,8 +44,8 @@ async fn setup_connect( reuse: bool, ) -> Result { setup_base_socket(connect_addr, &socket, reuse)?; - let socket = citadel_io::TcpSocket::from_std_stream(socket.into()); - Ok(tokio::time::timeout(timeout, socket.connect(connect_addr)).await??) + let socket = citadel_io::tokio::net::TcpSocket::from_std_stream(socket.into()); + Ok(citadel_io::tokio::time::timeout(timeout, socket.connect(connect_addr)).await??) } fn get_udp_socket_inner( @@ -68,7 +68,7 @@ fn get_udp_socket_inner( let socket = get_udp_socket_builder(domain)?; setup_bind(addr, &socket, reuse)?; let std_socket: std::net::UdpSocket = socket.into(); - let tokio_socket = citadel_io::UdpSocket::from_std(std_socket)?; + let tokio_socket = citadel_io::tokio::net::UdpSocket::from_std(std_socket)?; Ok(tokio_socket) } @@ -189,10 +189,11 @@ mod tests { use crate::socket_helpers::{ get_tcp_listener, get_tcp_stream, get_udp_socket, is_ipv6_enabled, }; + use citadel_io::tokio; + use citadel_io::tokio::io::{AsyncReadExt, AsyncWriteExt}; use rstest::*; use std::net::SocketAddr; use std::time::Duration; - use tokio::io::{AsyncReadExt, AsyncWriteExt}; const TIMEOUT: Duration = Duration::from_millis(2000); @@ -210,7 +211,7 @@ mod tests { let server = get_tcp_listener(addr).unwrap(); let addr = server.local_addr().unwrap(); - let server = citadel_io::spawn(async move { + let server = citadel_io::tokio::task::spawn(async move { log::trace!(target: "citadel", "Starting server @ {:?}", addr); let (mut conn, addr) = server.accept().await.unwrap(); log::trace!(target: "citadel", "RECV {:?} from {:?}", &conn, addr); @@ -219,12 +220,12 @@ mod tests { assert_eq!(buf, &[1, 2, 3]); }); - let client = citadel_io::spawn(async move { + let client = citadel_io::tokio::task::spawn(async move { let mut client = get_tcp_stream(addr, TIMEOUT).await.unwrap(); client.write_all(&[1, 2, 3]).await.unwrap(); }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); Ok(r0.and(r1)?) } @@ -241,9 +242,9 @@ mod tests { } let server = get_udp_socket(addr).unwrap(); let addr = server.local_addr().unwrap(); - let (ready_tx, ready_rx) = tokio::sync::oneshot::channel(); + let (ready_tx, ready_rx) = citadel_io::tokio::sync::oneshot::channel(); - let server = citadel_io::spawn(async move { + let server = citadel_io::tokio::task::spawn(async move { log::trace!(target: "citadel", "Starting server @ {:?}", addr); let buf = &mut [0u8; 3]; ready_tx.send(()).unwrap(); @@ -258,14 +259,14 @@ mod tests { "127.0.0.1:0" }; - let client = citadel_io::spawn(async move { + let client = citadel_io::tokio::task::spawn(async move { let client = get_udp_socket(client_bind_addr)?; ready_rx.await?; client.send_to(&[1, 2, 3], addr).await?; Ok(()) as Result<(), anyhow::Error> }); - let (r0, r1) = tokio::try_join!(server, client)?; + let (r0, r1) = citadel_io::tokio::try_join!(server, client)?; log::trace!(target: "citadel", "Done with UDP test {:?}", addr); r0.and(r1) } diff --git a/citadel_wire/src/standard/tls.rs b/citadel_wire/src/standard/tls.rs index 961fd4174..6e7473983 100644 --- a/citadel_wire/src/standard/tls.rs +++ b/citadel_wire/src/standard/tls.rs @@ -1,15 +1,25 @@ +use crate::exports::{Certificate, PrivateKey}; use crate::quic::generate_self_signed_cert; -use rustls::{Certificate, ClientConfig, PrivateKey, RootCertStore}; +use rustls::{ClientConfig, RootCertStore}; use std::io::Error; use std::sync::Arc; use tokio_rustls::{TlsAcceptor, TlsConnector}; /// Useful for allowing migration from a TLS config to a QUIC config in the citadel_proto crate -#[derive(Clone)] pub struct TLSQUICInterop { pub tls_acceptor: TlsAcceptor, - pub quic_chain: Vec, - pub quic_priv_key: PrivateKey, + pub quic_chain: Vec>, + pub quic_priv_key: PrivateKey<'static>, +} + +impl Clone for TLSQUICInterop { + fn clone(&self) -> Self { + TLSQUICInterop { + tls_acceptor: self.tls_acceptor.clone(), + quic_chain: self.quic_chain.clone(), + quic_priv_key: self.quic_priv_key.clone_key(), + } + } } pub fn create_client_dangerous_config() -> TlsConnector { @@ -27,7 +37,7 @@ pub fn create_rustls_client_config>( cert_vec_to_secure_client_config( &allowed_certs .iter() - .map(|r| rustls::Certificate(r.as_ref().to_vec())) + .map(|r| crate::exports::Certificate::from(r.as_ref().to_vec())) .collect(), ) } @@ -43,7 +53,7 @@ pub fn cert_vec_to_secure_client_config( let mut root_store = RootCertStore::empty(); for cert in certs { - root_store.add(cert)?; + root_store.add(cert.clone())?; } Ok(crate::quic::secure::client_config(root_store)) @@ -65,12 +75,11 @@ pub fn create_server_self_signed_config() -> Result Result>, + key_der: PrivateKeyDer<'static>, +) -> Result { + // Create a new RustlsServerConfig + let rustls_config = + RustlsServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .with_no_client_auth() + .with_single_cert(cert_chain, key_der)?; + + Ok(rustls_config) +} + pub fn create_server_config( pkcs12_der: &[u8], password: &str, @@ -87,7 +112,7 @@ pub fn create_server_config( crate::misc::pkcs_12_components_to_quic_keys(certs_stack.as_ref(), &cert, &priv_key)?; let server_config = - crate::quic::secure::server_config(quic_chain.clone(), quic_priv_key.clone())?; + create_rustls_config_from_keys(quic_chain.clone(), quic_priv_key.clone_key())?; let ret = TLSQUICInterop { tls_acceptor: TlsAcceptor::from(Arc::new(server_config)), @@ -100,16 +125,19 @@ pub fn create_server_config( /// This can be an expensive operation, empirically lasting upwards of 200ms on some systems /// This should only be called once, preferably at init of the protocol -pub async fn load_native_certs_async() -> Result, Error> { - citadel_io::spawn_blocking(load_native_certs) +pub async fn load_native_certs_async() -> Result>, Error> { + citadel_io::tokio::task::spawn_blocking(load_native_certs) .await .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, format!("{err:?}")))? } /// Loads native certs. This is an expensive operation, and should be called once per node -pub fn load_native_certs() -> Result, Error> { - rustls_native_certs::load_native_certs() - .map(|r| r.into_iter().map(|cert| Certificate(cert.0)).collect()) +pub fn load_native_certs() -> Result>, Error> { + Ok(rustls_native_certs::load_native_certs() + .certs + .into_iter() + .map(Certificate::from) + .collect()) } #[cfg(test)] diff --git a/citadel_wire/src/standard/upnp_handler.rs b/citadel_wire/src/standard/upnp_handler.rs index 34f89920d..06ac9c4d0 100644 --- a/citadel_wire/src/standard/upnp_handler.rs +++ b/citadel_wire/src/standard/upnp_handler.rs @@ -1,10 +1,10 @@ use crate::error::FirewallError; +use citadel_io::tokio::time::Duration; use igd::aio::Gateway; use igd::{PortMappingProtocol, SearchOptions}; use std::fmt::Formatter; use std::net::{IpAddr, Ipv4Addr, SocketAddrV4}; use std::str::FromStr; -use tokio::time::Duration; pub struct UPnPHandler { local_ip_address: Ipv4Addr, diff --git a/citadel_wire/src/udp_traversal/hole_punch_config.rs b/citadel_wire/src/udp_traversal/hole_punch_config.rs index bd9edabf9..f0abdbfb7 100644 --- a/citadel_wire/src/udp_traversal/hole_punch_config.rs +++ b/citadel_wire/src/udp_traversal/hole_punch_config.rs @@ -1,5 +1,5 @@ use crate::nat_identification::NatType; -use citadel_io::UdpSocket; +use citadel_io::tokio::net::UdpSocket; use itertools::Itertools; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; diff --git a/citadel_wire/src/udp_traversal/linear/method3.rs b/citadel_wire/src/udp_traversal/linear/method3.rs index ddb0426db..f016f860c 100644 --- a/citadel_wire/src/udp_traversal/linear/method3.rs +++ b/citadel_wire/src/udp_traversal/linear/method3.rs @@ -2,10 +2,10 @@ use std::collections::{HashMap, HashSet}; use std::io::ErrorKind; use std::net::SocketAddr; -use citadel_io::UdpSocket; +use citadel_io::tokio::net::UdpSocket; +use citadel_io::tokio::sync::Mutex as TokioMutex; +use citadel_io::tokio::time::Duration; use serde::{Deserialize, Serialize}; -use tokio::sync::Mutex as TokioMutex; -use tokio::time::Duration; use crate::error::FirewallError; use crate::socket_helpers::ensure_ipv6; @@ -107,7 +107,7 @@ impl Method3 { let receiver_task = async move { // we are only interested in the first receiver to receive a value - let timeout = tokio::time::timeout( + let timeout = citadel_io::tokio::time::timeout( Duration::from_millis(3000), Self::recv_until( socket_wrapper, @@ -128,14 +128,14 @@ impl Method3 { }; let sender_task = async move { - //tokio::time::sleep(Duration::from_millis(10)).await; // wait to allow time for the joined receiver task to execute + //citadel_io::tokio::time::sleep(Duration::from_millis(10)).await; // wait to allow time for the joined receiver task to execute Self::send_packet_barrage(packet_send_params, None) .await .map_err(|err| FirewallError::HolePunch(err.to_string()))?; Ok(()) as Result<(), FirewallError> }; - let (res0, res1) = tokio::join!(receiver_task, sender_task); + let (res0, res1) = citadel_io::tokio::join!(receiver_task, sender_task); log::trace!(target: "citadel", "Hole-punch join result: recv={:?} and send={:?}", res0, res1); @@ -170,7 +170,7 @@ impl Method3 { this_node_type, } = params; - let mut sleep = tokio::time::interval(Duration::from_millis(*millis_delta)); + let mut sleep = citadel_io::tokio::time::interval(Duration::from_millis(*millis_delta)); let delta_ttl = delta_ttl.unwrap_or(0); let ttls = (0..*count) .map(|idx| ttl_init + (idx * delta_ttl)) diff --git a/citadel_wire/src/udp_traversal/linear/mod.rs b/citadel_wire/src/udp_traversal/linear/mod.rs index 36177ba28..a34d5e585 100644 --- a/citadel_wire/src/udp_traversal/linear/mod.rs +++ b/citadel_wire/src/udp_traversal/linear/mod.rs @@ -1,6 +1,7 @@ use std::net::SocketAddr; -use citadel_io::UdpSocket; +use citadel_io::tokio::net::UdpSocket; +use citadel_io::tokio::time::Duration; use either::Either; use igd::PortMappingProtocol; use tokio::sync::mpsc::UnboundedSender; @@ -71,8 +72,10 @@ impl SingleUDPHolePuncher { pub async fn try_method( &mut self, method: NatTraversalMethod, - mut kill_switch: tokio::sync::broadcast::Receiver<(HolePunchID, HolePunchID)>, - mut post_kill_rebuild: tokio::sync::mpsc::UnboundedSender>, + mut kill_switch: citadel_io::tokio::sync::broadcast::Receiver<(HolePunchID, HolePunchID)>, + mut post_kill_rebuild: citadel_io::tokio::sync::mpsc::UnboundedSender< + Option, + >, ) -> Result { match method { NatTraversalMethod::UPnP => { @@ -145,7 +148,7 @@ impl SingleUDPHolePuncher { None }; - let res = tokio::select! { + let res = citadel_io::tokio::select! { res0 = process => Either::Right(res0?), res1 = kill_listener => Either::Left(res1) }; diff --git a/citadel_wire/src/udp_traversal/multi/mod.rs b/citadel_wire/src/udp_traversal/multi/mod.rs index 775935cc3..4261be13f 100644 --- a/citadel_wire/src/udp_traversal/multi/mod.rs +++ b/citadel_wire/src/udp_traversal/multi/mod.rs @@ -83,7 +83,7 @@ impl DualStackUdpHolePuncher { // TODO: Setup concurrent UPnP AND NAT-PMP async https://docs.rs/natpmp/latest/natpmp/struct.NatpmpAsync.html let task = async move { - tokio::task::spawn(drive(hole_punchers, relative_node_type, napp)) + citadel_io::tokio::task::spawn(drive(hole_punchers, relative_node_type, napp)) .await .map_err(|err| anyhow::Error::msg(format!("panic in hole puncher: {:?}", err)))? }; @@ -132,11 +132,11 @@ async fn drive( .await?); let (final_candidate_tx, final_candidate_rx) = - tokio::sync::oneshot::channel::(); + citadel_io::tokio::sync::oneshot::channel::(); let (ref kill_signal_tx, _kill_signal_rx) = - tokio::sync::broadcast::channel(hole_punchers.len()); - let (ref post_rebuild_tx, post_rebuild_rx) = tokio::sync::mpsc::unbounded_channel(); + citadel_io::tokio::sync::broadcast::channel(hole_punchers.len()); + let (ref post_rebuild_tx, post_rebuild_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let final_candidate_tx = &mut citadel_io::Mutex::new(Some(final_candidate_tx)); @@ -154,7 +154,7 @@ async fn drive( post_rebuild_rx: Option>>, } - let rebuilder = &tokio::sync::Mutex::new(RebuildReadyContainer { + let rebuilder = &citadel_io::tokio::sync::Mutex::new(RebuildReadyContainer { local_failures: HashMap::new(), post_rebuild_rx: Some(post_rebuild_rx), }); @@ -180,7 +180,7 @@ async fn drive( } let current_enqueued: &tokio::sync::Mutex> = - &tokio::sync::Mutex::new(vec![]); + &citadel_io::tokio::sync::Mutex::new(vec![]); let finished_count = &citadel_io::Mutex::new(0); let hole_puncher_count = futures.len(); @@ -465,7 +465,7 @@ async fn drive( futures::future::pending().await }; - tokio::select! { + citadel_io::tokio::select! { _res0 = sender_reader_combo => { log::trace!(target: "citadel", "[DualStack] Sender/Reader combo finished"); }, diff --git a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs b/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs index 32be00be1..8f4b91ff6 100644 --- a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs +++ b/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs @@ -1,5 +1,5 @@ use crate::udp_traversal::HolePunchID; -use citadel_io::UdpSocket; +use citadel_io::tokio::net::UdpSocket; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::net::{IpAddr, SocketAddr}; diff --git a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs index fcd4107b5..2487cff04 100644 --- a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs +++ b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs @@ -3,7 +3,7 @@ use crate::udp_traversal::hole_punch_config::HolePunchConfig; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; use crate::udp_traversal::multi::DualStackUdpHolePuncher; use crate::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; -use citadel_io::UdpSocket; +use citadel_io::tokio::net::UdpSocket; use futures::Future; use netbeam::reliable_conn::ReliableOrderedStreamToTargetExt; use netbeam::sync::network_endpoint::NetworkEndpoint; @@ -199,6 +199,7 @@ impl EndpointHolePunchExt for NetworkEndpoint { #[cfg(test)] mod tests { use crate::udp_traversal::udp_hole_puncher::EndpointHolePunchExt; + use citadel_io::tokio; use netbeam::sync::test_utils::create_streams_with_addrs_and_lag; use rstest::rstest; @@ -224,9 +225,9 @@ mod tests { res.unwrap() }; - let server = citadel_io::spawn(server); - let client = citadel_io::spawn(client); - let (res0, res1) = tokio::join!(server, client); + let server = citadel_io::tokio::task::spawn(server); + let client = citadel_io::tokio::task::spawn(client); + let (res0, res1) = citadel_io::tokio::join!(server, client); log::trace!(target: "citadel", "JOIN complete! {:?} | {:?}", res0, res1); let (_res0, _res1) = (res0.unwrap(), res1.unwrap()); diff --git a/firebase-rtdb/Cargo.toml b/firebase-rtdb/Cargo.toml index a4a3564a1..b3e7b2921 100644 --- a/firebase-rtdb/Cargo.toml +++ b/firebase-rtdb/Cargo.toml @@ -14,10 +14,10 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -reqwest = { workspace = true, features = ["rustls", "rustls-native-certs", "rustls-tls", "json", "serde_json"] } +reqwest = { workspace = true, features = ["rustls-tls-native-roots", "rustls-tls", "json"] } log = { workspace = true } serde = { workspace = true, features = ["derive"] } [dev-dependencies] -tokio = { workspace = true, features = ["full"] } +citadel_io = { workspace = true } citadel_logging = { workspace = true } diff --git a/netbeam/Cargo.toml b/netbeam/Cargo.toml index 1b1f458d0..1aafe3af6 100644 --- a/netbeam/Cargo.toml +++ b/netbeam/Cargo.toml @@ -26,7 +26,6 @@ wasm = [ ] [dependencies] -tokio = { workspace = true, features = ["macros", "rt", "time", "io-util", "sync"] } citadel_io = { workspace = true } futures = { workspace = true, features = ["std"] } bytes = { workspace = true } @@ -34,7 +33,6 @@ async-trait = { workspace = true } bincode = { workspace = true } serde = { workspace = true, features = ["derive"] } anyhow = { workspace = true } -tokio-util = { workspace = true, features = ["codec"] } rand = { workspace = true } async-stream = { workspace = true } sync_wrapper = { workspace = true} diff --git a/netbeam/src/multiplex.rs b/netbeam/src/multiplex.rs index a43dfe898..6c5e99375 100644 --- a/netbeam/src/multiplex.rs +++ b/netbeam/src/multiplex.rs @@ -6,6 +6,8 @@ use crate::sync::subscription::{ use crate::sync::{RelativeNodeType, SymmetricConvID}; use anyhow::Error; use async_trait::async_trait; +use citadel_io::tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use citadel_io::tokio::sync::Mutex; use citadel_io::RwLock; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -15,8 +17,6 @@ use std::hash::Hash; use std::ops::Deref; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use tokio::sync::Mutex; pub trait MultiplexedConnKey: Debug + Eq + Hash + Copy + Send + Sync + Serialize + DeserializeOwned + IDGen @@ -108,7 +108,7 @@ impl MultiplexedConn { let mut subscribers = HashMap::new(); for id in ids { - let (tx, pre_reserved_rx) = tokio::sync::mpsc::unbounded_channel(); + let (tx, pre_reserved_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); subscribers.insert( id, MemorySender { @@ -317,6 +317,7 @@ mod tests { use crate::sync::test_utils::create_streams; use crate::sync::SymmetricConvID; use async_recursion::async_recursion; + use citadel_io::tokio; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] @@ -340,10 +341,10 @@ mod tests { return (server_stream, client_stream); } - let (tx, rx) = tokio::sync::oneshot::channel::<()>(); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); let (server_stream0, client_stream0) = (server_stream.clone(), client_stream.clone()); - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { // get one substream from the input stream let next_stream: OwnedMultiplexedSubscription = server_stream.initiate_subscription().await.unwrap(); @@ -352,7 +353,7 @@ mod tests { next_stream.multiplex::().await.unwrap() }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let next_stream: OwnedMultiplexedSubscription = client_stream.initiate_subscription().await.unwrap(); let val = next_stream.recv_serialized::().await.unwrap(); @@ -361,9 +362,9 @@ mod tests { next_stream.multiplex::().await.unwrap() }); - let (tx1, rx1) = tokio::sync::oneshot::channel::<()>(); + let (tx1, rx1) = citadel_io::tokio::sync::oneshot::channel::<()>(); - let server1 = tokio::spawn(async move { + let server1 = citadel_io::tokio::spawn(async move { // get one substream from the input stream let next_stream: OwnedMultiplexedSubscription = server_stream0.initiate_subscription().await.unwrap(); @@ -372,7 +373,7 @@ mod tests { next_stream.multiplex::().await.unwrap() }); - let client1 = tokio::spawn(async move { + let client1 = citadel_io::tokio::spawn(async move { let next_stream: OwnedMultiplexedSubscription = client_stream0.initiate_subscription().await.unwrap(); let val = next_stream.recv_serialized::().await.unwrap(); @@ -382,7 +383,7 @@ mod tests { }); let (next_server_stream, next_client_stream, _, _) = - tokio::join!(server, client, server1, client1); + citadel_io::tokio::join!(server, client, server1, client1); nested( idx + 1, diff --git a/netbeam/src/reliable_conn.rs b/netbeam/src/reliable_conn.rs index 34c8275a6..2b25b1168 100644 --- a/netbeam/src/reliable_conn.rs +++ b/netbeam/src/reliable_conn.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; use bytes::{Bytes, BytesMut}; +use citadel_io::tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use citadel_io::tokio::sync::Mutex; use serde::de::DeserializeOwned; use serde::Serialize; use std::net::SocketAddr; use std::sync::Arc; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use tokio::sync::Mutex; #[async_trait] /// This represents a direct client to server or client->server->peer connection (usually just TCP) for establishing the hole-punching process @@ -65,7 +65,7 @@ impl ReliableOrderedStreamToTargetExt for T {} #[async_trait] #[cfg(not(target_family = "wasm"))] -impl ReliableOrderedStreamToTarget for tokio::net::TcpStream { +impl ReliableOrderedStreamToTarget for citadel_io::tokio::net::TcpStream { async fn send_to_peer(&self, input: &[u8]) -> std::io::Result<()> { loop { self.writable().await?; @@ -103,13 +103,13 @@ impl ReliableOrderedStreamToTarget for tokio::net::TcpStream { } #[cfg(not(target_family = "wasm"))] -impl ConnAddr for tokio::net::TcpStream { +impl ConnAddr for citadel_io::tokio::net::TcpStream { fn local_addr(&self) -> std::io::Result { - tokio::net::TcpStream::local_addr(self) + citadel_io::tokio::net::TcpStream::local_addr(self) } fn peer_addr(&self) -> std::io::Result { - tokio::net::TcpStream::peer_addr(self) + citadel_io::tokio::net::TcpStream::peer_addr(self) } } @@ -159,10 +159,10 @@ pub mod simulator { }; use async_trait::async_trait; use bytes::Bytes; + use citadel_io::tokio::sync::mpsc::UnboundedSender; use rand::Rng; use std::net::SocketAddr; use std::sync::Arc; - use tokio::sync::mpsc::UnboundedSender; pub struct NetworkConnSimulator { inner: Arc, @@ -174,9 +174,9 @@ pub mod simulator { pub(crate) fn new(min_lag: usize, inner: T) -> Self { let inner = Arc::new(inner); let inner_fwd = inner.clone(); - let (fwd, mut rx) = tokio::sync::mpsc::unbounded_channel::>(); + let (fwd, mut rx) = citadel_io::tokio::sync::mpsc::unbounded_channel::>(); - citadel_io::spawn(async move { + citadel_io::tokio::task::spawn(async move { while let Some(packet) = rx.recv().await { if min_lag != 0 { let rnd = { @@ -185,7 +185,10 @@ pub mod simulator { rng.gen_range(min_lag..max) // 50 -> 150ms ping }; - tokio::time::sleep(std::time::Duration::from_millis(rnd as u64)).await; + citadel_io::tokio::time::sleep(std::time::Duration::from_millis( + rnd as u64, + )) + .await; } inner_fwd.send_to_peer(&packet).await.unwrap(); diff --git a/netbeam/src/sync/callback_channel.rs b/netbeam/src/sync/callback_channel.rs index c9b92258e..a776c2720 100644 --- a/netbeam/src/sync/callback_channel.rs +++ b/netbeam/src/sync/callback_channel.rs @@ -1,8 +1,8 @@ +use citadel_io::tokio::sync::mpsc::{Receiver, Sender}; use futures::Stream; use std::fmt::{Debug, Formatter}; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio::sync::mpsc::{Receiver, Sender}; #[derive(Clone)] pub struct CallbackChannel { @@ -38,11 +38,11 @@ struct CallbackChannelInner { to_channel: Sender>, } -pub type CallbackChannelPayload = (T, Option>); +pub type CallbackChannelPayload = (T, Option>); impl CallbackChannel { pub fn new(buffer: usize) -> (Self, CallbackReceiver) { - let (to_channel, from_channel) = tokio::sync::mpsc::channel(buffer); + let (to_channel, from_channel) = citadel_io::tokio::sync::mpsc::channel(buffer); ( Self { inner: CallbackChannelInner { to_channel }, @@ -54,7 +54,7 @@ impl CallbackChannel { } pub async fn send(&self, payload: T) -> Result> { - let (tx, rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); self.inner .to_channel .send((payload, Some(tx))) @@ -87,6 +87,7 @@ impl Stream for CallbackReceiver { #[cfg(test)] mod tests { use crate::sync::callback_channel::CallbackChannel; + use citadel_io::tokio; use futures::StreamExt; #[tokio::test] @@ -112,9 +113,9 @@ mod tests { } }; - let server = tokio::spawn(server); - let client = tokio::spawn(client); + let server = citadel_io::tokio::spawn(server); + let client = citadel_io::tokio::spawn(client); - let (_, _) = tokio::join!(server, client); + let (_, _) = citadel_io::tokio::join!(server, client); } } diff --git a/netbeam/src/sync/channel/bi_channel.rs b/netbeam/src/sync/channel/bi_channel.rs index 59ce9571e..2bf1e1969 100644 --- a/netbeam/src/sync/channel/bi_channel.rs +++ b/netbeam/src/sync/channel/bi_channel.rs @@ -178,7 +178,7 @@ impl Drop for ChannelRecvHalf { let chan = self.tx.take().unwrap(); let recv_halt = self.recv_halt.clone(); - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { rt.spawn(async move { log::trace!(target: "citadel", "[Drop] on {:?} | recv_halt: {}", chan.node_type(), recv_halt.load(Ordering::Relaxed)); // if we haven't yet received a halt signal, send signal to parallel side @@ -222,6 +222,7 @@ impl Future for ChannelLoader<'_, T, S> #[cfg(test)] mod tests { use crate::sync::test_utils::create_streams; + use citadel_io::tokio; use futures::StreamExt; #[tokio::test] @@ -229,7 +230,7 @@ mod tests { citadel_logging::setup_log(); let (server, client) = create_streams().await; - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { let mut channel = server.bi_channel::().await.unwrap(); for x in 0..1000 { @@ -243,7 +244,7 @@ mod tests { } }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let mut channel = client.bi_channel::().await.unwrap(); for x in 0..1000 { @@ -257,7 +258,7 @@ mod tests { } }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); r0.unwrap(); r1.unwrap(); } diff --git a/netbeam/src/sync/mod.rs b/netbeam/src/sync/mod.rs index 7320aec66..bf42ef50e 100644 --- a/netbeam/src/sync/mod.rs +++ b/netbeam/src/sync/mod.rs @@ -24,15 +24,14 @@ impl From for SymmetricConvID { } } -#[cfg(not(target_family = "wasm"))] pub mod test_utils { use async_trait::async_trait; use bytes::Bytes; + use citadel_io::tokio::net::{TcpListener, TcpStream}; + use citadel_io::tokio::sync::Mutex; + use citadel_io::tokio_util::codec::{Framed, LengthDelimitedCodec}; use futures::stream::{SplitSink, SplitStream}; use futures::{SinkExt, StreamExt}; - use tokio::net::{TcpListener, TcpStream}; - use tokio::sync::Mutex; - use tokio_util::codec::{Framed, LengthDelimitedCodec}; use crate::reliable_conn::simulator::NetworkConnSimulator; use crate::reliable_conn::{ConnAddr, ReliableOrderedStreamToTarget}; @@ -105,7 +104,7 @@ pub mod test_utils { } pub async fn create_streams() -> (NetworkApplication, NetworkApplication) { - let (tx, rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); let server = async move { let listener = create_listener("127.0.0.1:0"); tx.send(listener.local_addr().unwrap()).unwrap(); @@ -127,13 +126,13 @@ pub mod test_utils { .unwrap() }; - tokio::join!(server, client) + citadel_io::tokio::join!(server, client) } pub async fn create_streams_with_addrs_and_lag( min: usize, ) -> (NetworkEndpoint, NetworkEndpoint) { - let (tx, rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); let server = async move { let listener = create_listener("127.0.0.1:0"); tx.send(listener.local_addr().unwrap()).unwrap(); @@ -155,7 +154,7 @@ pub mod test_utils { .unwrap() }; - tokio::join!(server, client) + citadel_io::tokio::join!(server, client) } pub async fn create_streams_with_addrs() -> (NetworkEndpoint, NetworkEndpoint) { diff --git a/netbeam/src/sync/network_application.rs b/netbeam/src/sync/network_application.rs index 22292f429..726e9a97c 100644 --- a/netbeam/src/sync/network_application.rs +++ b/netbeam/src/sync/network_application.rs @@ -3,9 +3,9 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; +use citadel_io::tokio::sync::Mutex; use serde::de::DeserializeOwned; use serde::Serialize; -use tokio::sync::Mutex; use crate::multiplex::{MultiplexedConn, MultiplexedConnKey, MultiplexedPacket}; use crate::reliable_conn::{ReliableOrderedStreamToTarget, ReliableOrderedStreamToTargetExt}; @@ -26,13 +26,13 @@ pub type NetworkApplication = MultiplexedConn; pub(crate) const INITIAL_CAPACITY: usize = 32; pub struct PreActionChannel { - tx: tokio::sync::mpsc::Sender, - rx: Mutex>, + tx: citadel_io::tokio::sync::mpsc::Sender, + rx: Mutex>, } impl PreActionChannel { pub(crate) fn new() -> Self { - let (tx, rx) = tokio::sync::mpsc::channel(1); + let (tx, rx) = citadel_io::tokio::sync::mpsc::channel(1); Self { tx, rx: Mutex::new(rx), @@ -41,8 +41,8 @@ impl PreActionChannel { } pub struct PostActionChannel { - tx: Mutex>>, - rx: Mutex>>, + tx: Mutex>>, + rx: Mutex>>, } impl PostActionChannel { @@ -67,7 +67,7 @@ impl PostActionChannel { } pub(crate) async fn setup_channel(&self, id: K) { - let (tx, rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); self.tx.lock().await.insert(id, tx); self.rx.lock().await.insert(id, rx); } @@ -77,7 +77,7 @@ impl PostActionChannel { pub(crate) fn new(initial_ids: &Vec) -> Self { let (mut tx, mut rx) = (HashMap::new(), HashMap::new()); for id in initial_ids { - let (tx_s, rx_s) = tokio::sync::oneshot::channel(); + let (tx_s, rx_s) = citadel_io::tokio::sync::oneshot::channel(); tx.insert(*id, tx_s); rx.insert(*id, rx_s); } @@ -108,7 +108,7 @@ impl MultiplexedConn { let this = Self::new(relative_node_type, t); let conn_task = this.clone(); - citadel_io::spawn(async move { + citadel_io::tokio::task::spawn(async move { while let Ok(ref packet) = conn_task.conn.recv().await { if let Err(err) = conn_task.forward_packet(packet).await { log::trace!(target: "citadel", "Unable to forward packet: {:?}", err); diff --git a/netbeam/src/sync/network_endpoint.rs b/netbeam/src/sync/network_endpoint.rs index fd570ff4d..5b14d9407 100644 --- a/netbeam/src/sync/network_endpoint.rs +++ b/netbeam/src/sync/network_endpoint.rs @@ -53,6 +53,7 @@ impl NetworkEndpoint { mod tests { use crate::reliable_conn::ConnAddr; use crate::sync::test_utils::create_streams_with_addrs; + use citadel_io::tokio; #[tokio::test] async fn main() { diff --git a/netbeam/src/sync/operations/net_select_ok.rs b/netbeam/src/sync/operations/net_select_ok.rs index ee315121a..0e5ec219f 100644 --- a/netbeam/src/sync/operations/net_select_ok.rs +++ b/netbeam/src/sync/operations/net_select_ok.rs @@ -3,11 +3,11 @@ use crate::reliable_conn::{ReliableOrderedStreamToTarget, ReliableOrderedStreamT use crate::sync::subscription::Subscribable; use crate::sync::subscription::SubscriptionBiStream; use crate::sync::RelativeNodeType; +use citadel_io::tokio::sync::{Mutex, MutexGuard}; use futures::Future; use serde::{Deserialize, Serialize}; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio::sync::{Mutex, MutexGuard}; /// Two endpoints race to produce Ok(R). The first endpoint to produce Ok(R) wins. Includes conflict-resolution synchronization pub struct NetSelectOk<'a, R> { @@ -95,7 +95,7 @@ where { let conn = &(conn.initiate_subscription().await?); log::trace!(target: "citadel", "NET_SELECT_OK started conv={:?} for {:?}", conn.id(), local_node_type); - let (stopper_tx, stopper_rx) = tokio::sync::oneshot::channel::<()>(); + let (stopper_tx, stopper_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); struct LocalState { local_state: State, @@ -257,7 +257,7 @@ where Err(anyhow::Error::msg("Stopped before the resolver")) }; - tokio::select! { + citadel_io::tokio::select! { res0 = evaluator => { log::trace!(target: "citadel", "[conv={:?}] NET_SELECT_OK Ending for {:?}", conn.id(), local_node_type); let (ret, remote_success) = res0?; @@ -289,6 +289,7 @@ fn wrap_return( mod tests { use crate::sync::network_application::NetworkApplication; use crate::sync::test_utils::create_streams; + use citadel_io::tokio; use std::fmt::Debug; use std::future::Future; use std::time::Duration; @@ -368,9 +369,9 @@ mod tests { res }; - let server = tokio::spawn(server); - let client = tokio::spawn(client); - let (res0, res1) = tokio::join!(server, client); + let server = citadel_io::tokio::spawn(server); + let client = citadel_io::tokio::spawn(client); + let (res0, res1) = citadel_io::tokio::join!(server, client); let (res0, res1) = (res0.unwrap(), res1.unwrap()); if res0.result.is_some() { @@ -396,7 +397,7 @@ mod tests { } async fn dummy_function() -> Result<(), anyhow::Error> { - tokio::time::sleep(Duration::from_millis(50)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(50)).await; Ok(()) } diff --git a/netbeam/src/sync/operations/net_try_join.rs b/netbeam/src/sync/operations/net_try_join.rs index e99dce20e..0953f643a 100644 --- a/netbeam/src/sync/operations/net_try_join.rs +++ b/netbeam/src/sync/operations/net_try_join.rs @@ -3,11 +3,11 @@ use crate::reliable_conn::{ReliableOrderedStreamToTarget, ReliableOrderedStreamT use crate::sync::subscription::{Subscribable, SubscriptionBiStream}; use crate::sync::RelativeNodeType; use crate::ScopedFutureResult; +use citadel_io::tokio::sync::{Mutex, MutexGuard}; use serde::{Deserialize, Serialize}; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use tokio::sync::{Mutex, MutexGuard}; /// Two endpoints produce Ok(T). Returns when both endpoints produce Ok(T), or, when the first error occurs pub struct NetTryJoin<'a, T, E> { @@ -84,7 +84,7 @@ where { let conn = &(conn.initiate_subscription().await?); log::trace!(target: "citadel", "NET_TRY_JOIN started conv={:?} for {:?}", conn.id(), local_node_type); - let (stopper_tx, stopper_rx) = tokio::sync::oneshot::channel::<()>(); + let (stopper_tx, stopper_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); struct LocalState { local_state: State, @@ -186,7 +186,7 @@ where Err(anyhow::Error::msg("Stopped before the resolver")) }; - tokio::select! { + citadel_io::tokio::select! { res0 = evaluator => { log::trace!(target: "citadel", "NET_TRY_JOIN ending for {:?} (conv={:?})", local_node_type, conn.id()); let ret = res0?; @@ -205,6 +205,7 @@ fn wrap_return(value: Option>) -> Result Result<(), &'static str> { - tokio::time::sleep(Duration::from_millis(50)).await; + citadel_io::tokio::time::sleep(Duration::from_millis(50)).await; Ok(()) } diff --git a/netbeam/src/sync/primitives/net_mutex.rs b/netbeam/src/sync/primitives/net_mutex.rs index b4aeb4f17..38cc6fdc2 100644 --- a/netbeam/src/sync/primitives/net_mutex.rs +++ b/netbeam/src/sync/primitives/net_mutex.rs @@ -4,10 +4,10 @@ use std::sync::Arc; use std::task::{Context, Poll}; use crate::ScopedFutureResult; +use citadel_io::tokio::sync::mpsc::{Receiver, Sender}; +use citadel_io::tokio::sync::{Mutex, OwnedMutexGuard}; use futures::Future; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc::{Receiver, Sender}; -use tokio::sync::{Mutex, OwnedMutexGuard}; use crate::reliable_conn::ReliableOrderedStreamToTargetExt; use crate::sync::primitives::NetObject; @@ -25,7 +25,7 @@ pub struct NetMutex { app: Arc>, // contains the background_to_active_rx shared_state: Arc>>, - stop_tx: Option>, + stop_tx: Option>, bg_stop_signaller: Sender<()>, } @@ -52,7 +52,7 @@ impl Drop for LocalLockHolder { let this = self.0.take().unwrap(); let sender = this.1.clone(); std::mem::drop(this); // free the lock - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { rt.spawn(async move { let _ = sender.send(()).await; // alert the bg }); @@ -64,8 +64,8 @@ impl Drop for LocalLockHolder { impl NetMutex { async fn new_internal(conn: InnerChannel, t: T) -> Result { // create a channel to listen here for incoming messages and alter the local state as needed - let (stop_tx, stop_rx) = tokio::sync::oneshot::channel::<()>(); - let (active_to_bg_tx, active_to_bg_rx) = tokio::sync::mpsc::channel::<()>(1); + let (stop_tx, stop_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (active_to_bg_tx, active_to_bg_rx) = citadel_io::tokio::sync::mpsc::channel::<()>(1); let this = Self { app: Arc::new(conn), @@ -77,7 +77,7 @@ impl NetMutex { let shared_state = this.shared_state.clone(); let channel = this.app.clone(); - citadel_io::spawn(async move { + citadel_io::tokio::task::spawn(async move { if let Err(err) = passive_background_handler::(channel, shared_state, stop_rx, active_to_bg_rx) .await @@ -117,7 +117,7 @@ impl Drop for NetMutex { // stop the background task let _ = stop_tx.send(()); - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { rt.spawn(async move { conn.send_serialized(UpdatePacket::Halt).await }); } } @@ -147,7 +147,7 @@ impl Drop for NetMutexGuard { let guard = self.guard.take().unwrap(); let app = self.conn.clone(); - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { let future = NetMutexGuardDropCode::new::(app, guard); rt.spawn(future); } else { @@ -453,7 +453,7 @@ async fn yield_lock( async fn passive_background_handler( channel: Arc>, shared_state: Arc>>, - stop_rx: tokio::sync::oneshot::Receiver<()>, + stop_rx: citadel_io::tokio::sync::oneshot::Receiver<()>, mut active_to_background_rx: Receiver<()>, ) -> Result<(), anyhow::Error> { let background_task = async move { @@ -462,7 +462,7 @@ async fn passive_background_handler( match shared_state.clone().try_lock_owned() { Ok(lock) => { // here, any local requests will be blocked until an external packet gets received OR local signals background to stop; - let packet = tokio::select! { + let packet = citadel_io::tokio::select! { res0 = channel.recv_serialized::() => res0?, res1 = active_to_background_rx.recv() => { // in the case local tries ot make an outgoing request, we will stop listening in the background @@ -511,7 +511,7 @@ async fn passive_background_handler( } }; - tokio::select! { + citadel_io::tokio::select! { res0 = background_task => res0, _res1 = stop_rx => Ok(()) } @@ -520,6 +520,7 @@ async fn passive_background_handler( #[cfg(test)] mod tests { use crate::sync::test_utils::create_streams_with_addrs_and_lag; + use citadel_io::tokio; use rstest::rstest; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; @@ -528,7 +529,7 @@ mod tests { #[case(0, 1000)] #[case(5, 50)] #[case(50, 10)] - #[tokio::test] + #[citadel_io::tokio::test] async fn test_net_mutex(#[case] lag: usize, #[case] count: u64) { citadel_logging::setup_log(); @@ -536,13 +537,13 @@ mod tests { let init_value = 1000u64; let final_value = 1001u64; - let (client_done_tx, client_done_rx) = tokio::sync::oneshot::channel::<()>(); - let (server_done_tx, server_done_rx) = tokio::sync::oneshot::channel::<()>(); + let (client_done_tx, client_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (server_done_tx, server_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); let server_ref = Arc::new(AtomicU64::new(init_value)); let client_ref = server_ref.clone(); - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { let mutex = &server_stream.mutex(Some(init_value)).await.unwrap(); log::trace!(target: "citadel", "Success establishing mutex on server"); client_done_rx.await.unwrap(); @@ -564,7 +565,7 @@ mod tests { server_done_tx.send(()).unwrap(); }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let mutex = &client_stream.mutex::(None).await.unwrap(); log::trace!(target: "citadel", "Success establishing mutex on client"); let mut guard = mutex.lock().await.unwrap(); @@ -586,7 +587,7 @@ mod tests { server_done_rx.await.unwrap(); }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); r0.unwrap(); r1.unwrap(); } diff --git a/netbeam/src/sync/primitives/net_rwlock.rs b/netbeam/src/sync/primitives/net_rwlock.rs index fd4e955d9..d8925cf05 100644 --- a/netbeam/src/sync/primitives/net_rwlock.rs +++ b/netbeam/src/sync/primitives/net_rwlock.rs @@ -4,8 +4,8 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; -use tokio::sync::mpsc::{Receiver, Sender}; -use tokio::sync::{Mutex, OwnedMutexGuard}; +use citadel_io::tokio::sync::mpsc::{Receiver, Sender}; +use citadel_io::tokio::sync::{Mutex, OwnedMutexGuard}; use crate::reliable_conn::ReliableOrderedStreamToTargetExt; use crate::sync::primitives::net_mutex::{sync_establish_init, InnerChannel}; @@ -29,7 +29,7 @@ pub struct NetRwLock { // if local tries to write, then the mutex guard is given-as is shared: Arc>>, channel: Arc>, - stop_tx: Option>, + stop_tx: Option>, // Used to cheaply clone read locks locally. There is no equivalent WriteLock version, since only one can exist local_active_read_lock: Arc>>>, active_to_bg_signalled: Sender<()>, @@ -45,8 +45,8 @@ impl NetRwLock { initial_value: T, ) -> Result { // create a channel to listen here for incoming messages and alter the local state as needed - let (stop_tx, stop_rx) = tokio::sync::oneshot::channel::<()>(); - let (active_to_bg_tx, active_to_bg_rx) = tokio::sync::mpsc::channel(1); + let (stop_tx, stop_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (active_to_bg_tx, active_to_bg_rx) = citadel_io::tokio::sync::mpsc::channel(1); let this = Self { shared: Arc::new(Mutex::new((initial_value, active_to_bg_tx.clone()))), @@ -60,7 +60,7 @@ impl NetRwLock { let local_read_lock = this.local_active_read_lock.clone(); let channel = this.channel.clone(); - citadel_io::spawn(async move { + citadel_io::tokio::task::spawn(async move { if let Err(err) = passive_background_handler::( channel, shared_state, @@ -113,7 +113,7 @@ impl Drop for NetRwLock { let stop_tx = self.stop_tx.take().unwrap(); let _ = stop_tx.send(()); - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { rt.spawn(async move { conn.send_serialized(UpdatePacket::Halt).await }); } } @@ -214,7 +214,7 @@ pub(crate) mod read { // immediately remove the shared store to prevent further acquires *self.shared_store.write() = None; // 1 arc left (this) - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { let future = NetRwLockEitherGuardDropCode::new::(self.conn.clone(), this); rt.spawn(future); } @@ -282,7 +282,7 @@ pub(crate) mod write { impl Drop for NetRwLockWriteGuard { fn drop(&mut self) { let this = self.inner.take().unwrap(); - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { let future = NetRwLockEitherGuardDropCode::new::(self.conn.clone(), this); rt.spawn(future); } @@ -507,7 +507,7 @@ impl Drop for LocalLockHolder { // if this is a lock holder from the BG, we aren't interested in sending an alert if !self.is_from_background() { let sender = self.free_lock_and_get_sender(); // frees the lock - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { rt.spawn(async move { let _ = sender.send(()).await; // alert the bg }); @@ -585,7 +585,7 @@ async fn yield_lock( async fn passive_background_handler( channel: Arc>, shared_state: Arc>>, - stop_rx: tokio::sync::oneshot::Receiver<()>, + stop_rx: citadel_io::tokio::sync::oneshot::Receiver<()>, mut active_to_background_rx: Receiver<()>, read_lock_local: Arc>>>, ) -> Result<(), anyhow::Error> { @@ -596,7 +596,7 @@ async fn passive_background_handler( match shared_state.clone().try_lock_owned() { Ok(lock) => { // here, any local requests will be blocked until an external packet gets received OR local signals background to stop; - let packet = tokio::select! { + let packet = citadel_io::tokio::select! { res0 = channel.recv_serialized::() => res0?, res1 = active_to_background_rx.recv() => { // in the case local tries ot make an outgoing request, we will stop listening in the background @@ -658,7 +658,7 @@ async fn passive_background_handler( } }; - tokio::select! { + citadel_io::tokio::select! { res0 = background_task => res0, _res1 = stop_rx => Ok(()) } @@ -845,6 +845,7 @@ mod tests { use std::sync::Arc; use crate::sync::test_utils::create_streams; + use citadel_io::tokio; #[tokio::test] async fn many_writes() { @@ -856,13 +857,13 @@ mod tests { let init_value = 1000u64; let final_value = 1001u64; - let (client_done_tx, client_done_rx) = tokio::sync::oneshot::channel::<()>(); - let (server_done_tx, server_done_rx) = tokio::sync::oneshot::channel::<()>(); + let (client_done_tx, client_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (server_done_tx, server_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); let server_ref = Arc::new(AtomicU64::new(init_value)); let client_ref = server_ref.clone(); - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { let rwlock = &server_stream.rwlock(Some(init_value)).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on server"); client_done_rx.await.unwrap(); @@ -884,7 +885,7 @@ mod tests { server_done_tx.send(()).unwrap(); }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let rwlock = &client_stream.rwlock::(None).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on client"); let mut guard = rwlock.write().await.unwrap(); @@ -906,7 +907,7 @@ mod tests { server_done_rx.await.unwrap(); }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); r0.unwrap(); r1.unwrap(); } @@ -921,15 +922,15 @@ mod tests { let init_value = 1000u64; let final_value = 1001u64; - let (client_done_tx, client_done_rx) = tokio::sync::oneshot::channel::<()>(); - let (server_done_tx0, server_done_rx0) = tokio::sync::oneshot::channel::<()>(); - let (server_done_tx, server_done_rx) = tokio::sync::oneshot::channel::<()>(); - let (client_done_tx2, client_done_rx2) = tokio::sync::oneshot::channel::<()>(); + let (client_done_tx, client_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (server_done_tx0, server_done_rx0) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (server_done_tx, server_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (client_done_tx2, client_done_rx2) = citadel_io::tokio::sync::oneshot::channel::<()>(); let server_ref = Arc::new(AtomicU64::new(init_value)); let client_ref = server_ref.clone(); - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { let rwlock = &server_stream.rwlock(Some(init_value)).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on server"); client_done_rx.await.unwrap(); @@ -954,7 +955,7 @@ mod tests { client_done_rx2.await.unwrap(); }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let rwlock = &client_stream.rwlock::(None).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on client"); let mut guard = rwlock.write().await.unwrap(); @@ -980,12 +981,12 @@ mod tests { client_done_tx2.send(()).unwrap(); }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); r0.unwrap(); r1.unwrap(); } - #[tokio::test] + #[citadel_io::tokio::test] async fn many_reads_no_initial_write() { citadel_logging::setup_log(); @@ -993,9 +994,9 @@ mod tests { const COUNT: u64 = 1000; - let (client_done_tx, client_done_rx) = tokio::sync::oneshot::channel::<()>(); + let (client_done_tx, client_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { let rwlock = &server_stream.rwlock::(Some(1000)).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on server"); @@ -1004,7 +1005,7 @@ mod tests { for idx in 0..COUNT { log::trace!(target: "citadel", "Server obtaining lock {}", idx); let lock = rwlock.read().await.unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(1)).await; + citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Server obtained read lock {} w/val {:?}", idx, &*lock); reads.push(lock); } @@ -1012,7 +1013,7 @@ mod tests { client_done_rx.await.unwrap(); }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let rwlock = &client_stream.rwlock::(None).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on client"); let mut reads = Vec::new(); @@ -1020,7 +1021,7 @@ mod tests { for idx in 0..COUNT { log::trace!(target: "citadel", "Client obtaining lock {}", idx); let lock = rwlock.read().await.unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(1)).await; + citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Client obtained read lock {} w/val {:?}", idx, &*lock); reads.push(lock); } @@ -1031,12 +1032,12 @@ mod tests { client_done_tx.send(()).unwrap(); }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); r0.unwrap(); r1.unwrap(); } - #[tokio::test] + #[citadel_io::tokio::test] async fn many_reads_end_with_write() { citadel_logging::setup_log(); @@ -1044,8 +1045,8 @@ mod tests { const COUNT: u64 = 1000; - let (client_done_tx, client_done_rx) = tokio::sync::oneshot::channel::<()>(); - let (server_done_tx, server_done_rx) = tokio::sync::oneshot::channel::<()>(); + let (client_done_tx, client_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (server_done_tx, server_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); let init_value = 1000u64; let final_value = 1001u64; @@ -1053,7 +1054,7 @@ mod tests { let server_ref = Arc::new(AtomicU64::new(init_value)); let client_ref = server_ref.clone(); - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { let rwlock = &server_stream.rwlock::(Some(1000)).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on server"); @@ -1062,7 +1063,7 @@ mod tests { for idx in 0..COUNT { log::trace!(target: "citadel", "Server obtaining lock {}", idx); let lock = rwlock.read().await.unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(1)).await; + citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Server obtained read lock {} w/val {:?}", idx, &*lock); reads.push(lock); } @@ -1077,7 +1078,7 @@ mod tests { server_done_tx.send(()).unwrap(); }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let rwlock = &client_stream.rwlock::(None).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on client"); let mut reads = Vec::new(); @@ -1085,7 +1086,7 @@ mod tests { for idx in 0..COUNT { log::trace!(target: "citadel", "Client obtaining lock {}", idx); let lock = rwlock.read().await.unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(1)).await; + citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Client obtained read lock {} w/val {:?}", idx, &*lock); reads.push(lock); } @@ -1103,12 +1104,12 @@ mod tests { server_done_rx.await.unwrap(); }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); r0.unwrap(); r1.unwrap(); } - #[tokio::test] + #[citadel_io::tokio::test] async fn many_interweaved() { citadel_logging::setup_log(); @@ -1116,15 +1117,15 @@ mod tests { const COUNT: u64 = 1000; - let (init_tx, init_rx) = tokio::sync::oneshot::channel::<()>(); - let (init2_tx, init2_rx) = tokio::sync::oneshot::channel::<()>(); - let (client_done_tx, client_done_rx) = tokio::sync::oneshot::channel::<()>(); - let (server_done_tx, server_done_rx) = tokio::sync::oneshot::channel::<()>(); + let (init_tx, init_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (init2_tx, init2_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (client_done_tx, client_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); + let (server_done_tx, server_done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); let server_ref = Arc::new(AtomicU64::new(0)); let client_ref = server_ref.clone(); - let server = tokio::spawn(async move { + let server = citadel_io::tokio::spawn(async move { let rwlock = &server_stream.rwlock::(Some(0)).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on server"); init_tx.send(()).unwrap(); @@ -1135,13 +1136,13 @@ mod tests { log::trace!(target: "citadel", "Server obtaining lock {}", idx); if do_read { let lock = rwlock.read().await.unwrap(); - //tokio::time::sleep(std::time::Duration::from_millis(1)).await; + //citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Server obtained read lock {} w/val {:?}", idx, &*lock); do_read = false; assert_eq!(server_ref.load(Ordering::Relaxed), *lock); } else { let mut lock = rwlock.write().await.unwrap(); - //tokio::time::sleep(std::time::Duration::from_millis(1)).await; + //citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Server obtained write lock {} w/val {:?}", idx, &*lock); do_read = true; *lock = idx; @@ -1153,7 +1154,7 @@ mod tests { server_done_tx.send(()).unwrap(); }); - let client = tokio::spawn(async move { + let client = citadel_io::tokio::spawn(async move { let rwlock = &client_stream.rwlock::(None).await.unwrap(); log::trace!(target: "citadel", "Success establishing rwlock on client"); init_rx.await.unwrap(); @@ -1164,13 +1165,13 @@ mod tests { for idx in 0..COUNT { if do_read { let lock = rwlock.read().await.unwrap(); - //tokio::time::sleep(std::time::Duration::from_millis(1)).await; + //citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Server obtained read lock {} w/val {:?}", idx, &*lock); do_read = false; assert_eq!(client_ref.load(Ordering::Relaxed), *lock); } else { let mut lock = rwlock.write().await.unwrap(); - //tokio::time::sleep(std::time::Duration::from_millis(1)).await; + //citadel_io::tokio::time::sleep(std::time::Duration::from_millis(1)).await; log::trace!(target: "citadel", "****Server obtained write lock {} w/val {:?}", idx, &*lock); do_read = true; *lock = idx; @@ -1182,7 +1183,7 @@ mod tests { server_done_rx.await.unwrap(); }); - let (r0, r1) = tokio::join!(server, client); + let (r0, r1) = citadel_io::tokio::join!(server, client); r0.unwrap(); r1.unwrap(); } diff --git a/netbeam/src/sync/subscription.rs b/netbeam/src/sync/subscription.rs index 085e2f330..467e63b20 100644 --- a/netbeam/src/sync/subscription.rs +++ b/netbeam/src/sync/subscription.rs @@ -6,10 +6,10 @@ use crate::sync::network_application::{ use crate::sync::RelativeNodeType; use async_trait::async_trait; use bytes::Bytes; +use citadel_io::tokio::sync::mpsc::UnboundedReceiver; +use citadel_io::tokio::sync::Mutex; use citadel_io::RwLock; use std::collections::HashMap; -use tokio::sync::mpsc::UnboundedReceiver; -use tokio::sync::Mutex; #[async_trait] pub trait SubscriptionBiStream: Send + Sync { @@ -108,7 +108,7 @@ pub(crate) fn close_sequence_for_multiplexed_bistream< } // the runtime may not exist while dropping - if let Ok(rt) = tokio::runtime::Handle::try_current() { + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { rt.spawn(async move { if let Err(err) = PostActionSync::new(&ptr, id).await { log::warn!(target: "citadel", "[MetaActionSync/close] error: {:?}", err.to_string()) diff --git a/netbeam/src/sync/sync_start.rs b/netbeam/src/sync/sync_start.rs index 84841f731..75157adc6 100644 --- a/netbeam/src/sync/sync_start.rs +++ b/netbeam/src/sync/sync_start.rs @@ -155,7 +155,7 @@ where conn.send_serialized::>(SyncPacket::

::Ack(sync_time)) .await?; - tokio::time::sleep(Duration::from_nanos(rtt as _)).await; + citadel_io::tokio::time::sleep(Duration::from_nanos(rtt as _)).await; log::trace!(target: "citadel", "[Sync] Executing provided subroutine for receiver ..."); Ok((future)(payload_recv).await) } @@ -178,7 +178,7 @@ where if sync_time > now { let delta = i64::abs(sync_time - tt.get_global_time_ns()); - tokio::time::sleep(Duration::from_nanos(delta as _)).await; + citadel_io::tokio::time::sleep(Duration::from_nanos(delta as _)).await; } log::trace!(target: "citadel", "[Sync] Executing provided subroutine for initiator ..."); @@ -192,6 +192,7 @@ where mod tests { use crate::sync::test_utils::create_streams; use crate::time_tracker::TimeTracker; + use citadel_io::tokio; use futures::{FutureExt, StreamExt}; #[tokio::test] @@ -226,7 +227,10 @@ mod tests { res }; - let (server, client) = (citadel_io::spawn(server), citadel_io::spawn(client)); + let (server, client) = ( + citadel_io::tokio::task::spawn(server), + citadel_io::tokio::task::spawn(client), + ); let joined = futures::future::join(server, client).then(|(res0, res1)| async move { let (res0, res1) = (res0.unwrap(), res1.unwrap()); @@ -234,7 +238,7 @@ mod tests { tx.unbounded_send(()).unwrap(); }); - citadel_io::spawn(joined); + citadel_io::tokio::task::spawn(joined); } rx.take(COUNT).collect::<()>().await; @@ -258,9 +262,9 @@ mod tests { res }; - let server = tokio::spawn(server); - let client = tokio::spawn(client); - let (res0, res1) = tokio::join!(server, client); + let server = citadel_io::tokio::spawn(server); + let client = citadel_io::tokio::spawn(client); + let (res0, res1) = citadel_io::tokio::join!(server, client); res0.unwrap(); res1.unwrap(); } diff --git a/netbeam/src/sync/tracked_callback_channel.rs b/netbeam/src/sync/tracked_callback_channel.rs index f20c51803..be6ab9caf 100644 --- a/netbeam/src/sync/tracked_callback_channel.rs +++ b/netbeam/src/sync/tracked_callback_channel.rs @@ -1,3 +1,4 @@ +use citadel_io::tokio::sync::mpsc::{Receiver, Sender}; use citadel_io::Mutex; use futures::Stream; use std::collections::HashMap; @@ -7,7 +8,6 @@ use std::pin::Pin; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use std::task::{Context, Poll}; -use tokio::sync::mpsc::{Receiver, Sender}; pub struct TrackedCallbackChannel { inner: Arc>, @@ -57,7 +57,7 @@ impl Debug for TrackedCallbackError { } struct TrackedCallbackChannelInner { - map: Mutex>>, + map: Mutex>>, to_channel: Sender>, id: AtomicU64, } @@ -84,7 +84,7 @@ impl TrackedCallbackChannelPayload { impl TrackedCallbackChannel { pub fn new(buffer: usize) -> (Self, CallbackReceiver) { - let (to_channel, from_channel) = tokio::sync::mpsc::channel(buffer); + let (to_channel, from_channel) = citadel_io::tokio::sync::mpsc::channel(buffer); ( Self { inner: Arc::new(TrackedCallbackChannelInner { @@ -101,7 +101,7 @@ impl TrackedCallbackChannel { pub async fn send(&self, payload: T) -> Result> { let (rx, id) = { - let (tx, rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); let next_value = self.inner.id.fetch_add(1, Ordering::Relaxed); self.inner.map.lock().insert(next_value, tx); (rx, next_value) @@ -164,6 +164,7 @@ impl Stream for CallbackReceiver { #[cfg(test)] mod tests { use crate::sync::tracked_callback_channel::{TrackedCallbackChannel, TrackedCallbackError}; + use citadel_io::tokio; use futures::StreamExt; #[tokio::test] @@ -192,10 +193,10 @@ mod tests { } }; - let server = tokio::spawn(server); - let client = tokio::spawn(client); + let server = citadel_io::tokio::spawn(server); + let client = citadel_io::tokio::spawn(client); - let (_, _) = tokio::join!(server, client); + let (_, _) = citadel_io::tokio::join!(server, client); } #[tokio::test] @@ -224,10 +225,10 @@ mod tests { } }; - let server = tokio::spawn(server); - let client = tokio::spawn(client); + let server = citadel_io::tokio::spawn(server); + let client = citadel_io::tokio::spawn(client); - let (_, _) = tokio::join!(server, client); + let (_, _) = citadel_io::tokio::join!(server, client); } #[test] From 7789a6976fec955379fc5f32aad471a5795500f9 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Mon, 25 Nov 2024 22:04:42 -0500 Subject: [PATCH 02/12] chore: rebase onto main --- .cargo/config.toml | 3 +- Makefile.toml | 5 +++ async_ip/src/lib.rs | 1 - citadel_crypt/tests/primary.rs | 34 ------------------- citadel_pqcrypto/Cargo.toml | 1 - citadel_pqcrypto/src/lib.rs | 8 ----- citadel_proto/src/proto/misc/lock_holder.rs | 2 +- .../src/proto/misc/underlying_proto.rs | 2 +- citadel_proto/src/proto/node_request.rs | 1 - .../packet_processor/peer/peer_cmd_packet.rs | 10 +++--- .../src/proto/peer/p2p_conn_handler.rs | 8 +++-- citadel_proto/src/proto/session_manager.rs | 3 +- citadel_proto/src/proto/state_container.rs | 3 +- citadel_proto/src/proto/validation.rs | 2 +- citadel_sdk/src/builder/node_builder.rs | 1 + citadel_sdk/src/fs.rs | 13 +++---- citadel_sdk/src/prefabs/client/mod.rs | 1 - .../src/prefabs/client/single_connection.rs | 9 +++-- .../server/accept_file_transfer_kernel.rs | 2 +- citadel_sdk/src/remote_ext.rs | 2 +- citadel_sdk/tests/stress_tests.rs | 29 ---------------- citadel_types/src/crypto/mod.rs | 10 ++++-- .../src/backend/filesystem_backend.rs | 1 - citadel_user/src/backend/mod.rs | 4 +-- citadel_wire/Cargo.toml | 1 + citadel_wire/src/error.rs | 1 - .../src/standard/nat_identification.rs | 14 ++------ citadel_wire/src/standard/socket_helpers.rs | 4 ++- .../src/udp_traversal/linear/method3.rs | 4 +-- citadel_wire/src/udp_traversal/linear/mod.rs | 3 +- citadel_wire/src/udp_traversal/multi/mod.rs | 14 ++++---- .../targetted_udp_socket_addr.rs | 2 +- .../src/udp_traversal/udp_hole_puncher.rs | 8 ++--- netbeam/src/sync/sync_start.rs | 2 +- 34 files changed, 65 insertions(+), 143 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 382626f86..75e9a472b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,4 +3,5 @@ rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+threads,+net"] [profile.wasix] opt-level = 3 -inherits = "dev" \ No newline at end of file +inherits = "dev" +rustflags = ["-Z", "unstable-options"] \ No newline at end of file diff --git a/Makefile.toml b/Makefile.toml index bf1cdfe28..839a16f85 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -290,6 +290,11 @@ args = ["test", "--package", "citadel_sdk", "--doc"] command = "cargo" args = ["install", "cargo-wasix"] +[tasks.wasix-fix] +description = "If you have a wasix error, run this task to potentially fix it" +command = "cargo" +args = ["wasix", "download-toolchain"] + [tasks.wasix] command = "cargo" args = ["--config=Cargo.wasix.toml", "wasix", "${@}", "--profile=wasix"] diff --git a/async_ip/src/lib.rs b/async_ip/src/lib.rs index e4a3ae308..cff509c9f 100644 --- a/async_ip/src/lib.rs +++ b/async_ip/src/lib.rs @@ -14,7 +14,6 @@ use async_trait::async_trait; use auto_impl::auto_impl; #[cfg(not(target_family = "wasm"))] use reqwest::Client; -use std::fmt::Display; use std::fmt::Formatter; use std::net::IpAddr; #[cfg(not(target_family = "wasm"))] diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index 3de3327e3..e124ab389 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -1035,31 +1035,6 @@ mod tests { ); } - #[should_panic(expected = "EncryptionFailure")] - #[rstest] - #[case( - EncryptionAlgorithm::AES_GCM_256, - KemAlgorithm::Kyber, - SigAlgorithm::None - )] - fn test_drill_encrypt_decrypt_basic_bad_psks( - #[case] enx: EncryptionAlgorithm, - #[case] kem: KemAlgorithm, - #[case] sig: SigAlgorithm, - ) { - citadel_logging::setup_log_no_panic_hook(); - test_harness_with_psks( - enx + kem + sig, - &PRE_SHARED_KEYS, - &PRE_SHARED_KEYS2, - |alice, bob, _, data| { - let encrypted = alice.encrypt(data).unwrap(); - let decrypted = bob.decrypt(encrypted).unwrap(); - assert_eq!(decrypted, data); - }, - ); - } - #[rstest] #[case( EncryptionAlgorithm::AES_GCM_256, @@ -1136,15 +1111,6 @@ mod tests { test_harness_with_psks(params, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, fx); } - fn test_harness_with_psks( - params: CryptoParameters, - bob_psks: &[Vec], - alice_psks: &[Vec], - fx: impl Fn(&StackedRatchet, &StackedRatchet, SecurityLevel, &[u8]), - ) { - test_harness_with_psks(params, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, fx); - } - fn test_harness_with_psks( params: CryptoParameters, bob_psks: &[Vec], diff --git a/citadel_pqcrypto/Cargo.toml b/citadel_pqcrypto/Cargo.toml index 6e66dcec0..99186f6a6 100644 --- a/citadel_pqcrypto/Cargo.toml +++ b/citadel_pqcrypto/Cargo.toml @@ -17,7 +17,6 @@ exclude = [ [features] default = ["std"] -unordered = [] std = [ "serde/std", "aes-gcm/std", diff --git a/citadel_pqcrypto/src/lib.rs b/citadel_pqcrypto/src/lib.rs index 2ba534fb4..86a72a522 100644 --- a/citadel_pqcrypto/src/lib.rs +++ b/citadel_pqcrypto/src/lib.rs @@ -44,18 +44,10 @@ pub mod constructor_opts; pub mod wire; -/// For debug purposes -#[cfg(not(feature = "unordered"))] pub const fn build_tag() -> &'static str { "ordered" } -/// For debug purposes -#[cfg(feature = "unordered")] -pub const fn build_tag() -> &'static str { - "unordered" -} - /// Returns the approximate size of each PQC. This is approximately true for the core NIST round-3 algorithms, but not necessarily true for the SIKE algos pub const fn get_approx_bytes_per_container() -> usize { 2000 diff --git a/citadel_proto/src/proto/misc/lock_holder.rs b/citadel_proto/src/proto/misc/lock_holder.rs index 334e45bfa..2135b6d61 100644 --- a/citadel_proto/src/proto/misc/lock_holder.rs +++ b/citadel_proto/src/proto/misc/lock_holder.rs @@ -75,7 +75,7 @@ impl<'a, T: 'a> LockHolder<'a, T> { LockHolder::new(self.inner.as_ref()) } - pub fn map U>(&'a self, transform: F) -> LockHolder<'_, U> { + pub fn map U>(&'a self, transform: F) -> LockHolder<'a, U> { LockHolder::new(self.inner.as_ref().map(transform)) } } diff --git a/citadel_proto/src/proto/misc/underlying_proto.rs b/citadel_proto/src/proto/misc/underlying_proto.rs index c2bc6a822..88a214dbe 100644 --- a/citadel_proto/src/proto/misc/underlying_proto.rs +++ b/citadel_proto/src/proto/misc/underlying_proto.rs @@ -51,7 +51,7 @@ impl ServerUnderlyingProtocol { /// Creates a new [`ServerUnderlyingProtocol`] with a preset [`std::net::TcpListener`] pub fn from_std_tcp_listener(listener: TcpListener) -> Result { listener.set_nonblocking(true)?; - Self::from_tokio_tcp_listener(tokio::net::TcpListener::from_std(listener)?) + Self::from_tokio_tcp_listener(citadel_io::tokio::net::TcpListener::from_std(listener)?) } /// Creates a new [`ServerUnderlyingProtocol`] with a preset [`citadel_io::tokio::net::TcpListener`] diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs index b93be8a06..4032e07d9 100644 --- a/citadel_proto/src/proto/node_request.rs +++ b/citadel_proto/src/proto/node_request.rs @@ -1,7 +1,6 @@ use crate::auth::AuthenticationRequest; use crate::prelude::{GroupBroadcast, PeerSignal, VirtualTargetType}; use crate::proto::state_container::VirtualConnectionType; -use crate::re_imports::openssl::sha::sha256; use citadel_crypt::streaming_crypt_scrambler::ObjectSource; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::TransferType; diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index 8acb81143..4f1f3721f 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -29,7 +29,7 @@ use crate::proto::peer::hole_punch_compat_sink_stream::ReliableOrderedCompatStre use crate::proto::peer::p2p_conn_handler::attempt_simultaneous_hole_punch; use crate::proto::peer::peer_crypt::{KeyExchangeProcess, PeerNatInfo}; use crate::proto::peer::peer_layer::{ - NodeConnectionType, PeerConnectionType, PeerResponse, PeerSignal, + HyperNodePeerLayerInner, NodeConnectionType, PeerConnectionType, PeerResponse, PeerSignal, }; use crate::proto::remote::Ticket; use crate::proto::session_manager::HdpSessionManager; @@ -1010,8 +1010,8 @@ async fn process_signal_command_as_server( { log::info!(target: "citadel", "Simultaneous register detected! Simulating implicated_cid={} sent an accept_register to target={}", implicated_cid, target_cid); peer_layer.insert_mapped_ticket(implicated_cid, ticket_new, ticket); - // route signal to peer drop(peer_layer); + // route signal to peer let _ = super::server::post_register::handle_response_phase_post_register( peer_conn_type, @@ -1058,7 +1058,6 @@ async fn process_signal_command_as_server( let to_primary_stream = return_if_none!(session.to_primary_stream.clone()); let sess_mgr = session.session_manager.clone(); - drop(peer_layer); route_signal_and_register_ticket_forwards( PeerSignal::PostRegister { peer_conn_type, @@ -1130,7 +1129,7 @@ async fn process_signal_command_as_server( peer_layer_lock .insert_tracked_posting( implicated_cid, - Duration::from_secs(60), + Duration::from_secs(60 * 60), ticket, PeerSignal::DeregistrationSuccess { peer_conn_type }, |_| {}, @@ -1257,7 +1256,6 @@ async fn process_signal_command_as_server( .await?; Ok(PrimaryProcessorResult::Void) } else { - drop(peer_layer); route_signal_and_register_ticket_forwards( PeerSignal::PostConnect { peer_conn_type, @@ -1674,7 +1672,7 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( let to_primary_stream = to_primary_stream.clone(); // Give the target_cid 10 seconds to respond - let res = sess_mgr.route_signal_primary(implicated_cid, target_cid, ticket, signal.clone(), move |peer_hyper_ratchet| { + let res = sess_mgr.route_signal_primary(peer_layer, implicated_cid, target_cid, ticket, signal.clone(), move |peer_hyper_ratchet| { packet_crafter::peer_cmd::craft_peer_signal(peer_hyper_ratchet, signal.clone(), ticket, timestamp, security_level) }, timeout, move |stale_signal| { // on timeout, run this diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index 52f7231c3..90c30ccb5 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -322,9 +322,11 @@ pub(crate) async fn attempt_simultaneous_hole_punch( // TODO: Replace with biconn channel logic citadel_io::tokio::time::sleep(Duration::from_millis(200)).await; let socket = hole_punched_socket.into_socket(); - let quic_endpoint = - citadel_wire::quic::QuicClient::new_with_rustls_config(socket, client_config.clone()) - .map_err(generic_error)?; + let quic_endpoint = citadel_wire::quic::QuicClient::new_with_rustls_config( + socket, + client_config.clone(), + ) + .map_err(generic_error)?; let p2p_stream = Node::quic_p2p_connect_defaults( quic_endpoint.endpoint, None, diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index e687abab2..15a20e846 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -31,7 +31,8 @@ use crate::proto::packet_processor::includes::{Duration, Instant}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::peer::peer_layer::{ - HyperNodePeerLayer, MailboxTransfer, PeerConnectionType, PeerResponse, PeerSignal, + HyperNodePeerLayer, HyperNodePeerLayerInner, MailboxTransfer, PeerConnectionType, PeerResponse, + PeerSignal, }; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::{ diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index e978e5da0..c284db01e 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -821,7 +821,6 @@ impl StateContainerInner { endpoint_crypto: PeerSessionCrypto, sess: &CitadelSession, file_transfer_compatible: bool, - file_transfer_compatible: bool, ) -> PeerChannel { let (channel_tx, channel_rx) = unbounded(); let (tx, rx) = crate::proto::outbound_sender::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); @@ -1150,7 +1149,7 @@ impl StateContainerInner { }; let (start_recv_tx, start_recv_rx) = if !is_revfs_pull { - let (tx, rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); (Some(tx), Some(rx)) } else { (None, None) diff --git a/citadel_proto/src/proto/validation.rs b/citadel_proto/src/proto/validation.rs index e3c8ec72d..c66e60553 100644 --- a/citadel_proto/src/proto/validation.rs +++ b/citadel_proto/src/proto/validation.rs @@ -438,7 +438,7 @@ pub(crate) mod aead { proper_hr: StackedRatchet, header: &'b H, mut payload: BytesMut, - ) -> Option { + ) -> Option> { let header_bytes = header.as_ref(); let header = Ref::new(header_bytes)? as Ref<&[u8], HdpHeader>; proper_hr diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs index 5967718bc..839a59fd3 100644 --- a/citadel_sdk/src/builder/node_builder.rs +++ b/citadel_sdk/src/builder/node_builder.rs @@ -260,6 +260,7 @@ impl NodeBuilder { } /// The file should be a DER formatted certificate + #[cfg(feature = "std")] pub async fn with_pem_file>(&mut self, path: P) -> anyhow::Result<&mut Self> { let mut der = std::io::Cursor::new(citadel_io::tokio::fs::read(path).await?); let certs = citadel_proto::re_imports::rustls_pemfile::certs(&mut der).collect::>(); diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index 516dff347..e41cd8325 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -87,7 +87,6 @@ mod tests { use crate::prelude::*; use crate::test_common::wait_for_peers; use citadel_io::tokio; - use futures::StreamExt; use rstest::rstest; use std::net::SocketAddr; use std::path::PathBuf; @@ -328,19 +327,15 @@ mod tests { assert!(client_success.load(Ordering::Relaxed)); } - #[rstest] - #[case(SecrecyMode::BestEffort)] - #[timeout(Duration::from_secs(60))] #[citadel_io::tokio::test(flavor = "multi_thread")] - async fn test_p2p_file_transfer_revfs( - #[case] secrecy_mode: SecrecyMode, - #[values(KemAlgorithm::Kyber)] kem: KemAlgorithm, - #[values(EncryptionAlgorithm::AES_GCM_256)] enx: EncryptionAlgorithm, - ) { + async fn test_p2p_file_transfer_revfs() { citadel_logging::setup_log(); crate::test_common::TestBarrier::setup(2); let client0_success = &AtomicBool::new(false); let client1_success = &AtomicBool::new(false); + let enx = EncryptionAlgorithm::AES_GCM_256; + let secrecy_mode = SecrecyMode::BestEffort; + let kem = KemAlgorithm::Kyber; let (server, server_addr) = crate::test_common::server_info(); diff --git a/citadel_sdk/src/prefabs/client/mod.rs b/citadel_sdk/src/prefabs/client/mod.rs index 906f7b6b7..5692a6773 100644 --- a/citadel_sdk/src/prefabs/client/mod.rs +++ b/citadel_sdk/src/prefabs/client/mod.rs @@ -38,7 +38,6 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a>: Sized + 'a { let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); let server_conn_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - server_password, |connect_success, remote| { on_channel_received_fn::<_, Self>( connect_success, diff --git a/citadel_sdk/src/prefabs/client/single_connection.rs b/citadel_sdk/src/prefabs/client/single_connection.rs index 972f6e677..70701bbd1 100644 --- a/citadel_sdk/src/prefabs/client/single_connection.rs +++ b/citadel_sdk/src/prefabs/client/single_connection.rs @@ -23,7 +23,8 @@ pub struct SingleClientServerConnectionKernel { remote: Option, server_password: Option, rx_incoming_object_transfer_handle: Mutex>, - tx_incoming_object_transfer_handle: tokio::sync::mpsc::UnboundedSender, + tx_incoming_object_transfer_handle: + citadel_io::tokio::sync::mpsc::UnboundedSender, // by using fn() -> Fut, the future does not need to be Sync _pd: PhantomData Fut>, } @@ -52,10 +53,10 @@ where Fut: Future> + Send, { fn generate_object_transfer_handle() -> ( - tokio::sync::mpsc::UnboundedSender, + citadel_io::tokio::sync::mpsc::UnboundedSender, Mutex>, ) { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let rx = FileTransferHandleRx { inner: rx, conn_type: VirtualTargetType::LocalGroupServer { implicated_cid: 0 }, @@ -110,7 +111,6 @@ where tx_incoming_object_transfer_handle, server_password, remote: None, - server_password, _pd: Default::default(), } } @@ -325,7 +325,6 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( client_settings, - None, |channel, remote| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); wait_for_peers().await; diff --git a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs index 382d95ab3..2861fb195 100644 --- a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs +++ b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs @@ -33,7 +33,7 @@ impl NetKernel for AcceptFileTransferKernel { pub fn exhaust_file_transfer(mut handle: ObjectTransferHandler) { // Exhaust the stream - let handle = tokio::task::spawn(async move { + let handle = citadel_io::tokio::task::spawn(async move { while let Some(evt) = handle.next().await { log::info!(target: "citadel", "File Transfer Event: {evt:?}"); if let ObjectTransferStatus::Fail(err) = &evt { diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index 210c86c4c..b300dd9b2 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -1357,7 +1357,7 @@ mod tests { KemAlgorithm::Kyber, SigAlgorithm::Falcon1024 )] - #[citadel_io::timeout(std::time::Duration::from_secs(90))] + #[timeout(std::time::Duration::from_secs(90))] #[tokio::test] async fn test_c2s_file_transfer( #[case] enx: EncryptionAlgorithm, diff --git a/citadel_sdk/tests/stress_tests.rs b/citadel_sdk/tests/stress_tests.rs index 94deb789a..3a9416a61 100644 --- a/citadel_sdk/tests/stress_tests.rs +++ b/citadel_sdk/tests/stress_tests.rs @@ -216,11 +216,6 @@ mod tests { enx: EncryptionAlgorithm, ) { citadel_logging::setup_log(); - - if windows_pipeline_check(kem, secrecy_mode) { - return; - } - citadel_sdk::test_common::TestBarrier::setup(2); static CLIENT_SUCCESS: AtomicBool = AtomicBool::new(false); static SERVER_SUCCESS: AtomicBool = AtomicBool::new(false); @@ -290,11 +285,6 @@ mod tests { #[values(EncryptionAlgorithm::Kyber)] enx: EncryptionAlgorithm, ) { citadel_logging::setup_log(); - - if windows_pipeline_check(kem, secrecy_mode) { - return; - } - citadel_sdk::test_common::TestBarrier::setup(2); static CLIENT_SUCCESS: AtomicBool = AtomicBool::new(false); static SERVER_SUCCESS: AtomicBool = AtomicBool::new(false); @@ -377,11 +367,6 @@ mod tests { enx: EncryptionAlgorithm, ) { citadel_logging::setup_log(); - - if windows_pipeline_check(kem, secrecy_mode) { - return; - } - citadel_sdk::test_common::TestBarrier::setup(2); let client0_success = &AtomicBool::new(false); let client1_success = &AtomicBool::new(false); @@ -563,18 +548,4 @@ mod tests { assert!(res.is_ok()); assert_eq!(CLIENT_SUCCESS.load(Ordering::Relaxed), peer_count); } - - /// This test is disabled by default because it is very slow and requires a lot of resources - fn windows_pipeline_check(kem: KemAlgorithm, secrecy_mode: SecrecyMode) -> bool { - if cfg!(windows) - && kem == KemAlgorithm::Ntru - && secrecy_mode == SecrecyMode::Perfect - && std::env::var("IN_CI").is_ok() - { - log::warn!(target: "citadel", "Skipping NTRU/Perfect forward secrecy test on Windows due to performance issues"); - true - } else { - false - } - } } diff --git a/citadel_types/src/crypto/mod.rs b/citadel_types/src/crypto/mod.rs index 3ecd7a410..84e920d1b 100644 --- a/citadel_types/src/crypto/mod.rs +++ b/citadel_types/src/crypto/mod.rs @@ -1,6 +1,6 @@ use crate::utils; use crate::utils::validate_crypto_params; -use bytes::BytesMut; +use bytes::{Bytes, BytesMut}; use packed_struct::derive::{PackedStruct, PrimitiveEnum_u8}; use packed_struct::{PackedStruct, PrimitiveEnum}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -90,7 +90,7 @@ impl Default for SecrecyMode { /// A memory-secure wrapper for shipping around Bytes pub struct SecBuffer { - inner: BytesMut, + pub inner: BytesMut, } impl SecBuffer { @@ -135,6 +135,10 @@ impl SecBuffer { &self.inner[..] } + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -185,7 +189,7 @@ impl AsMut<[u8]> for SecBuffer { impl From> for SecBuffer { fn from(inner: Vec) -> Self { - Self::from(&inner[..]) + Self::from(BytesMut::from(Bytes::from(inner))) } } diff --git a/citadel_user/src/backend/filesystem_backend.rs b/citadel_user/src/backend/filesystem_backend.rs index 6c3346b78..fda0831a1 100644 --- a/citadel_user/src/backend/filesystem_backend.rs +++ b/citadel_user/src/backend/filesystem_backend.rs @@ -12,7 +12,6 @@ use citadel_crypt::stacked_ratchet::Ratchet; use citadel_crypt::streaming_crypt_scrambler::ObjectSource; use citadel_io::tokio; use citadel_io::tokio_stream::StreamExt; -use citadel_types::crypto::SecurityLevel; use citadel_types::proto::{ObjectTransferStatus, TransferType, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; use std::collections::HashMap; diff --git a/citadel_user/src/backend/mod.rs b/citadel_user/src/backend/mod.rs index 6573bd27d..d37241ae0 100644 --- a/citadel_user/src/backend/mod.rs +++ b/citadel_user/src/backend/mod.rs @@ -14,10 +14,10 @@ use crate::backend::sql_backend::SqlConnectionOptions; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; use citadel_crypt::streaming_crypt_scrambler::ObjectSource; +use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user; use citadel_types::user::MutualPeer; -use tokio::sync::mpsc::UnboundedSender; /// Implementation for the default filesystem backend #[cfg(feature = "filesystem")] @@ -289,7 +289,7 @@ pub trait BackendConnection: Send + Sync { /// Streams an object to the backend async fn stream_object_to_backend( &self, - source: tokio::sync::mpsc::UnboundedReceiver>, + source: UnboundedReceiver>, sink_metadata: &VirtualObjectMetadata, status_tx: UnboundedSender, ) -> Result<(), AccountError>; diff --git a/citadel_wire/Cargo.toml b/citadel_wire/Cargo.toml index 834d0383f..7aa4d236b 100644 --- a/citadel_wire/Cargo.toml +++ b/citadel_wire/Cargo.toml @@ -22,6 +22,7 @@ std = [ "quinn/default", "serde/std", "async_ip/std", + "rustls-pemfile/std", ] localhost-testing = ["tracing"] wasm = [ diff --git a/citadel_wire/src/error.rs b/citadel_wire/src/error.rs index 4e68eb9b1..1733ddac0 100644 --- a/citadel_wire/src/error.rs +++ b/citadel_wire/src/error.rs @@ -1,4 +1,3 @@ -use std::fmt::Formatter; use citadel_io::tokio::io::Error; use std::fmt::Formatter; diff --git a/citadel_wire/src/standard/nat_identification.rs b/citadel_wire/src/standard/nat_identification.rs index 1663be546..80da590de 100644 --- a/citadel_wire/src/standard/nat_identification.rs +++ b/citadel_wire/src/standard/nat_identification.rs @@ -121,17 +121,9 @@ impl NatType { *LOCALHOST_TESTING_NAT_TYPE.lock() = Some(nat_type.clone()); }), - Err(_elapsed) => { - log::warn!(target: "citadel", "Timeout on NAT identification occurred"); - if cfg!(feature = "localhost-testing") { - log::warn!(target: "citadel", "Will use default NatType for localhost-testing"); - Ok(NatType::offline()) - } else { - Err(FirewallError::HolePunch( - "NAT identification elapsed".to_string(), - )) - } - } + Err(_elapsed) => Err(FirewallError::HolePunch( + "NAT identification elapsed".to_string(), + )), } } diff --git a/citadel_wire/src/standard/socket_helpers.rs b/citadel_wire/src/standard/socket_helpers.rs index f92a8bbcc..64ac7faa7 100644 --- a/citadel_wire/src/standard/socket_helpers.rs +++ b/citadel_wire/src/standard/socket_helpers.rs @@ -108,7 +108,9 @@ fn get_tcp_listener_inner( setup_bind(addr, &socket, reuse)?; socket.listen(1024)?; let std_tcp_socket: std::net::TcpListener = socket.into(); - Ok(citadel_io::TcpListener::from_std(std_tcp_socket)?) + Ok(citadel_io::tokio::net::TcpListener::from_std( + std_tcp_socket, + )?) } async fn get_tcp_stream_inner( diff --git a/citadel_wire/src/udp_traversal/linear/method3.rs b/citadel_wire/src/udp_traversal/linear/method3.rs index f016f860c..90b50afb5 100644 --- a/citadel_wire/src/udp_traversal/linear/method3.rs +++ b/citadel_wire/src/udp_traversal/linear/method3.rs @@ -97,7 +97,7 @@ impl Method3 { ttl_init: 20, delta_ttl: Some(60), socket: socket_wrapper, - endpoints: &tokio::sync::Mutex::new(endpoints.iter().copied().collect()), + endpoints: &citadel_io::tokio::sync::Mutex::new(endpoints.iter().copied().collect()), encryptor, millis_delta: MILLIS_DELTA, count: 2, @@ -445,7 +445,7 @@ struct SendPacketBarrageParams<'a> { ttl_init: u32, delta_ttl: Option, socket: &'a UdpWrapper<'a>, - endpoints: &'a tokio::sync::Mutex>, + endpoints: &'a citadel_io::tokio::sync::Mutex>, encryptor: &'a HolePunchConfigContainer, millis_delta: u64, count: u32, diff --git a/citadel_wire/src/udp_traversal/linear/mod.rs b/citadel_wire/src/udp_traversal/linear/mod.rs index a34d5e585..64e5bbc25 100644 --- a/citadel_wire/src/udp_traversal/linear/mod.rs +++ b/citadel_wire/src/udp_traversal/linear/mod.rs @@ -1,11 +1,10 @@ use std::net::SocketAddr; use citadel_io::tokio::net::UdpSocket; +use citadel_io::tokio::sync::mpsc::UnboundedSender; use citadel_io::tokio::time::Duration; use either::Either; use igd::PortMappingProtocol; -use tokio::sync::mpsc::UnboundedSender; -use tokio::time::Duration; use crate::error::FirewallError; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; diff --git a/citadel_wire/src/udp_traversal/multi/mod.rs b/citadel_wire/src/udp_traversal/multi/mod.rs index 4261be13f..ad4b5cce4 100644 --- a/citadel_wire/src/udp_traversal/multi/mod.rs +++ b/citadel_wire/src/udp_traversal/multi/mod.rs @@ -4,6 +4,7 @@ use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigCon use crate::udp_traversal::linear::SingleUDPHolePuncher; use crate::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; use crate::udp_traversal::{HolePunchID, NatTraversalMethod}; +use citadel_io::tokio::sync::mpsc::UnboundedReceiver; use futures::future::select_ok; use futures::stream::FuturesUnordered; use futures::{Future, StreamExt}; @@ -18,7 +19,6 @@ use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::task::{Context, Poll}; use std::time::Duration; -use tokio::sync::mpsc::UnboundedReceiver; /// Punches a hole using IPv4/6 addrs. IPv6 is more traversal-friendly since IP-translation between external and internal is not needed (unless the NAT admins are evil) /// @@ -123,7 +123,7 @@ async fn drive( // initiate a dedicated channel for sending packets for coordination let conn = app.bi_channel::().await?; let (ref conn_tx, conn_rx) = conn.split(); - let conn_rx = &tokio::sync::Mutex::new(conn_rx); + let conn_rx = &citadel_io::tokio::sync::Mutex::new(conn_rx); log::trace!(target: "citadel", "Initiating NetMutex ..."); // setup a mutex for handling contentions @@ -174,19 +174,19 @@ async fn drive( (res, hole_puncher) }; - let task = tokio::task::spawn(task); + let task = citadel_io::tokio::task::spawn(task); futures.push(task); } - let current_enqueued: &tokio::sync::Mutex> = + let current_enqueued: &citadel_io::tokio::sync::Mutex> = &citadel_io::tokio::sync::Mutex::new(vec![]); let finished_count = &citadel_io::Mutex::new(0); let hole_puncher_count = futures.len(); - let commanded_winner = &tokio::sync::Mutex::new(None); + let commanded_winner = &citadel_io::tokio::sync::Mutex::new(None); - let (done_tx, done_rx) = tokio::sync::oneshot::channel::<()>(); + let (done_tx, done_rx) = citadel_io::tokio::sync::oneshot::channel::<()>(); let done_tx = citadel_io::Mutex::new(Some(done_tx)); let signal_done = || -> Result<(), anyhow::Error> { @@ -226,7 +226,7 @@ async fn drive( drop(lock); let loser_poller = async move { - let mut ticker = tokio::time::interval(Duration::from_millis(100)); + let mut ticker = citadel_io::tokio::time::interval(Duration::from_millis(100)); loop { ticker.tick().await; diff --git a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs b/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs index 8f4b91ff6..3f3ad375b 100644 --- a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs +++ b/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs @@ -97,7 +97,7 @@ impl HolePunchedUdpSocket { let target_addr = SocketAddr::new(send_ip, addr.port()); log::trace!(target: "citadel", "Sending packet from {bind_addr} to {target_addr}"); - tokio::time::timeout( + citadel_io::tokio::time::timeout( Duration::from_secs(2), self.socket.send_to(buf, target_addr), ) diff --git a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs index 2487cff04..2c45fa3fd 100644 --- a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs +++ b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs @@ -61,7 +61,7 @@ async fn driver( ) -> Result { let mut retries = 0; loop { - let task = tokio::time::timeout( + let task = citadel_io::tokio::time::timeout( timeout, driver_inner(conn, encrypted_config_container.clone()), ); @@ -111,9 +111,9 @@ async fn driver_inner( // exchange internal bind port, also synchronizing the beginning of the hole punch process // while doing so let peer_internal_bind_addrs = conn.sync_exchange_payload(internal_addresses).await?; - log::trace!(target: "citadel", "\n~~~~~~~~~~~~\n [driver] Local NAT type: {:?}\n Peer NAT type: {:?}", local_nat_type, peer_nat_type); - log::trace!(target: "citadel", "[driver] Local internal bind addr: {internal_bind_addr_optimal:?}\nPeer internal bind addr: {peer_internal_bind_addrs:?}"); - log::trace!(target: "citadel", "\n~~~~~~~~~~~~\n"); + log::info!(target: "citadel", "\n~~~~~~~~~~~~\n [driver] Local NAT type: {:?}\n Peer NAT type: {:?}", local_nat_type, peer_nat_type); + log::info!(target: "citadel", "[driver] Local internal bind addr: {internal_bind_addr_optimal:?}\nPeer internal bind addr: {peer_internal_bind_addrs:?}"); + log::info!(target: "citadel", "\n~~~~~~~~~~~~\n"); // the next functions takes everything insofar obtained into account without causing collisions with any existing // connections (e.g., no conflicts with the primary stream existing in conn) let hole_punch_config = HolePunchConfig::new(peer_nat_type, &peer_internal_bind_addrs, sockets); diff --git a/netbeam/src/sync/sync_start.rs b/netbeam/src/sync/sync_start.rs index 75157adc6..0f9229073 100644 --- a/netbeam/src/sync/sync_start.rs +++ b/netbeam/src/sync/sync_start.rs @@ -65,7 +65,7 @@ impl<'a, R: 'a> NetSyncStart<'a, R> { >( conn: &'a S, relative_node_type: RelativeNodeType, - ) -> NetSyncStart<()> { + ) -> NetSyncStart<'a, ()> { NetSyncStart::exchange_payload(conn, relative_node_type, ()) } } From 8b96b72704b6ff6c03fb625594f77c6ed22db743 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Thu, 28 Nov 2024 18:50:22 -0500 Subject: [PATCH 03/12] feat: massive overhaul, cleanup of repo --- .github/workflows/validate.yml | 68 +-- Cargo.toml | 51 ++- Cargo.wasix.toml | 24 +- citadel_proto/src/auth.rs | 2 +- .../packet_processor/peer/peer_cmd_packet.rs | 15 + .../packet_processor/preconnect_packet.rs | 28 +- .../src/proto/peer/p2p_conn_handler.rs | 47 +- citadel_proto/src/proto/session.rs | 46 +- citadel_proto/src/proto/session_manager.rs | 38 +- citadel_proto/src/proto/state_container.rs | 25 +- citadel_sdk/src/builder/node_builder.rs | 11 +- citadel_sdk/src/fs.rs | 65 ++- citadel_sdk/src/lib.rs | 4 +- citadel_sdk/src/prefabs/client/broadcast.rs | 4 +- citadel_sdk/src/prefabs/client/mod.rs | 35 +- .../src/prefabs/client/peer_connection.rs | 428 +++++++++++++----- .../src/prefabs/client/single_connection.rs | 29 +- citadel_sdk/src/prefabs/mod.rs | 2 +- .../server/accept_file_transfer_kernel.rs | 24 +- .../src/prefabs/server/internal_service.rs | 6 +- citadel_sdk/src/remote_ext.rs | 77 +--- citadel_sdk/tests/stress_tests.rs | 10 +- citadel_user/Cargo.toml | 2 +- citadel_user/src/backend/utils/mod.rs | 65 +++ .../src/external_services/google_auth.rs | 2 +- citadel_user/tests/crypto.rs | 6 +- example-library/Cargo.toml | 61 +++ example-library/README.md | 103 +++++ .../c2s/client_basic_transient_connection.rs | 75 +++ .../c2s/client_basic_with_server_password.rs | 85 ++++ example-library/examples/c2s/client_echo.rs | 96 ++++ example-library/examples/c2s/server_basic.rs | 49 ++ .../c2s/server_basic_with_password.rs | 70 +++ example-library/examples/c2s/server_echo.rs | 81 ++++ example-library/examples/p2p/chat.rs | 127 ++++++ example-library/examples/p2p/file_transfer.rs | 113 +++++ example-library/examples/p2p/revfs_delete.rs | 131 ++++++ .../examples/p2p/revfs_read_write.rs | 136 ++++++ example-library/examples/p2p/revfs_take.rs | 140 ++++++ keys/testing.p12 | Bin 5725 -> 0 bytes mac_m1_README.txt | 2 +- {tutorials => notes}/argon2id-params.txt | 0 .../lets-encrypt-to-pkcs12.txt | 0 43 files changed, 1982 insertions(+), 401 deletions(-) create mode 100644 example-library/Cargo.toml create mode 100644 example-library/README.md create mode 100644 example-library/examples/c2s/client_basic_transient_connection.rs create mode 100644 example-library/examples/c2s/client_basic_with_server_password.rs create mode 100644 example-library/examples/c2s/client_echo.rs create mode 100644 example-library/examples/c2s/server_basic.rs create mode 100644 example-library/examples/c2s/server_basic_with_password.rs create mode 100644 example-library/examples/c2s/server_echo.rs create mode 100644 example-library/examples/p2p/chat.rs create mode 100644 example-library/examples/p2p/file_transfer.rs create mode 100644 example-library/examples/p2p/revfs_delete.rs create mode 100644 example-library/examples/p2p/revfs_read_write.rs create mode 100644 example-library/examples/p2p/revfs_take.rs delete mode 100644 keys/testing.p12 rename {tutorials => notes}/argon2id-params.txt (100%) rename {tutorials => notes}/lets-encrypt-to-pkcs12.txt (100%) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 22951e2a5..af803f6d6 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -18,7 +18,7 @@ jobs: core_libs: strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} timeout-minutes: 80 steps: @@ -40,7 +40,7 @@ jobs: run: mysql -uroot -h127.0.0.1 -ppassword -e 'CREATE DATABASE hyxewave; CREATE DATABASE hyxewave2; set global max_connections = 1000;' - name: Augment connection size for psql if: startsWith(matrix.os, 'ubuntu') - run: psql -c 'ALTER SYSTEM SET max_connections TO 1000' postgresql://postgres:postgres@localhost/hyxewave && psql -c 'ALTER SYSTEM SET max_connections TO 1000' postgresql://postgres:postgres@localhost/hyxewave2 + run: psql -c 'ALTER SYSTEM SET max_connections TO 1000' postgresql://postgres:postgres@localhost/hyxewave && psql -c 'ALTER SYSTEM SET max_connections TO 1000' postgresql://postgres:postgres@localhost/hyxewave2 - name: Add sqlite databases if: startsWith(matrix.os, 'ubuntu') run: touch /home/runner/hyxewave.db && touch /home/runner/hyxewave2.db @@ -127,21 +127,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: Avarok-Cybersecurity/gh-actions-deps@master -# - name: Install Valgrind -# run: | -# sudo apt-get update -y -# sudo apt-get install -y valgrind -# # Compile tests -# - name: cargo build secmem_bytes_test -# run: cargo build --bin secmem_bytes_test + # - name: Install Valgrind + # run: | + # sudo apt-get update -y + # sudo apt-get install -y valgrind + # # Compile tests + # - name: cargo build secmem_bytes_test + # run: cargo build --bin secmem_bytes_test # Run with valgrind -# - name: Run valgrind secmem_bytes -# run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/secmem_bytes_test -# - name: cargo build secmem_string_test -# run: cargo build --bin secmem_string_test + # - name: Run valgrind secmem_bytes + # run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/secmem_bytes_test + # - name: cargo build secmem_string_test + # run: cargo build --bin secmem_string_test # Run with valgrind -# - name: Run valgrind secmem_string -# run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/secmem_string_test + # - name: Run valgrind secmem_string + # run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/secmem_string_test - run: cargo check --package citadel_sdk --release --features=webrtc,sql,redis,multi-threaded - run: cargo install --locked cargo-deny && cargo deny check all - run: rustup component add clippy-preview @@ -149,25 +149,25 @@ jobs: - run: cargo clippy --features=webrtc,sql,redis,multi-threaded --release -- -D warnings - run: cargo clippy --features=webrtc,sql,redis -- -D warnings - run: cargo clippy --features=webrtc,sql,redis --release -- -D warnings - - run: cargo clippy --tests -- -D warnings + - run: cargo clippy --tests --examples -- -D warnings - run: cargo fmt --check - run: RUSTDOCFLAGS="-D warnings" cargo make docs - run: cargo test --package citadel_sdk --doc # - name: cargo build pq_kems -# run: cargo build --bin pq_kems_test -# # Run with valgrind -# - name: Run valgrind pq_kems -# run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/pq_kems_test + # run: cargo build --bin pq_kems_test + # # Run with valgrind + # - name: Run valgrind pq_kems + # run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/pq_kems_test -# features_check: -# name: check features -# runs-on: ubuntu-latest -# steps: -# - uses: Avarok-Cybersecurity/gh-actions-deps@master -# - name: Install features checker -# run: cargo install cargo-all-features -# - name: Check all feature combinations -# run: cargo check-all-features --package citadel_sdk + # features_check: + # name: check features + # runs-on: ubuntu-latest + # steps: + # - uses: Avarok-Cybersecurity/gh-actions-deps@master + # - name: Install features checker + # run: cargo install cargo-all-features + # - name: Check all feature combinations + # run: cargo check-all-features --package citadel_sdk coverage: runs-on: macos-latest @@ -192,11 +192,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - nat_type_client_a: - - "full_cone" - - "address_restricted" - - "port_restricted" - - "symmetric" + nat_type_client_a: + - "full_cone" + - "address_restricted" + - "port_restricted" + - "symmetric" timeout-minutes: 60 env: NAT_TYPE_CLIENT_A: ${{ matrix.nat_type_client_a }} diff --git a/Cargo.toml b/Cargo.toml index 8426e664a..c9ebc014a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,38 +3,41 @@ resolver = "2" members = [ "citadel_sdk", - "citadel_wire", - "citadel_user", - "citadel_crypt", - "async_ip", - "citadel_pqcrypto", - "citadel_proto", + "citadel_wire", + "citadel_user", + "citadel_crypt", + "async_ip", + "citadel_pqcrypto", + "citadel_proto", "firebase-rtdb", "netbeam", - "citadel_logging", + "citadel_logging", "citadel_io", "citadel_types", + "example-library", ] exclude = [ "./target/*", - "./examples" ] [workspace.dependencies] # workspace deps -citadel_sdk = { path = "./citadel_sdk", default-features = false, version = "0.11.2" } -citadel_wire = { path = "./citadel_wire", default-features = false, version = "0.11.2" } -citadel_user = { path = "./citadel_user", default-features = false, version = "0.11.2" } -citadel_crypt = { path = "./citadel_crypt", default-features = false, version = "0.11.2" } -citadel_pqcrypto = { path = "./citadel_pqcrypto", default-features = false, version = "0.11.2" } -citadel_proto = { path = "./citadel_proto", default-features = false, version = "0.11.2" } -citadel_logging = { path = "./citadel_logging", default-features = false, version = "0.11.2" } -citadel_io = { path = "./citadel_io", default-features = false, version = "0.11.2" } -citadel_types = { path = "./citadel_types", default-features = false, version = "0.11.2" } -netbeam = { path = "./netbeam", default-features = false, version = "0.11.2" } -firebase-rtdb = { path = "./firebase-rtdb", default-features = false, version = "0.11.2" } -async_ip = { path = "./async_ip", default-features = false, version = "0.11.2" } +citadel_sdk = { path = "./citadel_sdk", default-features = false } +citadel_wire = { path = "./citadel_wire", default-features = false } +citadel_user = { path = "./citadel_user", default-features = false } +citadel_crypt = { path = "./citadel_crypt", default-features = false } +citadel_pqcrypto = { path = "./citadel_pqcrypto", default-features = false } +citadel_proto = { path = "./citadel_proto", default-features = false } +citadel_logging = { path = "./citadel_logging", default-features = false } +citadel_io = { path = "./citadel_io", default-features = false } +citadel_types = { path = "./citadel_types", default-features = false } +netbeam = { path = "./netbeam", default-features = false } +firebase-rtdb = { path = "./firebase-rtdb", default-features = false } +async_ip = { path = "./async_ip", default-features = false } + +# examples +citadel-examples = { path = "./example-library", default-features = false } # ordinary deps generic-array = { version = "0.14.6" } @@ -50,7 +53,7 @@ getrandom = { version = "0.2.8", default-features = false } serde-big-array = { default-features = false, version = "0.5.0" } ascon-aead = { default-features = false, version = "0.4.0" } oqs = { version = "0.9.0", default-features = false } -pqcrypto-falcon-wasi = { version = "0.2.14", default-features=false} +pqcrypto-falcon-wasi = { version = "0.2.14", default-features = false } pqcrypto-traits-wasi = { version = "0.3.4", default-features = false } tracing-subscriber = { version = "0.3.16" } reqwest_wasi = { version = "0.11.16", default-features = false } @@ -65,9 +68,9 @@ sync_wrapper = { default-features = false, version = "1.0.0" } async-recursion = { version = "1.0.4" } rstest = { version = "0.23.0" } bincode = { default-features = false, version = "1.3.3" } -serde = { version="1.0.152", default-features = false } +serde = { version = "1.0.152", default-features = false } futures = { version = "0.3.25", default-features = false } -byteorder = { version = "1.4.3", default-features=false } +byteorder = { version = "1.4.3", default-features = false } num-integer = { default-features = false, version = "0.1.45" } arrayvec = { version = "0.7.2", default-features = false } bitvec = { default-features = false, version = "1.0.1" } @@ -112,7 +115,7 @@ embedded-semver = { version = "0.3.0", default-features = false } auto_impl = { default-features = false, version = "1.0.1" } zerocopy = { default-features = false, version = "0.7.7" } atomic = { default-features = false, version = "0.6.0" } -bytemuck = { default-features = false, version = "1.13.1"} +bytemuck = { default-features = false, version = "1.13.1" } either = { default-features = false, version = "1.8.0" } once_cell = { default-features = false, version = "1.17.0" } webrtc-util = { version = "0.8.0" } diff --git a/Cargo.wasix.toml b/Cargo.wasix.toml index 9cd4f5b01..b102b086a 100644 --- a/Cargo.wasix.toml +++ b/Cargo.wasix.toml @@ -22,18 +22,18 @@ exclude = [ [workspace.dependencies] # workspace deps -citadel_sdk = { path = "./citadel_sdk", default-features = false, version = "0.9.0" } -citadel_wire = { path = "./citadel_wire", default-features = false, version = "0.9.0" } -citadel_user = { path = "./citadel_user", default-features = false, version = "0.9.0" } -citadel_crypt = { path = "./citadel_crypt", default-features = false, version = "0.9.0" } -citadel_pqcrypto = { path = "./citadel_pqcrypto", default-features = false, version = "0.9.0" } -citadel_proto = { path = "./citadel_proto", default-features = false, version = "0.9.0" } -citadel_logging = { path = "./citadel_logging", default-features = false, version = "0.9.0" } -citadel_io = { path = "./citadel_io", default-features = false, version = "0.9.0" } -citadel_types = { path = "./citadel_types", default-features = false, version = "0.9.0" } -netbeam = { path = "./netbeam", default-features = false, version = "0.8.0" } -firebase-rtdb = { path = "./firebase-rtdb", default-features = false, version = "0.8.0" } -async_ip = { path = "./async_ip", default-features = false, version = "0.8.0" } +citadel_sdk = { path = "./citadel_sdk", default-features = false } +citadel_wire = { path = "./citadel_wire", default-features = false } +citadel_user = { path = "./citadel_user", default-features = false } +citadel_crypt = { path = "./citadel_crypt", default-features = false } +citadel_pqcrypto = { path = "./citadel_pqcrypto", default-features = false } +citadel_proto = { path = "./citadel_proto", default-features = false } +citadel_logging = { path = "./citadel_logging", default-features = false } +citadel_io = { path = "./citadel_io", default-features = false } +citadel_types = { path = "./citadel_types", default-features = false } +netbeam = { path = "./netbeam", default-features = false } +firebase-rtdb = { path = "./firebase-rtdb", default-features = false } +async_ip = { path = "./async_ip", default-features = false } # ordinary deps generic-array = { version = "0.14.6" } diff --git a/citadel_proto/src/auth.rs b/citadel_proto/src/auth.rs index d3b3ca1fa..6d73bb8ba 100644 --- a/citadel_proto/src/auth.rs +++ b/citadel_proto/src/auth.rs @@ -29,7 +29,7 @@ impl AuthenticationRequest { } /// No credentials will be used for login, only a one-time device-dependent cryptographic bundle - pub fn passwordless(uuid: Uuid, server_addr: SocketAddr) -> Self { + pub fn transient(uuid: Uuid, server_addr: SocketAddr) -> Self { Self::Passwordless { username: uuid.to_string(), server_addr, diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index 4f1f3721f..a17242679 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -625,6 +625,12 @@ pub async fn process_peer_cmd( ) }; + let udp_mode = if udp_rx_opt.is_some() { + UdpMode::Enabled + } else { + UdpMode::Disabled + }; + let channel_signal = NodeResult::PeerChannelCreated(PeerChannelCreated { ticket: ticket_for_chan.unwrap_or(ticket), @@ -660,6 +666,7 @@ pub async fn process_peer_cmd( app, encrypted_config_container, client_config, + udp_mode, ) .await; } @@ -762,6 +769,12 @@ pub async fn process_peer_cmd( ) }; + let udp_mode = if udp_rx_opt.is_some() { + UdpMode::Enabled + } else { + UdpMode::Disabled + }; + let channel_signal = NodeResult::PeerChannelCreated(PeerChannelCreated { ticket: ticket_for_chan.unwrap_or(ticket), @@ -810,6 +823,7 @@ pub async fn process_peer_cmd( app, encrypted_config_container, client_config, + udp_mode, ) .await; } @@ -1136,6 +1150,7 @@ async fn process_signal_command_as_server( ) .await; } + drop(peer_layer_lock); let peer_alert_signal = PeerSignal::DeregistrationSuccess { peer_conn_type: peer_conn_type.reverse(), }; diff --git a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs index 1fbeeae1f..85f90858a 100644 --- a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs @@ -24,7 +24,15 @@ use netbeam::sync::network_endpoint::NetworkEndpoint; use std::sync::atomic::Ordering; /// Handles preconnect packets. Handles the NAT traversal -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session_orig.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session_orig.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] pub async fn process_preconnect( session_orig: &CitadelSession, packet: HdpPacket, @@ -601,14 +609,16 @@ fn handle_success_as_receiver( if let Some(udp_splittable) = udp_splittable { let peer_addr = udp_splittable.peer_addr(); // the UDP subsystem will automatically engage at this point - CitadelSession::udp_socket_loader( - session.clone(), - VirtualTargetType::LocalGroupServer { implicated_cid }, - udp_splittable, - peer_addr, - session.kernel_ticket.get(), - Some(tcp_loaded_alerter_rx), - ); + if state_container.udp_mode == UdpMode::Enabled { + CitadelSession::udp_socket_loader( + session.clone(), + VirtualTargetType::LocalGroupServer { implicated_cid }, + udp_splittable, + peer_addr, + session.kernel_ticket.get(), + Some(tcp_loaded_alerter_rx), + ); + } } else { log::warn!(target: "citadel", "No UDP splittable was specified. UdpMode: {:?}", state_container.udp_mode); } diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index 90c30ccb5..f2f8ca67c 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -18,6 +18,7 @@ use crate::proto::peer::peer_layer::PeerConnectionType; use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; use crate::proto::state_container::VirtualConnectionType; +use citadel_types::prelude::UdpMode; use citadel_user::re_exports::__private::Formatter; use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; @@ -75,6 +76,7 @@ async fn setup_listener_non_initiator( v_conn: VirtualConnectionType, hole_punched_addr: TargettedSocketAddr, ticket: Ticket, + udp_mode: UdpMode, ) -> Result<(), NetworkError> { // TODO: use custom self-signed let (listener, _) = Node::create_listen_socket( @@ -90,6 +92,7 @@ async fn setup_listener_non_initiator( v_conn, hole_punched_addr, ticket, + udp_mode, ) .await } @@ -101,6 +104,7 @@ async fn p2p_conn_handler( v_conn: VirtualConnectionType, hole_punched_addr: TargettedSocketAddr, ticket: Ticket, + udp_mode: UdpMode, ) -> Result<(), NetworkError> { let kernel_tx = session.kernel_tx.clone(); let implicated_cid = session.implicated_cid.clone(); @@ -115,12 +119,6 @@ async fn p2p_conn_handler( let session = CitadelSession::upgrade_weak(weak) .ok_or(NetworkError::InternalError("HdpSession dropped"))?; - /* - if p2p_stream.peer_addr()?.ip() != necessary_remote_addr.ip() { - log::warn!(target: "citadel", "Blocked p2p connection from {:?} since IP does not match {:?}", p2p_stream, necessary_remote_addr); - continue; - }*/ - handle_p2p_stream( p2p_stream, implicated_cid, @@ -130,6 +128,7 @@ async fn p2p_conn_handler( v_conn, hole_punched_addr, ticket, + udp_mode, )?; Ok(()) } @@ -159,6 +158,7 @@ fn handle_p2p_stream( v_conn: VirtualConnectionType, hole_punched_addr: TargettedSocketAddr, ticket: Ticket, + udp_mode: UdpMode, ) -> std::io::Result<()> { // SECURITY: Since this branch only occurs IF the primary session is connected, then the primary user is // logged-in. However, what if a malicious user decides to connect here? @@ -205,14 +205,17 @@ fn handle_p2p_stream( state_container .insert_direct_p2p_connection(direct_p2p_remote, v_conn.get_target_cid()) .map_err(|err| generic_error(err.into_string()))?; - CitadelSession::udp_socket_loader( - sess.clone(), - v_conn, - UdpSplittableTypes::Quic(udp_conn), - hole_punched_addr, - ticket, - None, - ); + + if udp_mode == UdpMode::Enabled { + CitadelSession::udp_socket_loader( + sess.clone(), + v_conn, + UdpSplittableTypes::Quic(udp_conn), + hole_punched_addr, + ticket, + None, + ); + } std::mem::drop(state_container); @@ -283,7 +286,15 @@ async fn p2p_stopper(receiver: Receiver<()>) -> Result<(), NetworkError> { } /// Both sides need to begin this process at `sync_time` -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(implicated_cid=implicated_cid.get(), peer_cid=peer_connection_type.get_original_target_cid())))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(implicated_cid=implicated_cid.get(), peer_cid=peer_connection_type.get_original_target_cid() + ) +))] #[allow(clippy::too_many_arguments)] pub(crate) async fn attempt_simultaneous_hole_punch( peer_connection_type: PeerConnectionType, @@ -297,6 +308,7 @@ pub(crate) async fn attempt_simultaneous_hole_punch( app: NetworkEndpoint, encrypted_config_container: HolePunchConfigContainer, client_config: Arc, + udp_mode: UdpMode, ) -> std::io::Result<()> { let is_initiator = app.is_initiator(); let kernel_tx = &kernel_tx; @@ -346,13 +358,14 @@ pub(crate) async fn attempt_simultaneous_hole_punch( v_conn, addr, ticket, + udp_mode, ) } else { log::trace!(target: "citadel", "Non-initiator will begin listening immediately"); drop(hole_punched_socket); // drop to prevent conflicts caused by SO_REUSE_ADDR - setup_listener_non_initiator(local_addr, remote_connect_addr, session.clone(), v_conn, addr, ticket) + setup_listener_non_initiator(local_addr, remote_connect_addr, session.clone(), v_conn, addr, ticket, udp_mode) .await - .map_err(|err|generic_error(format!("Non-initiator was unable to secure connection despite hole-punching success: {err:?}"))) + .map_err(|err| generic_error(format!("Non-initiator was unable to secure connection despite hole-punching success: {err:?}"))) } }; diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index c932533ec..f736788e7 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -20,43 +20,44 @@ use citadel_wire::hypernode_type::NodeType; use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; use netbeam::time_tracker::TimeTracker; +//use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender, channel, TrySendError}; +use crate::auth::AuthenticationRequest; use crate::constants::{ DRILL_UPDATE_FREQUENCY_LOW_BASE, FIREWALL_KEEP_ALIVE_UDP, GROUP_EXPIRE_TIME_MS, HDP_HEADER_BYTE_LEN, INITIAL_RECONNECT_LOCKOUT_TIME_NS, KEEP_ALIVE_INTERVAL_MS, KEEP_ALIVE_TIMEOUT_NS, LOGIN_EXPIRATION_TIME, }; use crate::error::NetworkError; -use crate::proto::packet::{packet_flags, HdpPacket}; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; -use crate::proto::packet_crafter::{self, GroupTransmitter, RatchetPacketCrafterContainer}; -use citadel_types::proto::VirtualObjectMetadata; -//use futures_codec::Framed; -use crate::proto::misc; -use crate::proto::misc::clean_shutdown::{CleanShutdownSink, CleanShutdownStream}; -use crate::proto::misc::dual_rwlock::DualRwLock; -use crate::proto::misc::net::GenericNetworkStream; -use crate::proto::packet_processor::includes::{Duration, SocketAddr}; -use crate::proto::packet_processor::{self, PrimaryProcessorResult}; -use crate::proto::session_manager::HdpSessionManager; -use citadel_types::proto::ConnectMode; -use citadel_types::proto::SessionSecuritySettings; -//use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender, channel, TrySendError}; -use crate::auth::AuthenticationRequest; use crate::kernel::RuntimeFuture; use crate::prelude::{GroupBroadcast, PeerEvent, PeerResponse, PreSharedKey, SecureProtocolPacket}; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; +//use futures_codec::Framed; +use crate::proto::misc; +use crate::proto::misc::clean_shutdown::{CleanShutdownSink, CleanShutdownStream}; use crate::proto::misc::dual_cell::DualCell; use crate::proto::misc::dual_late_init::DualLateInit; +use crate::proto::misc::dual_rwlock::DualRwLock; +use crate::proto::misc::net::GenericNetworkStream; use crate::proto::misc::udp_internal_interface::{UdpSplittableTypes, UdpStream}; +//use futures_codec::Framed; +use crate::proto::node_result::{Disconnect, InternalServerError, NodeResult}; use crate::proto::outbound_sender::{ channel, unbounded, SendError, UnboundedReceiver, UnboundedSender, }; use crate::proto::outbound_sender::{ OutboundPrimaryStreamReceiver, OutboundPrimaryStreamSender, OutboundUdpSender, }; +use crate::proto::packet::{packet_flags, HdpPacket}; +use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; +use crate::proto::packet_crafter::{self, GroupTransmitter, RatchetPacketCrafterContainer}; +use crate::proto::packet_processor::disconnect_packet::SUCCESS_DISCONNECT; +use crate::proto::packet_processor::includes::{Duration, SocketAddr}; use crate::proto::packet_processor::raw_primary_packet::{check_proxy, ReceivePortType}; +use crate::proto::packet_processor::{self, PrimaryProcessorResult}; use crate::proto::peer::p2p_conn_handler::P2PInboundHandle; use crate::proto::peer::peer_layer::{HyperNodePeerLayer, PeerSignal}; +use crate::proto::remote::{NodeRemote, Ticket}; +use crate::proto::session_manager::HdpSessionManager; use crate::proto::session_queue_handler::{ QueueWorkerResult, QueueWorkerTicket, SessionQueueWorker, SessionQueueWorkerHandle, DRILL_REKEY_WORKER, FIREWALL_KEEP_ALIVE, KEEP_ALIVE_CHECKER, PROVISIONAL_CHECKER, @@ -70,9 +71,14 @@ use crate::proto::state_subcontainers::preconnect_state_container::UdpChannelSen use crate::proto::state_subcontainers::rekey_container::calculate_update_frequency; use crate::proto::transfer_stats::TransferStats; use atomic::Atomic; +use bytemuck::NoUninit; use citadel_crypt::prelude::{ConstructorOpts, FixedSizedSource}; use citadel_crypt::streaming_crypt_scrambler::{scramble_encrypt_source, ObjectSource}; +use citadel_types::crypto::SecurityLevel; +use citadel_types::proto::ConnectMode; +use citadel_types::proto::SessionSecuritySettings; use citadel_types::proto::TransferType; +use citadel_types::proto::VirtualObjectMetadata; use citadel_user::backend::PersistenceHandler; use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::exports::Connection; @@ -81,12 +87,6 @@ use std::ops::Deref; use std::path::PathBuf; use std::pin::Pin; use std::time::{SystemTime, UNIX_EPOCH}; -//use futures_codec::Framed; -use crate::proto::node_result::{Disconnect, InternalServerError, NodeResult}; -use crate::proto::packet_processor::disconnect_packet::SUCCESS_DISCONNECT; -use crate::proto::remote::{NodeRemote, Ticket}; -use bytemuck::NoUninit; -use citadel_types::crypto::SecurityLevel; //use crate::define_struct; @@ -687,6 +687,8 @@ impl CitadelSession { if udp_mode == UdpMode::Enabled { state_container.pre_connect_state.udp_channel_oneshot_tx = UdpChannelSender::default(); + } else { + state_container.pre_connect_state.udp_channel_oneshot_tx = UdpChannelSender::empty(); } // NEXT STEP: check preconnect, and update internal security-level recv side to the security level found in transfer to ensure all future packages are at that security-level diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index 15a20e846..a16cb11bf 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -341,7 +341,15 @@ impl HdpSessionManager { /// Ensures that the session is removed even if there is a technical error in the underlying stream /// TODO: Make this code less hacky, and make the removal process cleaner. Use RAII on HdpSessionInner? - #[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(implicated_cid=new_session.implicated_cid.get(), is_server=new_session.is_server, peer_addr=peer_addr.to_string())))] + #[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(implicated_cid=new_session.implicated_cid.get(), is_server=new_session.is_server, peer_addr=peer_addr.to_string() + ) + ))] async fn execute_session_with_safe_shutdown( session_manager: HdpSessionManager, new_session: CitadelSession, @@ -425,11 +433,11 @@ impl HdpSessionManager { log::trace!(target: "citadel", "Alerting {} that {} disconnected", peer_cid, implicated_cid); let peer_conn_type = PeerConnectionType::LocalGroupPeer { implicated_cid, - peer_cid + peer_cid, }; let signal = PeerSignal::Disconnect { peer_conn_type, - disconnect_response: Some(PeerResponse::Disconnected(format!("{peer_cid} disconnected from {implicated_cid} forcibly"))) + disconnect_response: Some(PeerResponse::Disconnected(format!("{peer_cid} disconnected from {implicated_cid} forcibly"))), }; if let Err(_err) = sess_mgr.send_signal_to_peer_direct(peer_cid, |peer_hyper_ratchet| { super::packet_crafter::peer_cmd::craft_peer_signal(peer_hyper_ratchet, signal, Ticket(0), timestamp, security_level) @@ -1094,27 +1102,25 @@ impl HdpSessionManager { return Err("Target CID cannot be equal to the implicated CID".to_string()); } - let account_manager = { inner!(self).account_manager.clone() }; + let (account_manager, peer_sess) = { + let this = inner!(self); + ( + this.account_manager.clone(), + this.sessions.get(&target_cid).map(|r| r.1.clone()), + ) + }; - log::trace!(target: "citadel", "Checking if {} is registered locally ... {:?}", target_cid, signal); + log::trace!(target: "citadel", "Checking if {target_cid} is registered locally ... {signal:?}"); if account_manager .hyperlan_cid_is_registered(target_cid) .await .map_err(|err| err.into_string())? { - let (sess, pers) = { - let this = inner!(self); - let sess = this.sessions.get(&target_cid).map(|r| r.1.clone()); - (sess, this.account_manager.get_persistence_handler().clone()) - }; + let pers = account_manager.get_persistence_handler().clone(); // get the target cid's session - if let Some(ref sess_ref) = sess { - sess_ref - .hypernode_peer_layer - .inner - .write() - .await + if let Some(ref sess_ref) = peer_sess { + peer_layer .insert_tracked_posting(implicated_cid, timeout, ticket, signal, on_timeout) .await; let peer_sender = sess_ref.to_primary_stream.as_ref().unwrap(); diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index c284db01e..c66679c17 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -155,7 +155,7 @@ pub(crate) struct InboundFileTransfer { pub total_groups: usize, pub groups_rendered: usize, pub last_group_window_len: usize, - pub last_group_finish_time: Instant, + pub last_group_finish_time: i64, pub ticket: Ticket, pub virtual_target: VirtualTargetType, pub metadata: VirtualObjectMetadata, @@ -1135,7 +1135,7 @@ impl StateContainerInner { let (reception_complete_tx, success_receiving_rx) = citadel_io::tokio::sync::oneshot::channel(); let entry = InboundFileTransfer { - last_group_finish_time: Instant::now(), + last_group_finish_time: tt.get_global_time_ns(), last_group_window_len: 0, object_id, total_groups: metadata_orig.group_count, @@ -1556,6 +1556,7 @@ impl StateContainerInner { GroupReceiverStatus::GROUP_COMPLETE(_last_wid) => { let receiver = self.inbound_groups.remove(&group_key).unwrap().receiver; let mut chunk = receiver.finalize(); + let bytes_in_group = chunk.len(); log::trace!(target: "citadel", "GROUP {} COMPLETE. Total groups: {} | Plaintext len: {} | Received plaintext len: {}", group_id, file_container.total_groups, file_container.metadata.plaintext_length, chunk.len()); if let Some(local_encryption_level) = file_container.local_encryption_level { @@ -1597,20 +1598,22 @@ impl StateContainerInner { ) })?; } else { - let now = Instant::now(); - let bytes_per_sec = file_container.metadata.plaintext_length as f32 - / now - .duration_since(file_container.last_group_finish_time) - .as_secs_f32() - .round(); - let mb_per_sec = bytes_per_sec / (1024.0f32 * 1024.0f32); - log::info!(target: "citadel", "Sending reception tick for group {} of {} | {:.2} MB/s", group_id, file_container.total_groups, mb_per_sec); + let now = self.time_tracker.get_global_time_ns(); + let elapsed_nanos = + now.saturating_sub(file_container.last_group_finish_time) as f64; + let bytes_per_ns = bytes_in_group as f64 / elapsed_nanos; // unit: bytes/ns + // convert bytes per period into MB/s + let mb_per_sec = bytes_per_ns * 1_000_000_000f64; // unit: bytes/sec + let mb_per_sec = mb_per_sec / 1_000_000f64; // unit: MB/sec + // Only use 2 decimals + let mb_per_sec = (mb_per_sec * 100.0).round() / 100.0; + log::trace!(target: "citadel", "Sending reception tick for group {} of {} | {} MB/s", group_id, file_container.total_groups, mb_per_sec); file_container.last_group_finish_time = now; let status = ObjectTransferStatus::ReceptionTick( group_id as usize, file_container.total_groups, - mb_per_sec, + mb_per_sec as f32, ); // sending the wave ack will complete the group on the initiator side file_transfer_handle.unbounded_send(status).map_err(|err| { diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs index 839a59fd3..d2d8a0b80 100644 --- a/citadel_sdk/src/builder/node_builder.rs +++ b/citadel_sdk/src/builder/node_builder.rs @@ -190,7 +190,7 @@ impl NodeBuilder { self } - /// Attaches miscellaneous server settings (e.g., passwordless mode, credential requirements) + /// Attaches miscellaneous server settings (e.g., transient mode, credential requirements) pub fn with_server_misc_settings(&mut self, misc_settings: ServerMiscSettings) -> &mut Self { self.server_misc_settings = Some(misc_settings); self @@ -354,11 +354,14 @@ mod tests { #[timeout(std::time::Duration::from_secs(60))] #[allow(clippy::let_underscore_must_use)] async fn test_options( - #[values(ServerUnderlyingProtocol::new_quic_self_signed(), ServerUnderlyingProtocol::new_tls_self_signed().unwrap())] + #[values(ServerUnderlyingProtocol::new_quic_self_signed(), ServerUnderlyingProtocol::new_tls_self_signed().unwrap() + )] underlying_protocol: ServerUnderlyingProtocol, - #[values(NodeType::Peer, NodeType::Server(std::net::SocketAddr::from_str("127.0.0.1:9999").unwrap()))] + #[values(NodeType::Peer, NodeType::Server(std::net::SocketAddr::from_str("127.0.0.1:9999").unwrap() + ))] node_type: NodeType, - #[values(KernelExecutorSettings::default(), KernelExecutorSettings::default().with_max_concurrency(2))] + #[values(KernelExecutorSettings::default(), KernelExecutorSettings::default().with_max_concurrency(2) + )] kernel_settings: KernelExecutorSettings, #[values(BackendType::InMemory, BackendType::new("file:/hello_world/path/").unwrap())] backend_type: BackendType, diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index e41cd8325..763549b17 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -78,15 +78,14 @@ pub async fn delete + Send>( #[cfg(test)] mod tests { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; - use crate::prefabs::server::accept_file_transfer_kernel::{ - exhaust_file_transfer, AcceptFileTransferKernel, - }; + use crate::prefabs::server::accept_file_transfer_kernel::AcceptFileTransferKernel; use crate::prefabs::client::peer_connection::{FileTransferHandleRx, PeerConnectionKernel}; use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prelude::*; use crate::test_common::wait_for_peers; use citadel_io::tokio; + use futures::StreamExt; use rstest::rstest; use std::net::SocketAddr; use std::path::PathBuf; @@ -109,7 +108,7 @@ mod tests { KemAlgorithm::Kyber, SigAlgorithm::Falcon1024 )] - #[timeout(std::time::Duration::from_secs(90))] + #[timeout(Duration::from_secs(90))] #[citadel_io::tokio::test] async fn test_c2s_file_transfer_revfs( #[case] enx: EncryptionAlgorithm, @@ -131,7 +130,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .disable_udp() .with_session_security_settings(session_security_settings) .build() @@ -204,7 +203,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .disable_udp() .with_session_security_settings(session_security_settings) .build() @@ -279,7 +278,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .disable_udp() .with_session_security_settings(session_security_settings) .build() @@ -327,15 +326,19 @@ mod tests { assert!(client_success.load(Ordering::Relaxed)); } + #[rstest] + #[case(SecrecyMode::BestEffort)] + #[timeout(Duration::from_secs(60))] #[citadel_io::tokio::test(flavor = "multi_thread")] - async fn test_p2p_file_transfer_revfs() { + async fn test_p2p_file_transfer_revfs( + #[case] secrecy_mode: SecrecyMode, + #[values(KemAlgorithm::Kyber)] kem: KemAlgorithm, + #[values(EncryptionAlgorithm::AES_GCM_256)] enx: EncryptionAlgorithm, + ) { citadel_logging::setup_log(); crate::test_common::TestBarrier::setup(2); let client0_success = &AtomicBool::new(false); let client1_success = &AtomicBool::new(false); - let enx = EncryptionAlgorithm::AES_GCM_256; - let secrecy_mode = SecrecyMode::BestEffort; - let kem = KemAlgorithm::Kyber; let (server, server_addr) = crate::test_common::server_info(); @@ -352,16 +355,23 @@ mod tests { let source_dir = &PathBuf::from("../resources/TheBridge.pdf"); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid0) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) .disable_udp() .with_session_security_settings(session_security) .build() .unwrap(); + let peer_conn_0 = PeerConnectionSetupAggregator::default() + .with_peer_custom(uuid1) + .ensure_registered() + .with_session_security_settings(session_security) + .enable_udp() + .add(); + // TODO: SinglePeerConnectionKernel let client_kernel0 = PeerConnectionKernel::new( server_connection_settings, - uuid1, + peer_conn_0, move |mut connection, remote_outer| async move { wait_for_peers().await; let mut connection = connection.recv().await.unwrap()?; @@ -400,15 +410,22 @@ mod tests { ); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid1) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) .disable_udp() .with_session_security_settings(session_security) .build() .unwrap(); + let peer_conn_1 = PeerConnectionSetupAggregator::default() + .with_peer_custom(uuid0) + .ensure_registered() + .with_session_security_settings(session_security) + .enable_udp() + .add(); + let client_kernel1 = PeerConnectionKernel::new( server_connection_settings, - uuid0, + peer_conn_1, move |mut connection, remote_outer| async move { wait_for_peers().await; let mut connection = connection.recv().await.unwrap()?; @@ -478,4 +495,22 @@ mod tests { drop(handle); } + + pub fn exhaust_file_transfer(mut handle: ObjectTransferHandler) { + // Exhaust the stream + let handle = citadel_io::tokio::task::spawn(async move { + while let Some(evt) = handle.next().await { + log::info!(target: "citadel", "File Transfer Event: {evt:?}"); + if let ObjectTransferStatus::Fail(err) = &evt { + log::error!(target: "citadel", "File Transfer Failed: {err:?}"); + } else if let ObjectTransferStatus::TransferComplete = &evt { + break; + } else if let ObjectTransferStatus::ReceptionComplete = &evt { + break; + } + } + }); + + drop(handle); + } } diff --git a/citadel_sdk/src/lib.rs b/citadel_sdk/src/lib.rs index 96b7a4a62..2d5aa2647 100644 --- a/citadel_sdk/src/lib.rs +++ b/citadel_sdk/src/lib.rs @@ -177,7 +177,7 @@ //! //! // await the server to execute //! # async move { -//! let result = server.await; +//! let result = server.await; //! # }; //! # Ok::<(), Box>(()) //! ``` @@ -202,7 +202,7 @@ //! //! let client = NodeBuilder::default().build(client_kernel)?; //! # async move { -//! let result = client.await; +//! let result = client.await; //! # }; //! # Ok::<(), Box>(()) //! ``` diff --git a/citadel_sdk/src/prefabs/client/broadcast.rs b/citadel_sdk/src/prefabs/client/broadcast.rs index 679220722..41e6d7bd3 100644 --- a/citadel_sdk/src/prefabs/client/broadcast.rs +++ b/citadel_sdk/src/prefabs/client/broadcast.rs @@ -423,7 +423,7 @@ mod tests { }; let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -505,7 +505,7 @@ mod tests { .collect::>(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); diff --git a/citadel_sdk/src/prefabs/client/mod.rs b/citadel_sdk/src/prefabs/client/mod.rs index 5692a6773..f453fce08 100644 --- a/citadel_sdk/src/prefabs/client/mod.rs +++ b/citadel_sdk/src/prefabs/client/mod.rs @@ -77,13 +77,20 @@ pub struct ServerConnectionSettingsBuilder { address: Option, udp_mode: Option, session_security_settings: Option, - authless_uuid: Option, + transient_uuid: Option, is_connect: bool, } impl ServerConnectionSettingsBuilder { - /// Creates a new connection to a central server that uses no credentialed authentication, only a secure channel - pub fn no_credentials(addr: T, id: Uuid) -> Self { + /// Creates a new connection to a central server that does not persist client metadata and account information + /// after the connection is dropped to the server. This is ideal for applications that do not require + /// persistence. + pub fn transient(addr: T) -> Self { + Self::transient_with_id(addr, Uuid::new_v4()) + } + + /// See docs for `transient`. This function allows you to specify a custom UUID for the transient connection. + pub fn transient_with_id(addr: T, id: impl Into) -> Self { Self { password: None, username: None, @@ -91,7 +98,7 @@ impl ServerConnectionSettingsBuilder { session_security_settings: None, name: None, psk: None, - authless_uuid: Some(id), + transient_uuid: Some(id.into()), address: Some(addr), is_connect: false, } @@ -110,7 +117,7 @@ impl ServerConnectionSettingsBuilder { username: Some(username.into()), name: Some(alias.into()), psk: None, - authless_uuid: None, + transient_uuid: None, address: Some(addr), udp_mode: None, session_security_settings: None, @@ -129,7 +136,7 @@ impl ServerConnectionSettingsBuilder { username: Some(username.into()), name: None, psk: None, - authless_uuid: None, + transient_uuid: None, address: Some(addr), udp_mode: None, session_security_settings: None, @@ -154,6 +161,10 @@ impl ServerConnectionSettingsBuilder { self.with_udp_mode(UdpMode::Disabled) } + pub fn enable_udp(self) -> Self { + self.with_udp_mode(UdpMode::Enabled) + } + /// Adds a session security settings to the client-to-server connection. This is necessary for the server to know how to handle the connection. pub fn with_session_security_settings>( mut self, @@ -176,8 +187,8 @@ impl ServerConnectionSettingsBuilder { None }; - if let Some(uuid) = self.authless_uuid { - Ok(ServerConnectionSettings::NoCredentials { + if let Some(uuid) = self.transient_uuid { + Ok(ServerConnectionSettings::Transient { server_addr: server_addr .ok_or(NetworkError::Generic("No address found".to_string()))?, uuid, @@ -220,7 +231,7 @@ impl ServerConnectionSettingsBuilder { /// The settings for a client-to-server connection pub enum ServerConnectionSettings { - NoCredentials { + Transient { server_addr: SocketAddr, uuid: Uuid, udp_mode: UdpMode, @@ -248,7 +259,7 @@ pub enum ServerConnectionSettings { impl ServerConnectionSettings { pub(crate) fn udp_mode(&self) -> UdpMode { match self { - Self::NoCredentials { udp_mode, .. } => *udp_mode, + Self::Transient { udp_mode, .. } => *udp_mode, Self::CredentialedRegister { udp_mode, .. } => *udp_mode, Self::CredentialedConnect { udp_mode, .. } => *udp_mode, } @@ -256,7 +267,7 @@ impl ServerConnectionSettings { pub(crate) fn session_security_settings(&self) -> SessionSecuritySettings { match self { - Self::NoCredentials { + Self::Transient { session_security_settings, .. } => *session_security_settings, @@ -273,7 +284,7 @@ impl ServerConnectionSettings { pub(crate) fn pre_shared_key(&self) -> Option<&PreSharedKey> { match self { - Self::NoCredentials { pre_shared_key, .. } => pre_shared_key.as_ref(), + Self::Transient { pre_shared_key, .. } => pre_shared_key.as_ref(), Self::CredentialedRegister { pre_shared_key, .. } => pre_shared_key.as_ref(), Self::CredentialedConnect { pre_shared_key, .. } => pre_shared_key.as_ref(), } diff --git a/citadel_sdk/src/prefabs/client/peer_connection.rs b/citadel_sdk/src/prefabs/client/peer_connection.rs index d8d0c7be6..6902c08e0 100644 --- a/citadel_sdk/src/prefabs/client/peer_connection.rs +++ b/citadel_sdk/src/prefabs/client/peer_connection.rs @@ -3,12 +3,13 @@ use crate::prelude::results::PeerConnectSuccess; use crate::prelude::*; use crate::test_common::wait_for_peers; use citadel_io::tokio::sync::mpsc::{Receiver, UnboundedSender}; -use citadel_io::Mutex; +use citadel_io::{tokio, Mutex}; use citadel_proto::re_imports::async_trait; use citadel_user::hypernode_account::UserIdentifierExt; use futures::stream::FuturesUnordered; -use futures::{Future, TryStreamExt}; +use futures::TryStreamExt; use std::collections::HashMap; +use std::future::Future; use std::marker::PhantomData; use std::sync::Arc; use uuid::Uuid; @@ -40,6 +41,27 @@ pub struct FileTransferHandleRx { pub conn_type: VirtualTargetType, } +impl FileTransferHandleRx { + /// Accepts all incoming file transfer handles and processes them in the background + pub fn accept_all(mut self) { + let task = tokio::task::spawn(async move { + let rx = &mut self.inner; + while let Some(mut handle) = rx.recv().await { + let task = tokio::task::spawn(async move { + if let Err(err) = handle.exhaust_stream().await { + let orientation = handle.orientation; + log::warn!(target: "citadel", "Error background handling of file transfer for {orientation:?}: {err:?}"); + } + }); + + drop(task); + } + }); + + drop(task); + } +} + impl std::ops::Deref for FileTransferHandleRx { type Target = citadel_io::tokio::sync::mpsc::UnboundedReceiver; @@ -194,6 +216,16 @@ impl AddedPeer { self } + /// Disables the UDP mode for the client-to-server connection. The default setting is Disabled + pub fn disable_udp(self) -> Self { + self.with_udp_mode(UdpMode::Disabled) + } + + /// Enables the UDP mode for the client-to-server connection. The default setting is Disabled + pub fn enable_udp(self) -> Self { + self.with_udp_mode(UdpMode::Enabled) + } + /// Sets the [`SessionSecuritySettings`] for this peer to peer connection pub fn with_session_security_settings( mut self, @@ -359,7 +391,7 @@ where for (mutually_registered, peer_to_connect) in peers_already_registered.into_iter().zip(peers_to_connect) { - // each task will be responsible for possibly registering to and connecting + // Each task will be responsible for possibly registering to and connecting // with the desired peer let remote = remote.clone(); let PeerConnectionSettings { @@ -378,6 +410,7 @@ where remote.find_target(implicated_cid, id).await? } else { // TODO: optimize peer registration + connection in one go + log::info!(target: "citadel", "{implicated_cid} proposing target {id:?} to central node"); let handle = remote.propose_target(implicated_cid, id.clone()).await?; // if the peer is not yet registered to the central node, wait for it to become registered // this is useful especially for testing purposes @@ -393,8 +426,9 @@ where } } + log::info!(target: "citadel", "{implicated_cid} registering to peer {id:?}"); let _reg_success = handle.register_to_peer().await?; - log::info!(target: "citadel", "Peer {:?} registered || success -> now connecting", id); + log::info!(target: "citadel", "{implicated_cid} registered to peer {id:?} registered || success -> now connecting"); handle }; @@ -454,13 +488,16 @@ mod tests { use crate::prefabs::client::peer_connection::PeerConnectionKernel; use crate::prefabs::client::ServerConnectionSettingsBuilder; use crate::prelude::*; + use crate::remote_ext::results::PeerConnectSuccess; use crate::test_common::{server_info, wait_for_peers, TestBarrier}; use citadel_io::tokio; + use citadel_io::tokio::sync::mpsc::{Receiver, UnboundedSender}; use citadel_user::prelude::UserIdentifierExt; use futures::stream::FuturesUnordered; use futures::TryStreamExt; use rstest::rstest; use std::collections::HashMap; + use std::future::Future; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::time::Duration; use uuid::Uuid; @@ -505,6 +542,7 @@ mod tests { for peer in peers { agg = agg .with_peer_custom(peer) + .ensure_registered() .with_udp_mode(udp_mode) .with_session_security_settings(SessionSecuritySettings::default()) .add(); @@ -520,31 +558,38 @@ mod tests { .build() .unwrap(); + let username = username.clone(); + let client_kernel = PeerConnectionKernel::new( server_connection_settings, agg.clone(), - move |mut results, mut remote| async move { - let mut success = 0; - let mut p2p_remotes = HashMap::new(); - - while let Some(conn) = results.recv().await { - log::trace!(target: "citadel", "User {} received {:?}", username, conn); - let mut conn = conn?; - crate::test_common::udp_mode_assertions( - udp_mode, - conn.udp_channel_rx.take(), - ) - .await; - success += 1; - let _ = - p2p_remotes.insert(conn.channel.get_peer_cid(), conn.remote.clone()); - if success == peer_count - 1 { - break; - } - } + move |results, mut remote| async move { + log::info!(target: "citadel", "***PEER {username} CONNECTED ***"); + let implicated_cid = remote.conn_type.get_implicated_cid(); + let check = move |conn: PeerConnectSuccess| async move { + let implicated_cid = conn.channel.get_implicated_cid(); + let _mutual_peers = conn + .remote + .remote() + .get_local_group_mutual_peers(implicated_cid) + .await + .unwrap(); + conn + }; + let p2p_remotes = handle_peer_connect_successes( + results, + implicated_cid, + peer_count, + udp_mode, + check, + ) + .await + .into_iter() + .map(|r| (r.channel.get_peer_cid(), r.remote)) + .collect::>(); - // by now, all the network peers have been registered to - // test that getting the peers (not necessarily mutual) + // By now, all the network peers have been registered to. + // Test that getting the peers (not necessarily mutual) // show up let network_peers = remote.get_peers(None).await.unwrap(); for user in agg.inner { @@ -563,7 +608,7 @@ mod tests { assert!(mutual_peers.iter().any(|r| r.cid == peer_cid)) } - log::trace!(target: "citadel", "***PEER {} CONNECT RESULT: {}***", username, success); + log::info!(target: "citadel", "***PEER {username} finished all checks***"); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; remote.shutdown_kernel().await @@ -586,12 +631,13 @@ mod tests { #[case(3)] #[timeout(Duration::from_secs(90))] #[tokio::test(flavor = "multi_thread")] - async fn peer_to_peer_connect_passwordless( + async fn peer_to_peer_connect_transient( #[case] peer_count: usize, ) -> Result<(), Box> { assert!(peer_count > 1); citadel_logging::setup_log(); TestBarrier::setup(peer_count); + let udp_mode = UdpMode::Enabled; let do_deregister = peer_count == 2; @@ -612,33 +658,36 @@ mod tests { .map(UserIdentifier::from) .collect::>(); + let mut agg = PeerConnectionSetupAggregator::default(); + + for peer in peers { + agg = agg + .with_peer_custom(peer) + .with_udp_mode(udp_mode) + .ensure_registered() + .with_session_security_settings(SessionSecuritySettings::default()) + .add(); + } + let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); let client_kernel = PeerConnectionKernel::new( server_connection_settings, - peers, - move |mut results, remote| async move { - let mut success = 0; + agg, + move |results, remote| async move { + log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); let implicated_cid = remote.conn_type.get_implicated_cid(); - while let Some(conn) = results.recv().await { - log::trace!(target: "citadel", "User {} received {:?}", uuid, conn); - let mut conn = conn?; + let check = move |conn: PeerConnectSuccess| async move { let peer_cid = conn.channel.get_peer_cid(); - - crate::test_common::p2p_assertions(implicated_cid, &conn).await; - - crate::test_common::udp_mode_assertions( - Default::default(), - conn.udp_channel_rx.take(), - ) - .await; - if do_deregister { - conn.remote.deregister().await?; + conn.remote + .deregister() + .await + .expect("Deregistration failed"); assert!(!conn .remote .inner @@ -648,21 +697,26 @@ mod tests { .await .unwrap()); } + conn + }; - success += 1; - if success == peer_count - 1 { - break; - } - } + let _ = handle_peer_connect_successes( + results, + implicated_cid, + peer_count, + udp_mode, + check, + ) + .await; - log::trace!(target: "citadel", "***PEER {} CONNECT RESULT: {}***", uuid, success); + log::info!(target: "citadel", "***PEER {uuid} finished all checks***"); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; remote.shutdown_kernel().await }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = NodeBuilder::default().build(client_kernel)?; client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -681,16 +735,18 @@ mod tests { #[rstest] #[case(2)] + #[case(3)] #[timeout(std::time::Duration::from_secs(90))] - #[citadel_io::tokio::test(flavor = "multi_thread")] + #[tokio::test(flavor = "multi_thread")] async fn test_peer_to_peer_file_transfer( #[case] peer_count: usize, ) -> Result<(), Box> { assert!(peer_count > 1); citadel_logging::setup_log(); TestBarrier::setup(peer_count); + let udp_mode = UdpMode::Enabled; - let client_success = &AtomicBool::new(false); + let sender_success = &AtomicBool::new(false); let receiver_success = &AtomicBool::new(false); let (server, server_addr) = server_info(); @@ -700,46 +756,59 @@ mod tests { .map(|_| Uuid::new_v4()) .collect::>(); + let sender_uuid = total_peers[0]; + for idx in 0..peer_count { let uuid = total_peers.get(idx).cloned().unwrap(); - let peers = total_peers + let mut peers = total_peers .clone() .into_iter() .filter(|r| r != &uuid) .map(UserIdentifier::from) .collect::>(); + // 0: [1, 2] <-- At idx 0, we want the sender to connect to all other peers + // 1: [0] <-- At idx 1, we want the receiver to connect to the sender + // 2: [0] <-- At idx 2, we want the receiver to connect to the sender + // .. + // n: [0] <-- At idx n, we want the receiver to connect to the sender + if idx != 0 { + peers = vec![sender_uuid.into()]; + } + + let mut agg = PeerConnectionSetupAggregator::default(); + + for peer in peers { + agg = agg + .with_peer_custom(peer) + .ensure_registered() + .with_udp_mode(udp_mode) + .with_session_security_settings(SessionSecuritySettings::default()) + .add(); + } let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); let client_kernel = PeerConnectionKernel::new( server_connection_settings, - peers, - move |mut results, remote| async move { - let mut success = 0; + agg, + move |results, remote| async move { + log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); + wait_for_peers().await; let implicated_cid = remote.conn_type.get_implicated_cid(); - - while let Some(conn) = results.recv().await { - log::trace!(target: "citadel", "User {} received {:?}", uuid, conn); - wait_for_peers().await; - let mut conn = conn?; - //let peer_cid = conn.channel.get_peer_cid(); - - crate::test_common::p2p_assertions(implicated_cid, &conn).await; - - // one user will send the file, the other will receive the file - if idx == 0 { + let is_sender = idx == 0; // the first peer is the sender, the rest are receivers + let check = move |mut conn: PeerConnectSuccess| async move { + if is_sender { conn.remote .send_file_with_custom_opts( "../resources/TheBridge.pdf", 32 * 1024, TransferType::FileTransfer, ) - .await?; - - client_success.store(true, Ordering::Relaxed); + .await + .expect("Failed to send file"); } else { // TODO: route file-transfer + other events to peer channel let mut handle = conn @@ -757,7 +826,6 @@ mod tests { while let Some(status) = handle.next().await { match status { ObjectTransferStatus::ReceptionComplete => { - log::trace!(target: "citadel", "Peer has finished receiving the file!"); let cmp = include_bytes!("../../../../resources/TheBridge.pdf"); let streamed_data = @@ -770,6 +838,7 @@ mod tests { "Original data and streamed data does not match" ); + log::info!(target: "citadel", "Peer has finished receiving and verifying the file!"); break; } @@ -781,18 +850,31 @@ mod tests { _ => {} } } - - receiver_success.store(true, Ordering::Relaxed); } - success += 1; - if success == peer_count - 1 { - break; - } + conn + }; + // Use a peer count of two since we only have one sender and one receiver per pair + // However, we need a way of ensuring we collect three results + let peer_count = if idx == 0 { peer_count } else { 2 }; + let _ = handle_peer_connect_successes( + results, + implicated_cid, + peer_count, + udp_mode, + check, + ) + .await; + + if is_sender { + sender_success.store(true, Ordering::Relaxed); + } else { + receiver_success.store(true, Ordering::Relaxed); } - log::trace!(target: "citadel", "***PEER {} CONNECT RESULT: {}***", uuid, success); + log::info!(target: "citadel", "***PEER {uuid} (is_sender: {is_sender}) finished all checks***"); wait_for_peers().await; + log::info!(target: "citadel", "***PEER {uuid} (is_sender: {is_sender}) shutting down***"); remote.shutdown_kernel().await }, ); @@ -810,7 +892,7 @@ mod tests { }; } - assert!(client_success.load(Ordering::Relaxed)); + assert!(sender_success.load(Ordering::Relaxed)); assert!(receiver_success.load(Ordering::Relaxed)); Ok(()) } @@ -818,13 +900,14 @@ mod tests { #[rstest] #[case(2)] #[timeout(std::time::Duration::from_secs(90))] - #[citadel_io::tokio::test(flavor = "multi_thread")] + #[tokio::test(flavor = "multi_thread")] async fn test_peer_to_peer_rekey( #[case] peer_count: usize, ) -> Result<(), Box> { assert!(peer_count > 1); citadel_logging::setup_log(); TestBarrier::setup(peer_count); + let udp_mode = UdpMode::Enabled; let client_success = &AtomicUsize::new(0); let (server, server_addr) = server_info(); @@ -843,36 +926,51 @@ mod tests { .map(UserIdentifier::from) .collect::>(); + let mut agg = PeerConnectionSetupAggregator::default(); + + for peer in peers { + agg = agg + .with_peer_custom(peer) + .ensure_registered() + .with_udp_mode(udp_mode) + .with_session_security_settings(SessionSecuritySettings::default()) + .add(); + } + let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); let client_kernel = PeerConnectionKernel::new( server_connection_settings, - peers, - move |mut results, remote| async move { - let mut success = 0; + agg, + move |results, remote| async move { + log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); let implicated_cid = remote.conn_type.get_implicated_cid(); - while let Some(conn) = results.recv().await { - log::trace!(target: "citadel", "User {} received {:?}", uuid, conn); - let conn = conn?; - crate::test_common::p2p_assertions(implicated_cid, &conn).await; - + let check = move |conn: PeerConnectSuccess| async move { if idx == 0 { for x in 1..10 { - assert_eq!(conn.remote.rekey().await?, Some(x)); + assert_eq!( + conn.remote.rekey().await.expect("Failed to rekey"), + Some(x) + ); } } - success += 1; - if success == peer_count - 1 { - break; - } - } + conn + }; + let _ = handle_peer_connect_successes( + results, + implicated_cid, + peer_count, + udp_mode, + check, + ) + .await; - log::trace!(target: "citadel", "***PEER {} CONNECT RESULT: {}***", uuid, success); + log::info!(target: "citadel", "***PEER {uuid} finished all checks***"); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; remote.shutdown_kernel().await @@ -899,13 +997,14 @@ mod tests { #[rstest] #[case(2)] #[timeout(std::time::Duration::from_secs(90))] - #[citadel_io::tokio::test(flavor = "multi_thread")] + #[tokio::test(flavor = "multi_thread")] async fn test_peer_to_peer_disconnect( #[case] peer_count: usize, ) -> Result<(), Box> { assert!(peer_count > 1); citadel_logging::setup_log(); TestBarrier::setup(peer_count); + let udp_mode = UdpMode::Enabled; let client_success = &AtomicUsize::new(0); let (server, server_addr) = server_info(); @@ -924,30 +1023,46 @@ mod tests { .map(UserIdentifier::from) .collect::>(); + let mut agg = PeerConnectionSetupAggregator::default(); + + for peer in peers { + agg = agg + .with_peer_custom(peer) + .ensure_registered() + .with_udp_mode(udp_mode) + .with_session_security_settings(SessionSecuritySettings::default()) + .add(); + } + let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); let client_kernel = PeerConnectionKernel::new( server_connection_settings, - peers, - move |mut results, remote| async move { - let mut success = 0; + agg, + move |results, remote| async move { + log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); let implicated_cid = remote.conn_type.get_implicated_cid(); - while let Some(conn) = results.recv().await { - log::trace!(target: "citadel", "User {} received {:?}", uuid, conn); - let conn = conn?; - crate::test_common::p2p_assertions(implicated_cid, &conn).await; - conn.remote.disconnect().await?; - success += 1; - if success == peer_count - 1 { - break; - } - } + let check = move |conn: PeerConnectSuccess| async move { + conn.remote + .disconnect() + .await + .expect("Failed to p2p disconnect"); + conn + }; + let _ = handle_peer_connect_successes( + results, + implicated_cid, + peer_count, + udp_mode, + check, + ) + .await; + log::info!(target: "citadel", "***PEER {uuid} finished all checks***"); - log::trace!(target: "citadel", "***PEER {} CONNECT RESULT: {}***", uuid, success); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; remote.shutdown_kernel().await @@ -997,6 +1112,7 @@ mod tests { let mut peer0_agg = PeerConnectionSetupAggregator::default() .with_peer_custom(uuid1) + .ensure_registered() .with_session_security_settings(session_security); if let Some(password) = p2p_password { @@ -1007,6 +1123,7 @@ mod tests { let mut peer1_agg = PeerConnectionSetupAggregator::default() .with_peer_custom(uuid0) + .ensure_registered() .with_session_security_settings(session_security); if let Some(_password) = p2p_password { @@ -1016,14 +1133,14 @@ mod tests { let peer1_connection = peer1_agg.add(); let server_connection_settings0 = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid0) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() .unwrap(); let server_connection_settings1 = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid1) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -1078,4 +1195,83 @@ mod tests { assert!(!peer_0_error_received.load(Ordering::SeqCst)); assert!(!peer_1_error_received.load(Ordering::SeqCst)); } + + async fn handle_peer_connect_successes( + mut conn_rx: Receiver>, + implicated_cid: u64, + peer_count: usize, + udp_mode: UdpMode, + checks: F, + ) -> Vec + where + F: for<'a> Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, + Fut: Future + Send, + { + let (finished_tx, finished_rx) = tokio::sync::oneshot::channel(); + + let task = async move { + let (done_tx, mut done_rx) = tokio::sync::mpsc::unbounded_channel(); + let mut conns = vec![]; + while let Some(conn) = conn_rx.recv().await { + conns.push(conn); + if conns.len() == peer_count - 1 { + break; + } + } + + for conn in conns { + let conn = conn.expect("Error receiving peer connection"); + handle_peer_connect_success( + conn, + done_tx.clone(), + implicated_cid, + udp_mode, + checks.clone(), + ); + } + + // Now, wait for all to finish + let mut ret = vec![]; + while let Some(done) = done_rx.recv().await { + ret.push(done); + if ret.len() == peer_count - 1 { + break; + } + } + + finished_tx + .send(ret) + .expect("Error sending finished signal in handle_peer_connect_successes"); + }; + + drop(tokio::task::spawn(task)); + let ret = finished_rx + .await + .expect("Error receiving finished signal in handle_peer_connect_successes"); + + assert_eq!(ret.len(), peer_count - 1); + ret + } + + fn handle_peer_connect_success( + mut conn: PeerConnectSuccess, + done_tx: UnboundedSender, + implicated_cid: u64, + udp_mode: UdpMode, + checks: F, + ) where + F: Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, + Fut: Future + Send, + { + let task = async move { + crate::test_common::p2p_assertions(implicated_cid, &conn).await; + crate::test_common::udp_mode_assertions(udp_mode, conn.udp_channel_rx.take()).await; + let conn = checks(conn).await; + done_tx + .send(conn) + .expect("Error sending done signal in handle_peer_connect_success"); + }; + + drop(tokio::task::spawn(task)); + } } diff --git a/citadel_sdk/src/prefabs/client/single_connection.rs b/citadel_sdk/src/prefabs/client/single_connection.rs index 70701bbd1..d2428e1d4 100644 --- a/citadel_sdk/src/prefabs/client/single_connection.rs +++ b/citadel_sdk/src/prefabs/client/single_connection.rs @@ -41,7 +41,7 @@ pub(crate) enum ConnectionType { username: String, password: SecBuffer, }, - Passwordless { + Transient { uuid: Uuid, server_addr: SocketAddr, }, @@ -78,11 +78,11 @@ where username, password, .. } => ConnectionType::Connect { username, password }, - ServerConnectionSettings::NoCredentials { + ServerConnectionSettings::Transient { server_addr: address, uuid, .. - } => ConnectionType::Passwordless { + } => ConnectionType::Transient { uuid, server_addr: address, }, @@ -174,8 +174,8 @@ where AuthenticationRequest::credentialed(username, password) } - ConnectionType::Passwordless { uuid, server_addr } => { - AuthenticationRequest::passwordless(uuid, server_addr) + ConnectionType::Transient { uuid, server_addr } => { + AuthenticationRequest::transient(uuid, server_addr) } }; @@ -289,7 +289,8 @@ mod tests { #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_single_connection_registered( #[values(UdpMode::Enabled, UdpMode::Disabled)] udp_mode: UdpMode, - #[values(ServerUnderlyingProtocol::new_quic_self_signed(), ServerUnderlyingProtocol::new_tls_self_signed().unwrap())] + #[values(ServerUnderlyingProtocol::new_quic_self_signed(), ServerUnderlyingProtocol::new_tls_self_signed().unwrap() + )] underlying_protocol: ServerUnderlyingProtocol, ) { citadel_logging::setup_log(); @@ -350,7 +351,7 @@ mod tests { #[case(UdpMode::Enabled, Some("test-password"))] #[timeout(std::time::Duration::from_secs(90))] #[citadel_io::tokio::test(flavor = "multi_thread")] - async fn test_single_connection_passwordless( + async fn test_single_connection_transient( #[case] udp_mode: UdpMode, #[case] server_password: Option<&'static str>, ) { @@ -373,7 +374,7 @@ mod tests { let uuid = Uuid::new_v4(); let mut server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode); if let Some(server_password) = server_password { @@ -410,7 +411,7 @@ mod tests { #[case(UdpMode::Enabled, Some("test-password"))] #[timeout(std::time::Duration::from_secs(90))] #[tokio::test(flavor = "multi_thread")] - async fn test_single_connection_passwordless_wrong_password( + async fn test_single_connection_transient_wrong_password( #[case] udp_mode: UdpMode, #[case] server_password: Option<&'static str>, ) { @@ -429,7 +430,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .with_session_password("wrong-password") .build() @@ -461,7 +462,7 @@ mod tests { #[case(UdpMode::Disabled)] #[timeout(std::time::Duration::from_secs(90))] #[citadel_io::tokio::test(flavor = "multi_thread")] - async fn test_single_connection_passwordless_deregister(#[case] udp_mode: UdpMode) { + async fn test_single_connection_transient_deregister(#[case] udp_mode: UdpMode) { citadel_logging::setup_log(); TestBarrier::setup(2); @@ -478,7 +479,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .build() .unwrap(); @@ -527,7 +528,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .build() .unwrap(); @@ -605,7 +606,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .build() .unwrap(); diff --git a/citadel_sdk/src/prefabs/mod.rs b/citadel_sdk/src/prefabs/mod.rs index 39cfe120a..8bb285190 100644 --- a/citadel_sdk/src/prefabs/mod.rs +++ b/citadel_sdk/src/prefabs/mod.rs @@ -71,7 +71,7 @@ impl TargetLockedRemote for ClientServerRemote { fn remote(&self) -> &NodeRemote { &self.inner } - fn target_username(&self) -> Option<&String> { + fn target_username(&self) -> Option<&str> { None } fn user_mut(&mut self) -> &mut VirtualTargetType { diff --git a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs index 2861fb195..02410bfc6 100644 --- a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs +++ b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs @@ -1,5 +1,4 @@ use crate::prelude::*; -use futures::StreamExt; #[derive(Default)] pub struct AcceptFileTransferKernel; @@ -16,11 +15,11 @@ impl NetKernel for AcceptFileTransferKernel { async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { if let NodeResult::ObjectTransferHandle(mut handle) = message { - handle + let _ = handle .handle - .accept() + .exhaust_stream() + .await .map_err(|err| NetworkError::Generic(err.into_string()))?; - exhaust_file_transfer(handle.handle); } Ok(()) @@ -30,20 +29,3 @@ impl NetKernel for AcceptFileTransferKernel { Ok(()) } } - -pub fn exhaust_file_transfer(mut handle: ObjectTransferHandler) { - // Exhaust the stream - let handle = citadel_io::tokio::task::spawn(async move { - while let Some(evt) = handle.next().await { - log::info!(target: "citadel", "File Transfer Event: {evt:?}"); - if let ObjectTransferStatus::Fail(err) = &evt { - log::error!(target: "citadel", "File Transfer Failed: {err:?}"); - std::process::exit(1); - } else if let ObjectTransferStatus::TransferComplete = &evt { - break; - } - } - }); - - drop(handle); -} diff --git a/citadel_sdk/src/prefabs/server/internal_service.rs b/citadel_sdk/src/prefabs/server/internal_service.rs index 7f3064dbb..2f644e090 100644 --- a/citadel_sdk/src/prefabs/server/internal_service.rs +++ b/citadel_sdk/src/prefabs/server/internal_service.rs @@ -133,7 +133,7 @@ mod test { }); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_bind_addr, Uuid::new_v4()) + ServerConnectionSettingsBuilder::transient_with_id(server_bind_addr, Uuid::new_v4()) .build() .unwrap(); @@ -214,7 +214,7 @@ mod test { }); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_bind_addr, Uuid::new_v4()) + ServerConnectionSettingsBuilder::transient_with_id(server_bind_addr, Uuid::new_v4()) .build() .unwrap(); @@ -262,7 +262,7 @@ mod test { Ok(()) }, ) - .await + .await }, ); diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index b300dd9b2..d0dde6b76 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -41,7 +41,7 @@ pub(crate) mod user_ids { pub trait TargetLockedRemote: Send + Sync { fn user(&self) -> &VirtualTargetType; fn remote(&self) -> &NodeRemote; - fn target_username(&self) -> Option<&String>; + fn target_username(&self) -> Option<&str>; fn user_mut(&mut self) -> &mut VirtualTargetType; fn session_security_settings(&self) -> Option<&SessionSecuritySettings>; } @@ -53,8 +53,8 @@ pub(crate) mod user_ids { fn remote(&self) -> &NodeRemote { self.remote } - fn target_username(&self) -> Option<&String> { - self.target_username.as_ref() + fn target_username(&self) -> Option<&str> { + self.target_username.as_deref() } fn user_mut(&mut self) -> &mut VirtualTargetType { &mut self.user @@ -72,8 +72,8 @@ pub(crate) mod user_ids { fn remote(&self) -> &NodeRemote { &self.remote } - fn target_username(&self) -> Option<&String> { - self.target_username.as_ref() + fn target_username(&self) -> Option<&str> { + self.target_username.as_deref() } fn user_mut(&mut self) -> &mut VirtualTargetType { &mut self.user @@ -555,22 +555,10 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { while let Some(event) = stream.next().await { match map_errors(event)? { NodeResult::ObjectTransferHandle(ObjectTransferHandle { mut handle, .. }) => { - while let Some(res) = handle.next().await { - log::trace!(target: "citadel", "Client received RES {res:?}"); - match res { - ObjectTransferStatus::TransferComplete => { - return Ok(()); - } - - ObjectTransferStatus::Fail(err) => { - return Err(NetworkError::Generic(format!( - "File transfer failed: {err:?}" - ))); - } - - _ => {} - } - } + return handle + .transfer_file() + .await + .map_err(|err| NetworkError::Generic(err.into_string())); } NodeResult::PeerEvent(PeerEvent { @@ -656,28 +644,10 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { while let Some(event) = stream.next().await { match map_errors(event)? { NodeResult::ObjectTransferHandle(ObjectTransferHandle { mut handle, .. }) => { - let mut local_path = None; - while let Some(res) = handle.next().await { - match res { - ObjectTransferStatus::ReceptionBeginning(path, _) => { - local_path = Some(path) - } - ObjectTransferStatus::TransferComplete => { - break; - } - - ObjectTransferStatus::Fail(err) => { - return Err(NetworkError::Generic(format!( - "File download failed: {err:?}" - ))); - } - - _ => {} - } - } - - return local_path - .ok_or(NetworkError::InternalError("Local path never loaded")); + return handle + .receive_file() + .await + .map_err(|err| NetworkError::Generic(err.into_string())); } NodeResult::PeerEvent(PeerEvent { @@ -717,10 +687,10 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { while let Some(event) = stream.next().await { match map_errors(event)? { NodeResult::ReVFS(result) => { - if let Some(error) = result.error_message { - return Err(NetworkError::Generic(error)); + return if let Some(error) = result.error_message { + Err(NetworkError::Generic(error)) } else { - return Ok(()); + Ok(()) } } @@ -765,7 +735,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { channel, udp_rx_opt, }) => { - let username = self.target_username().cloned(); + let username = self.target_username().map(ToString::to_string); let remote = PeerRemote { inner: self.remote().clone(), peer: peer_target.as_virtual_connection(), @@ -823,7 +793,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { .get_username_by_cid(implicated_cid) .await? .ok_or_else(|| NetworkError::msg("Unable to find username for local user"))?; - let peer_username_opt = self.target_username().cloned(); + let peer_username_opt = self.target_username().map(ToString::to_string); let mut stream = self .remote() @@ -1137,14 +1107,13 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { // where the username was provided, but the cid was 0 (unknown). let peer_username = self .target_username() - .ok_or_else(|| NetworkError::msg("target_cid=0, yet, no username was provided"))? - .clone(); + .ok_or_else(|| NetworkError::msg("target_cid=0, yet, no username was provided"))?; let implicated_cid = self.user().get_implicated_cid(); let expected_peer_cid = self .remote() .account_manager() .get_persistence_handler() - .get_cid_by_username(&peer_username); + .get_cid_by_username(peer_username); // get the peer cid from the account manager (implying the peers are already registered). // fallback to the mapped cid if the peer is not registered let peer_cid = self @@ -1253,8 +1222,8 @@ pub mod remote_specialization { fn remote(&self) -> &NodeRemote { &self.inner } - fn target_username(&self) -> Option<&String> { - self.username.as_ref() + fn target_username(&self) -> Option<&str> { + self.username.as_deref() } fn user_mut(&mut self) -> &mut VirtualTargetType { &mut self.peer @@ -1376,7 +1345,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_session_security_settings(session_security_settings) .disable_udp() .build() diff --git a/citadel_sdk/tests/stress_tests.rs b/citadel_sdk/tests/stress_tests.rs index 3a9416a61..28b955bf6 100644 --- a/citadel_sdk/tests/stress_tests.rs +++ b/citadel_sdk/tests/stress_tests.rs @@ -243,7 +243,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -316,7 +316,7 @@ mod tests { .unwrap(); let mut connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security); @@ -402,7 +402,7 @@ mod tests { let peer1_connection = peer1_agg.add(); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid0) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -425,7 +425,7 @@ mod tests { ); let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid1) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -506,7 +506,7 @@ mod tests { }; let server_connection_settings = - ServerConnectionSettingsBuilder::no_credentials(server_addr, uuid) + ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); diff --git a/citadel_user/Cargo.toml b/citadel_user/Cargo.toml index abc7a156c..8cf6074c9 100644 --- a/citadel_user/Cargo.toml +++ b/citadel_user/Cargo.toml @@ -47,7 +47,7 @@ serde_json = { workspace = true, features = ["alloc"] } bytes = { workspace = true } bstr = { workspace = true, features = ["alloc", "unicode"] } sqlx = { workspace = true, features = ["all-databases", "runtime-tokio-native-tls"], optional = true } -redis-base = { package = "redis", workspace = true, features = ["tokio-comp", "tokio-native-tls-comp"], optional=true } +redis-base = { workspace = true, features = ["tokio-comp", "tokio-native-tls-comp"], optional=true } mobc = { workspace = true, optional = true, features = ["tokio"] } firebase-rtdb = { workspace = true, optional = true } jwt = { workspace = true, features = ["openssl"], optional = true } diff --git a/citadel_user/src/backend/utils/mod.rs b/citadel_user/src/backend/utils/mod.rs index 2574507ec..1a51184c7 100644 --- a/citadel_user/src/backend/utils/mod.rs +++ b/citadel_user/src/backend/utils/mod.rs @@ -1,5 +1,6 @@ use futures::Stream; use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; use std::pin::Pin; use std::task::{Context, Poll}; @@ -100,6 +101,70 @@ impl ObjectTransferHandler { (this, tx) } + /// Exhausts the steam, independently of the orientation + /// If the orientation is Sender, this will return no path + /// If the orientation is Receiver, this will return the path of the received file + pub async fn exhaust_stream(&mut self) -> Result, AccountError> { + self.accept()?; + + let mut save_path = None; + while let Some(event) = self.inner.inner.recv().await { + match event { + ObjectTransferStatus::ReceptionBeginning(path, _) => { + save_path = Some(path); + } + ObjectTransferStatus::ReceptionComplete => { + return Ok(save_path); + } + + ObjectTransferStatus::TransferComplete => { + return Ok(None); + } + + ObjectTransferStatus::Fail(err) => { + return Err(AccountError::msg(err)); + } + + _ => {} + } + } + + Err(AccountError::msg("Failed to receive file: stream ended")) + } + + /// Receives the file, exhausting the underlying stream and returning the save path + /// after completion. + /// + /// If the orientation is Sender, this will return an error + pub async fn receive_file(&mut self) -> Result { + if !matches!(self.orientation, ObjectTransferOrientation::Receiver { .. }) { + return Err(AccountError::msg( + "Cannot receive file: orientation is not Receiver", + )); + } + + let file = self.exhaust_stream().await?; + file.ok_or_else(|| AccountError::msg("Failed to receive file: no file path")) + } + + /// Transfers the file, exhausting the underlying stream + /// + /// If the orientation is Receiver, this will return an error + pub async fn transfer_file(&mut self) -> Result<(), AccountError> { + if !matches!(self.orientation, ObjectTransferOrientation::Sender { .. }) { + return Err(AccountError::msg( + "Cannot transfer file: orientation is not Sender", + )); + } + + let file = self.exhaust_stream().await?; + if file.is_some() { + Err(AccountError::msg("An unexpected error occurred: file transfer occurred, yet, returned a save path. Please report to developers")) + } else { + Ok(()) + } + } + /// When the local handle type is for a Receiver, /// the receiver must accept the transfer before /// receiving the data diff --git a/citadel_user/src/external_services/google_auth.rs b/citadel_user/src/external_services/google_auth.rs index 7eb684ba6..7b6679496 100644 --- a/citadel_user/src/external_services/google_auth.rs +++ b/citadel_user/src/external_services/google_auth.rs @@ -21,7 +21,7 @@ impl GoogleAuth { pub async fn load_from_google_services_file>( path: P, ) -> Result { - let string = tokio::fs::read_to_string(path) + let string = citadel_io::tokio::fs::read_to_string(path) .await .map_err(|err| AccountError::Generic(err.to_string()))?; let mut map: HashMap = serde_json::from_str(string.as_str()) diff --git a/citadel_user/tests/crypto.rs b/citadel_user/tests/crypto.rs index 6d975ae95..bb08bfb6a 100644 --- a/citadel_user/tests/crypto.rs +++ b/citadel_user/tests/crypto.rs @@ -8,7 +8,7 @@ mod tests { use citadel_pqcrypto::constructor_opts::ConstructorOpts; use std::collections::HashMap; - #[tokio::test] + #[citadel_io::tokio::test] async fn jwt() { citadel_logging::setup_log(); const USER: u64 = 999; @@ -17,8 +17,8 @@ mod tests { citadel_user::external_services::google_auth::GoogleAuth::load_from_google_services_file( "/Users/nologik/googlesvc.json", ) - .await - .unwrap(); + .await + .unwrap(); let jwt = auth.sign_new_custom_jwt_auth(USER).unwrap(); log::trace!(target: "citadel", "JWT: {}", jwt); diff --git a/example-library/Cargo.toml b/example-library/Cargo.toml new file mode 100644 index 000000000..b2685befd --- /dev/null +++ b/example-library/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "citadel-examples" +description = "Example library for educational purposes" +authors = ["Thomas Braun "] +edition = "2021" +workspace = ".." +homepage = "https://avarok.net/" +repository = "https://github.com/Avarok-Cybersecurity/Citadel-Protocol" +readme = "../README.md" +categories = ["cryptography", "network-programming", "asynchronous"] +license = "MIT OR Apache-2.0" +publish = false + +[[example]] +name = "server_echo" +path = "examples/c2s/server_echo.rs" + +[[example]] +name = "client_echo" +path = "examples/c2s/client_echo.rs" + +[[example]] +name = "server_basic" +path = "examples/c2s/server_basic.rs" + +[[example]] +name = "server_basic_with_password" +path = "examples/c2s/server_basic_with_password.rs" + +[[example]] +name = "client_basic_with_password" +path = "examples/c2s/client_basic_with_server_password.rs" + +[[example]] +name = "client_basic_transient_connection" +path = "examples/c2s/client_basic_transient_connection.rs" + +[[example]] +name = "p2p_chat" +path = "examples/p2p/chat.rs" + +[[example]] +name = "file_transfer" +path = "examples/p2p/file_transfer.rs" + +[[example]] +name = "p2p_refvs_read_write" +path = "examples/p2p/revfs_read_write.rs" + +[[example]] +name = "p2p_refvs_take" +path = "examples/p2p/revfs_take.rs" + +[[example]] +name = "p2p_refvs_delete" +path = "examples/p2p/revfs_delete.rs" + +[dependencies] +citadel_sdk = { path = "../citadel_sdk" } +tokio = { version = "1", features = ["full"] } +futures = "0.3" \ No newline at end of file diff --git a/example-library/README.md b/example-library/README.md new file mode 100644 index 000000000..4fc868c92 --- /dev/null +++ b/example-library/README.md @@ -0,0 +1,103 @@ +# Citadel Protocol Examples + +This directory contains examples demonstrating how to use the Citadel Protocol for various networking scenarios, including client-server (C2S) and peer-to-peer (P2P) communication patterns. + +## Important First Step: Running a Server + +**Before running any example, you must have a Citadel server running!** + +1. First, set the server address: +```bash +export CITADEL_SERVER_ADDR="127.0.0.1:25000" +``` + +2. Start a basic server: +```bash +cargo run --example server_basic +``` + +3. Keep this server running while you try other examples in separate terminals. + +## Prerequisites + +Additional environment variables needed for specific examples: + +```bash +# For P2P examples +export CITADEL_MY_USER="user1" # Your username +export CITADEL_OTHER_USER="user2" # Peer's username you want to connect to +``` + +## Available Examples + +### Client-Server (C2S) Examples + +1. **Basic Client Examples** + - `client_basic_transient_connection.rs`: Demonstrates temporary connections without persistent user accounts + - `client_basic_with_server_password.rs`: Shows how to connect to password-protected servers + - `client_echo.rs`: A simple echo client demonstrating basic message exchange using a credentialed, persistent account + +2. **Server Examples** + - `server_basic.rs`: A basic Citadel server implementation + - `server_basic_with_password.rs`: Server with password protection enabled + - `server_echo.rs`: Echo server implementation responding to client messages + +### Peer-to-Peer (P2P) Examples + +1. **Chat Application** + - `chat.rs`: Interactive P2P chat application using standard input/output + +2. **File Operations** + - `file_transfer.rs`: Secure file transfer between peers + +3. **Remote Encrypted Virtual Filesystem (RE-VFS)** + - `revfs_read_write.rs`: Basic read/write operations using RE-VFS + - `revfs_delete.rs`: File deletion operations + - `revfs_take.rs`: Takes a file from the RE-VFS, deleting it from the RE-VFS in the process + +## Running the Examples + +### For Client-Server Examples: + +1. Make sure you have a server running (see "Important First Step" above) + +2. Then run a client: +```bash +# Basic client +cargo run --example client_basic_transient_connection + +# Or with server password +cargo run --example client_basic_with_server_password +``` + +### For P2P Examples: + +1. Make sure you have a server running (see "Important First Step" above) + +2. For the chat example, run two instances: +```bash +# First peer +export CITADEL_MY_USER="user1" +export CITADEL_OTHER_USER="user2" +cargo run --example chat + +# Second peer (in another terminal) +export CITADEL_MY_USER="user2" +export CITADEL_OTHER_USER="user1" +cargo run --example chat +``` + +3. For file transfer: +```bash +# Sender +export CITADEL_MY_USER="sender" +export CITADEL_OTHER_USER="receiver" +cargo run --example file_transfer + +# Receiver (in another terminal) +export CITADEL_MY_USER="receiver" +export CITADEL_OTHER_USER="sender" +cargo run --example file_transfer +``` + +Each example contains detailed documentation at the top of its source file explaining specific usage and features. For more detailed information about each example, see the [examples/README.md](examples/README.md) file. diff --git a/example-library/examples/c2s/client_basic_transient_connection.rs b/example-library/examples/c2s/client_basic_transient_connection.rs new file mode 100644 index 000000000..89fa36b7e --- /dev/null +++ b/example-library/examples/c2s/client_basic_transient_connection.rs @@ -0,0 +1,75 @@ +//! # Basic Transient Connection Example +//! +//! This example demonstrates how to create a temporary, non-persistent connection +//! to a Citadel server. A transient connection exists only for the duration of +//! the session and does not maintain any state between connections. +//! +//! ## Features Demonstrated +//! - Transient (temporary) connections +//! - Basic client-server communication +//! - Session-only state management +//! - Connection cleanup handling +//! +//! ## Usage +//! ```bash +//! export CITADEL_SERVER_ADDR="127.0.0.1:25000" +//! cargo run --example client_basic_transient_connection +//! ``` +//! +//! ## How it Works +//! 1. Client establishes temporary connection to server +//! 2. No persistent account or credentials are created +//! 3. Connection remains active for the session duration +//! 4. All state is cleared when connection closes +//! +//! ## Use Cases +//! Transient connections are ideal for: +//! - Temporary or one-time connections +//! - Applications without need for persistence +//! - Testing and development +//! - Scenarios where state persistence isn't required +//! +//! ## Security Note +//! While transient connections don't maintain persistent identity, the server +//! can still enforce security through client certificates at the transport +//! layer to ensure only authorized clients can connect. + +use citadel_sdk::{ + prefabs::client::single_connection::SingleClientServerConnectionKernel, prelude::*, +}; +use std::env; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + println!("Connecting to server at {}", server_addr); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings. If a custom transient ID is required, use `transient_with_id` over `transient`. + let server_connection_settings = ServerConnectionSettingsBuilder::transient(server_addr) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; + + // Create client kernel + let kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, + |connect_success, remote| async move { + println!("Connected to server! CID: {}", connect_success.cid); + remote.shutdown_kernel().await + }, + ); + + // Build the node + let client = NodeBuilder::default().build(kernel)?; + + // Run the node + client.await?; + + Ok(()) +} diff --git a/example-library/examples/c2s/client_basic_with_server_password.rs b/example-library/examples/c2s/client_basic_with_server_password.rs new file mode 100644 index 000000000..ab7154fcf --- /dev/null +++ b/example-library/examples/c2s/client_basic_with_server_password.rs @@ -0,0 +1,85 @@ +//! # Password-Protected Server Connection Example +//! +//! This example demonstrates how to connect to a Citadel server that requires +//! a password for authentication. It shows proper password handling and secure +//! connection establishment. +//! +//! ## Features Demonstrated +//! - Server password authentication +//! - Perfect Forward Secrecy mode +//! - Secure password handling +//! - Connection establishment with protected server +//! - Error handling for authentication failures +//! +//! ## Prerequisites +//! - A running Citadel server with password protection enabled +//! (use `server_basic_with_password.rs`) +//! +//! ## Usage +//! ```bash +//! export CITADEL_SERVER_ADDR="127.0.0.1:25000" +//! export CITADEL_SERVER_PASSWORD="your_secure_password" +//! cargo run --example client_basic_with_server_password +//! ``` +//! +//! ## Security Notes +//! - Never hardcode passwords in production code +//! - Avoid storing passwords in environment variables in production +//! - Use secure password management systems for production deployments +//! - The server password is different from user account credentials +//! +//! ## How it Works +//! 1. Client reads server password from environment +//! 2. Establishes encrypted connection to server +//! 3. Performs password-based authentication +//! 4. Maintains secure session after authentication + +use citadel_sdk::{ + prefabs::client::single_connection::SingleClientServerConnectionKernel, prelude::*, +}; +use std::env; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + // Security note: Do not hardcode password in production into the environment. It exposes your application + // to local attacks resultant from rogue processes or users that scan the env for secrets. + let connect_password = + env::var("CITADEL_SERVER_PASSWORD").expect("CITADEL_SERVER_PASSWORD not set"); + println!("Connecting to server at {}", server_addr); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + "my_username", + "My Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .with_session_password(connect_password) + .disable_udp() + .build()?; + + // Create client kernel + let kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, + |connect_success, remote| async move { + println!("Connected to server! CID: {}", connect_success.cid); + remote.shutdown_kernel().await + }, + ); + + // Build the node + let client = NodeBuilder::default().build(kernel)?; + + // Run the node + client.await?; + + Ok(()) +} diff --git a/example-library/examples/c2s/client_echo.rs b/example-library/examples/c2s/client_echo.rs new file mode 100644 index 000000000..d4ca2d5e0 --- /dev/null +++ b/example-library/examples/c2s/client_echo.rs @@ -0,0 +1,96 @@ +//! # Echo Client Example with Persistent Account +//! +//! This example demonstrates how to create a client that establishes a persistent, +//! credentialed connection with a Citadel server and exchanges messages. Unlike the +//! transient connection example, this client creates a persistent account that can +//! be reused across sessions. +//! +//! ## Features Demonstrated +//! - Credentialed registration with persistent account +//! - Perfect Forward Secrecy mode for enhanced security +//! - Bidirectional message exchange with server +//! - Channel-based communication +//! - Error handling and connection management +//! +//! ## Prerequisites +//! - A running Citadel server (use `server_echo.rs` for full functionality) +//! - Server must be configured to accept credentialed connections +//! +//! ## Usage +//! ```bash +//! export CITADEL_SERVER_ADDR="127.0.0.1:25000" +//! cargo run --example client_echo +//! ``` +//! +//! ## How it Works +//! 1. Establishes a credentialed connection with username/password +//! 2. Creates a secure channel with the server +//! 3. Sends a message and waits for the server's echo response +//! 4. Demonstrates proper connection cleanup on exit + +use citadel_sdk::{ + prefabs::client::single_connection::SingleClientServerConnectionKernel, prelude::*, +}; +use futures::StreamExt; +use std::env; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + println!("Connecting to server at {}", server_addr); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + "my_username", + "My Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; + + // Create client kernel + let kernel = SingleClientServerConnectionKernel::new( + server_connection_settings, + |connect_success, remote| async move { + println!("Connected to server! CID: {}", connect_success.cid); + let (tx, mut rx) = connect_success.channel.split(); + + let message = "Hello from client!"; + // Send initial message + let msg = SecBuffer::from(message); + if let Err(e) = tx.send_message(msg.into()).await { + println!("Error sending message: {}", e); + return Err(e); + } + + // Receive messages using Stream trait + if let Some(echo) = rx.next().await { + let response = String::from_utf8(echo.as_ref().to_vec()) + .expect("Failed to convert message to string"); + println!("Received echo from server: {response}",); + assert_eq!(&response, message); + } else { + println!("No message received from server"); + return Err(NetworkError::msg("No message received from server")); + } + + remote.shutdown_kernel().await + }, + ); + + // Build the node + let client = NodeBuilder::default().build(kernel)?; + + // Run the node + client.await?; + + Ok(()) +} diff --git a/example-library/examples/c2s/server_basic.rs b/example-library/examples/c2s/server_basic.rs new file mode 100644 index 000000000..14b751dd4 --- /dev/null +++ b/example-library/examples/c2s/server_basic.rs @@ -0,0 +1,49 @@ +//! # Basic Citadel Server Example +//! +//! This example demonstrates how to create a basic Citadel server that facilitates +//! peer-to-peer and group connections. This is the simplest form of a Citadel server, +//! which acts primarily as a connection broker. +//! +//! ## Features Demonstrated +//! - Basic server setup +//! - Connection brokering for P2P and group connections +//! - Server configuration using environment variables +//! - Use of the EmptyKernel for simple connection handling +//! +//! ## Usage +//! ```bash +//! export CITADEL_SERVER_ADDR="127.0.0.1:25000" +//! cargo run --example server_basic +//! ``` +//! +//! ## Note +//! This server only facilitates connections between peers. It does not handle direct +//! client-server communication. For bidirectional client-server communication, +//! see the `server_echo.rs` example which uses a `ClientConnectListenerKernel`. + +use citadel_sdk::prefabs::server; +use citadel_sdk::prelude::*; +use std::env; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + println!("Starting server on {}", server_addr); + + // This is a basic server. It will only help facilitate p2p and group connections. + // Clients will not be able to communicate directly with this server through channels. + // If post-connection client-server bidirectional communication is needed, use a + // `ClientConnectListenerKernel` instead which runs a closure each time a new connection is + // established with a client + let kernel = server::empty::EmptyKernel; + + // Build the server + let node = NodeBuilder::default() + .with_node_type(NodeType::server(server_addr)?) + .build(kernel)?; + + // Run the server + node.await?; + + Ok(()) +} diff --git a/example-library/examples/c2s/server_basic_with_password.rs b/example-library/examples/c2s/server_basic_with_password.rs new file mode 100644 index 000000000..42cca905c --- /dev/null +++ b/example-library/examples/c2s/server_basic_with_password.rs @@ -0,0 +1,70 @@ +//! # Password-Protected Citadel Server Example +//! +//! This example demonstrates how to create a Citadel server that requires password +//! authentication from clients. It provides an additional layer of security by +//! requiring clients to provide a valid password before establishing a connection. +//! +//! ## Features Demonstrated +//! - Server password protection +//! - Basic connection brokering +//! - Server configuration with environment variables +//! - Secure password handling +//! - Use of EmptyKernel for connection handling +//! +//! ## Usage +//! ```bash +//! export CITADEL_SERVER_ADDR="127.0.0.1:25000" +//! export CITADEL_SERVER_PASSWORD="your_secure_password" +//! cargo run --example server_basic_with_password +//! ``` +//! +//! ## Security Notes +//! - Never hardcode passwords in production code +//! - Avoid storing passwords in environment variables in production +//! - Use secure password management systems for production deployments +//! - Server password protects initial connection, different from user accounts +//! +//! ## How it Works +//! 1. Server starts with password protection enabled +//! 2. Clients must provide correct password to connect +//! 3. After authentication, server acts as connection broker +//! 4. Facilitates P2P and group connections between authenticated clients +//! +//! ## Note +//! This server only facilitates connections between peers. It does not handle +//! direct client-server communication. For bidirectional client-server +//! communication, use a `ClientConnectListenerKernel` instead. + +use citadel_sdk::prefabs::server; +use citadel_sdk::prelude::*; +use std::env; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + // Security note: Do not hardcode password in production into the environment. It exposes your application + // to local attacks resultant from rogue processes or users that scan the env for secrets. + let connect_password = + env::var("CITADEL_SERVER_PASSWORD").expect("CITADEL_SERVER_PASSWORD not set"); + println!("Starting server on {}", server_addr); + + // This is a basic server. It will only help facilitate p2p and group connections. + // Clients will not be able to communicate directly with this server through channels. + // If post-connection client-server bidirectional communication is needed, use a + // `ClientConnectListenerKernel` instead which runs each time a new connection is + // established with a client + let kernel = server::empty::EmptyKernel; + + // Build the server. It is password-protected, meaning that each time + // a client attempts to register or connect, they must provide the password. + // This "password" is effectively a pre-shared key (PSK) + let node = NodeBuilder::default() + .with_node_type(NodeType::server(server_addr)?) + .with_server_password(connect_password) + .build(kernel)?; + + // Run the server + node.await?; + + Ok(()) +} diff --git a/example-library/examples/c2s/server_echo.rs b/example-library/examples/c2s/server_echo.rs new file mode 100644 index 000000000..ab3f069f8 --- /dev/null +++ b/example-library/examples/c2s/server_echo.rs @@ -0,0 +1,81 @@ +//! # Echo Server Example +//! +//! This example demonstrates how to create a Citadel server that actively +//! communicates with clients. Unlike the basic server examples, this server +//! uses a `ClientConnectListenerKernel` to handle bidirectional communication +//! with connected clients. +//! +//! ## Features Demonstrated +//! - Active client-server communication +//! - ClientConnectListenerKernel usage +//! - Channel-based message handling +//! - Connection event handling +//! - Asynchronous message processing +//! +//! ## Usage +//! ```bash +//! export CITADEL_SERVER_ADDR="127.0.0.1:25000" +//! cargo run --example server_echo +//! ``` +//! +//! Then run the corresponding client: +//! ```bash +//! cargo run --example client_echo +//! ``` +//! +//! ## How it Works +//! 1. Server starts and listens for connections +//! 2. For each new client connection: +//! - Creates a new message channel +//! - Listens for incoming messages +//! - Echoes received messages back to the client +//! 3. Handles multiple clients concurrently +//! +//! ## Note +//! This server demonstrates active client-server communication using +//! the `ClientConnectListenerKernel`. It processes each message and +//! sends it back to the client, making it ideal for testing client +//! connectivity and message handling. + +use citadel_sdk::{ + prefabs::server::client_connect_listener::ClientConnectListenerKernel, prelude::*, +}; +use futures::StreamExt; +use std::env; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + println!("Starting server on {}", server_addr); + + // Set up the server kernel. The provided closure will be called every time a new client connects + let kernel = ClientConnectListenerKernel::new(move |conn_success, _remote| async move { + println!("New client connected! CID: {}", conn_success.cid); + + let (tx, mut rx) = conn_success.channel.split(); + while let Some(msg) = rx.next().await { + println!( + "Received message from client {}: {}", + conn_success.cid, + String::from_utf8_lossy(msg.as_ref()) + ); + + // Echo the message back + if let Err(e) = tx.send_message(msg.into()).await { + println!("Error sending response: {}", e); + } + } + + Ok(()) + }); + + // Build the server + let node = NodeBuilder::default() + .with_node_type(NodeType::server(server_addr)?) + .build(kernel)?; + + // Run the server + node.await?; + + Ok(()) +} diff --git a/example-library/examples/p2p/chat.rs b/example-library/examples/p2p/chat.rs new file mode 100644 index 000000000..dd2b119aa --- /dev/null +++ b/example-library/examples/p2p/chat.rs @@ -0,0 +1,127 @@ +//! # P2P Chat Example +//! +//! This example demonstrates how to implement a peer-to-peer chat application using the Citadel Protocol. +//! It shows how to: +//! - Set up secure P2P connections between two peers +//! - Handle real-time message exchange +//! - Use the Perfect Forward Secrecy mode for enhanced security +//! - Implement interactive input/output for chat functionality +//! +//! ## Usage +//! +//! Run two instances with different user identities: +//! ```bash +//! # First peer +//! export CITADEL_MY_USER="user1" +//! export CITADEL_OTHER_USER="user2" +//! cargo run --example chat +//! +//! # Second peer (in another terminal) +//! export CITADEL_MY_USER="user2" +//! export CITADEL_OTHER_USER="user1" +//! cargo run --example chat +//! ``` +//! +//! ## Features Demonstrated +//! - Secure P2P connection establishment +//! - Perfect Forward Secrecy mode +//! - Async message handling +//! - Interactive terminal I/O +//! - Error handling and connection management + +use citadel_sdk::{ + prefabs::client::peer_connection::{PeerConnectionKernel, PeerConnectionSetupAggregator}, + prelude::*, +}; +use futures::StreamExt; +use std::env; +use tokio::io::AsyncBufReadExt; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + let my_user = env::var("CITADEL_MY_USER").expect("MY_USER not set"); + let other_user = env::var("CITADEL_OTHER_USER").expect("OTHER_USER not set"); + + println!("Starting P2P chat as peer {my_user}"); + println!("Will connect to peer {other_user}"); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; + + // Create peer connection setup + let peer_connection = PeerConnectionSetupAggregator::default() + .with_peer_custom(other_user) + .with_session_security_settings(session_security) + .enable_udp() + .add(); + + // Set up the peer connection kernel + let kernel = PeerConnectionKernel::new( + server_connection_settings, + peer_connection, + move |mut connection, remote| async move { + println!("Connected to server successfully!"); + + // Wait for peer connection + let peer_conn = connection.recv().await.unwrap()?; + println!( + "Connected to peer {:?}!", + peer_conn.remote.target_username() + ); + + // Set up message handling + let (tx, mut message_stream) = peer_conn.channel.split(); + let mut stdin = tokio::io::BufReader::new(tokio::io::stdin()).lines(); + + println!("Type your messages (press Enter to send, Ctrl+C to quit):"); + + loop { + tokio::select! { + msg = message_stream.next() => { + if let Some(msg) = msg { + println!("\rReceived: {}", String::from_utf8_lossy(msg.as_ref())); + } + } + line = stdin.next_line() => { + match line { + Ok(Some(msg)) if !msg.is_empty() => { + tx.send_message(msg.into_bytes().into()).await?; + } + Ok(None) => break, // EOF + _ => continue, + } + } + _ = tokio::signal::ctrl_c() => { + println!("\nReceived Ctrl+C, shutting down..."); + break; + } + } + } + + remote.shutdown_kernel().await + }, + ); + + // Build the peer + let node = NodeBuilder::default().build(kernel)?; + + // Run the peer + node.await?; + + Ok(()) +} diff --git a/example-library/examples/p2p/file_transfer.rs b/example-library/examples/p2p/file_transfer.rs new file mode 100644 index 000000000..70c82cf85 --- /dev/null +++ b/example-library/examples/p2p/file_transfer.rs @@ -0,0 +1,113 @@ +//! # P2P File Transfer Example +//! +//! This example demonstrates how to set up a peer connection between two peers and transfer files +//! directly between them. This example uses direct file transfer rather than the RE-VFS system. +//! +//! ## Features Demonstrated +//! - Direct P2P file transfer +//! - Binary data streaming +//! - Progress tracking +//! - Error handling for file operations +//! +//! ## Usage +//! +//! Run two instances - one sender and one receiver: +//! ```bash +//! # Sender +//! export CITADEL_MY_USER="sender" +//! export CITADEL_OTHER_USER="receiver" +//! export IS_SENDER="true" +//! cargo run --example file_transfer +//! +//! # Receiver (in another terminal) +//! export CITADEL_MY_USER="receiver" +//! export CITADEL_OTHER_USER="sender" +//! cargo run --example file_transfer +//! ``` +//! +//! ## Note +//! This example demonstrates basic file transfer. For more advanced file operations, +//! see the RE-VFS examples which provide a virtual filesystem interface. + +use citadel_sdk::{ + prefabs::client::peer_connection::{PeerConnectionKernel, PeerConnectionSetupAggregator}, + prelude::*, +}; +use std::env; +use std::path::PathBuf; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + let my_user = env::var("CITADEL_MY_USER").expect("MY_USER not set"); + let other_user = env::var("CITADEL_OTHER_USER").expect("OTHER_USER not set"); + let is_sender = env::var("IS_SENDER").unwrap_or_default().to_lowercase() == "true"; + + println!("Starting file transfer as peer {my_user}"); + println!("Will connect to peer {other_user}"); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; + + // Create peer connection setup + let peer_connection = PeerConnectionSetupAggregator::default() + .with_peer_custom(other_user) + .with_session_security_settings(session_security) + .enable_udp() + .add(); + + let file_path = PathBuf::from("test_file.txt"); + if !file_path.exists() { + tokio::fs::write(&file_path, "Hello, this is a test file!").await?; + } + + // Set up the peer connection kernel + let kernel = PeerConnectionKernel::new( + server_connection_settings, + peer_connection, + move |mut connection, remote| async move { + println!("Connected to server successfully!"); + + // Wait for peer connection + let peer_conn = connection.recv().await.unwrap()?; + let peer_remote = peer_conn.remote; + println!("Connected to peer {:?}!", peer_remote.target_username()); + if is_sender { + peer_remote.send_file(file_path).await?; + } else { + let mut incoming_file_requests = + remote.get_incoming_file_transfer_handle().unwrap(); + let mut file_handle = incoming_file_requests.recv().await.unwrap(); + let downloaded_file = file_handle.receive_file().await?; + // Compare the contents in "file_path" and "downloaded_file" + let file_contents = tokio::fs::read_to_string(file_path).await?; + let downloaded_file_contents = tokio::fs::read_to_string(downloaded_file).await?; + assert_eq!(file_contents, downloaded_file_contents); + } + + remote.shutdown_kernel().await + }, + ); + + // Build the peer + let node = NodeBuilder::default().build(kernel)?; + + // Run the peer + node.await?; + + Ok(()) +} diff --git a/example-library/examples/p2p/revfs_delete.rs b/example-library/examples/p2p/revfs_delete.rs new file mode 100644 index 000000000..925a5c3d0 --- /dev/null +++ b/example-library/examples/p2p/revfs_delete.rs @@ -0,0 +1,131 @@ +//! # RE-VFS Delete Operation Example +//! +//! This example demonstrates how to use the Remote Encrypted Virtual Filesystem (RE-VFS) +//! to store files and then delete them. It shows the proper way to manage file cleanup +//! in the RE-VFS system. +//! +//! ## Features Demonstrated +//! - RE-VFS file storage +//! - Secure file deletion +//! - Permission handling for delete operations +//! - P2P connection establishment +//! - Error handling for file operations +//! +//! ## Usage +//! Run two instances - one sender and one with delete permissions: +//! ```bash +//! # Sender (stores file in RE-VFS) +//! export CITADEL_MY_USER="sender" +//! export CITADEL_OTHER_USER="manager" +//! export IS_SENDER="true" +//! cargo run --example revfs_delete +//! +//! # Manager (deletes file from RE-VFS) +//! export CITADEL_MY_USER="manager" +//! export CITADEL_OTHER_USER="sender" +//! cargo run --example revfs_delete +//! ``` +//! +//! ## How it Works +//! 1. Sender stores a file in the RE-VFS +//! 2. Manager connects with appropriate permissions +//! 3. Manager issues delete command for the file +//! 4. File is permanently removed from RE-VFS +//! +//! ## Note +//! Delete operations are permanent and cannot be undone. Ensure proper +//! permissions and verification before deleting files from the RE-VFS. +//! Unlike the `revfs_take` example, this operation doesn't retrieve the +//! file before deletion. + +use citadel_sdk::{ + prefabs::client::peer_connection::{PeerConnectionKernel, PeerConnectionSetupAggregator}, + prelude::*, +}; +use futures::StreamExt; +use std::env; +use std::path::PathBuf; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + let my_user = env::var("CITADEL_MY_USER").expect("MY_USER not set"); + let other_user = env::var("CITADEL_OTHER_USER").expect("OTHER_USER not set"); + let is_sender = env::var("IS_SENDER").unwrap_or_default().to_lowercase() == "true"; + + println!("Starting file transfer as peer {my_user}"); + println!("Will connect to peer {other_user}"); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; + + // Create peer connection setup + let peer_connection = PeerConnectionSetupAggregator::default() + .with_peer_custom(other_user) + .with_session_security_settings(session_security) + .enable_udp() + .add(); + + let file_path = PathBuf::from("test_file.txt"); + if !file_path.exists() { + tokio::fs::write(&file_path, "Hello, this is a test file!").await?; + } + + // Set up the peer connection kernel + let kernel = PeerConnectionKernel::new( + server_connection_settings, + peer_connection, + move |mut connection, remote| async move { + println!("Connected to server successfully!"); + + // Wait for peer connection + let peer_conn = connection.recv().await.unwrap()?; + let (tx, mut rx) = peer_conn.channel.split(); + let peer_remote = peer_conn.remote; + println!("Connected to peer {:?}!", peer_remote.target_username()); + + let virtual_file_path = "/home/foo/bar/test_file.txt"; + + if is_sender { + // Securely store the file on the remote peer. The peer cannot read the file contents + citadel_sdk::fs::write(&peer_remote, file_path.clone(), virtual_file_path).await?; + // Now, delete the contents of the file from the remote peer + citadel_sdk::fs::delete(&peer_remote, virtual_file_path).await?; + // Alert the other side that the file has been successfully processed + tx.send_message(SecBuffer::from("success").into()).await?; + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } else { + let incoming_file_requests = remote.get_incoming_file_transfer_handle().unwrap(); + // There will be a single file transfer handles sent to us: one for the file the peer sends to us + incoming_file_requests.accept_all(); + // Patiently wait for the "success" message from the sender + let msg = rx.next().await.unwrap(); + assert_eq!(msg.as_ref(), b"success"); + } + + remote.shutdown_kernel().await + }, + ); + + // Build the peer + let node = NodeBuilder::default().build(kernel)?; + + // Run the peer + node.await?; + + Ok(()) +} diff --git a/example-library/examples/p2p/revfs_read_write.rs b/example-library/examples/p2p/revfs_read_write.rs new file mode 100644 index 000000000..67d6380a5 --- /dev/null +++ b/example-library/examples/p2p/revfs_read_write.rs @@ -0,0 +1,136 @@ +//! # RE-VFS Read/Write Example +//! +//! This example demonstrates how to use the Remote Encrypted Virtual Filesystem (RE-VFS) +//! for basic file operations between peers. It shows how to store files in the RE-VFS +//! and retrieve them later, with all data being encrypted end-to-end. +//! +//! ## Features Demonstrated +//! - RE-VFS initialization and setup +//! - Secure file storage operations +//! - File retrieval from RE-VFS +//! - End-to-end encryption +//! - Error handling for file operations +//! +//! ## Usage +//! Run two instances - one sender and one receiver: +//! ```bash +//! # Sender (writes file to RE-VFS) +//! export CITADEL_MY_USER="sender" +//! export CITADEL_OTHER_USER="receiver" +//! export IS_SENDER="true" +//! cargo run --example revfs_read_write +//! +//! # Receiver (reads file from RE-VFS) +//! export CITADEL_MY_USER="receiver" +//! export CITADEL_OTHER_USER="sender" +//! cargo run --example revfs_read_write +//! ``` +//! +//! ## How it Works +//! 1. Sender stores a file in the RE-VFS using write operations +//! 2. File is encrypted and stored in the virtual filesystem +//! 3. Receiver connects and reads the file from RE-VFS +//! 4. File remains in RE-VFS for future access +//! +//! ## Note +//! Unlike the `revfs_take` example, reading a file does not remove it from +//! the RE-VFS. The file remains available for multiple reads by authorized peers. + +use citadel_sdk::{ + prefabs::client::peer_connection::{PeerConnectionKernel, PeerConnectionSetupAggregator}, + prelude::*, +}; +use futures::StreamExt; +use std::env; +use std::path::PathBuf; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + let my_user = env::var("CITADEL_MY_USER").expect("MY_USER not set"); + let other_user = env::var("CITADEL_OTHER_USER").expect("OTHER_USER not set"); + let is_sender = env::var("IS_SENDER").unwrap_or_default().to_lowercase() == "true"; + + println!("Starting file transfer as peer {my_user}"); + println!("Will connect to peer {other_user}"); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; + + // Create peer connection setup + let peer_connection = PeerConnectionSetupAggregator::default() + .with_peer_custom(other_user) + .with_session_security_settings(session_security) + .enable_udp() + .add(); + + let file_path = PathBuf::from("test_file.txt"); + if !file_path.exists() { + tokio::fs::write(&file_path, "Hello, this is a test file!").await?; + } + + // Set up the peer connection kernel + let kernel = PeerConnectionKernel::new( + server_connection_settings, + peer_connection, + move |mut connection, remote| async move { + println!("Connected to server successfully!"); + + // Wait for peer connection + let peer_conn = connection.recv().await.unwrap()?; + let (tx, mut rx) = peer_conn.channel.split(); + let peer_remote = peer_conn.remote; + println!("Connected to peer {:?}!", peer_remote.target_username()); + + let virtual_file_path = "/home/foo/bar/test_file.txt"; + + if is_sender { + // Securely store the file on the remote peer. The peer cannot read the file contents + citadel_sdk::fs::write(&peer_remote, file_path.clone(), virtual_file_path).await?; + // Now, download the contents of the file from the remote peer + let locally_downloaded_file = + citadel_sdk::fs::read(&peer_remote, virtual_file_path).await?; + // Compare the contents in "file_path" and "locally_downloaded_file" + let file_contents = tokio::fs::read_to_string(&file_path).await?; + let downloaded_file_contents = + tokio::fs::read_to_string(&locally_downloaded_file).await?; + assert_eq!(file_contents, downloaded_file_contents); + // Alert the other side that the file has been successfully stored + tx.send_message(SecBuffer::from("success").into()).await?; + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } else { + let incoming_file_requests = remote.get_incoming_file_transfer_handle().unwrap(); + // There will be two file transfer handles sent to us: one for the file the peer sends to us, + // and another for when the peer requests a file from us. We will accept both. + incoming_file_requests.accept_all(); + // Patiently wait for the "success" message from the sender + let msg = rx.next().await.unwrap(); + assert_eq!(msg.as_ref(), b"success"); + } + + remote.shutdown_kernel().await + }, + ); + + // Build the peer + let node = NodeBuilder::default().build(kernel)?; + + // Run the peer + node.await?; + + Ok(()) +} diff --git a/example-library/examples/p2p/revfs_take.rs b/example-library/examples/p2p/revfs_take.rs new file mode 100644 index 000000000..cfa94a931 --- /dev/null +++ b/example-library/examples/p2p/revfs_take.rs @@ -0,0 +1,140 @@ +//! # RE-VFS Take Operation Example +//! +//! This example demonstrates how to use the Remote Encrypted Virtual Filesystem (RE-VFS) +//! to store and then "take" a file. The take operation removes the file from the RE-VFS +//! while retrieving it, effectively performing an atomic move operation. +//! +//! ## Features Demonstrated +//! - RE-VFS file storage +//! - Atomic take operation (retrieve and delete) +//! - P2P connection establishment +//! - Secure file transfer +//! - Error handling for file operations +//! +//! ## Usage +//! Run two instances - one sender and one receiver: +//! ```bash +//! # Sender (stores file in RE-VFS) +//! export CITADEL_MY_USER="sender" +//! export CITADEL_OTHER_USER="receiver" +//! export IS_SENDER="true" +//! cargo run --example revfs_take +//! +//! # Receiver (takes file from RE-VFS) +//! export CITADEL_MY_USER="receiver" +//! export CITADEL_OTHER_USER="sender" +//! cargo run --example revfs_take +//! ``` +//! +//! ## How it Works +//! 1. Sender stores a file in the RE-VFS +//! 2. Receiver connects and performs a take operation +//! 3. File is atomically moved from RE-VFS to receiver's local storage +//! 4. Original file in RE-VFS is automatically deleted +//! +//! ## Note +//! The take operation is useful when you want to ensure that a file can only be +//! retrieved once, or when you want to automatically clean up the RE-VFS after +//! file retrieval. + +//! This example demonstrates how to set up a peer connection between two peers and use REVFS to store then take a file + +use citadel_sdk::{ + prefabs::client::peer_connection::{PeerConnectionKernel, PeerConnectionSetupAggregator}, + prelude::*, +}; +use futures::StreamExt; +use std::env; +use std::path::PathBuf; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server_addr = env::var("CITADEL_SERVER_ADDR").expect("CITADEL_SERVER_ADDR not set"); + let my_user = env::var("CITADEL_MY_USER").expect("MY_USER not set"); + let other_user = env::var("CITADEL_OTHER_USER").expect("OTHER_USER not set"); + let is_sender = env::var("IS_SENDER").unwrap_or_default().to_lowercase() == "true"; + + println!("Starting file transfer as peer {my_user}"); + println!("Will connect to peer {other_user}"); + + // Set up session security + let session_security = SessionSecuritySettingsBuilder::default() + .with_secrecy_mode(SecrecyMode::Perfect) + .with_crypto_params(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256) + .build()?; + + // Create server connection settings + let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; + + // Create peer connection setup + let peer_connection = PeerConnectionSetupAggregator::default() + .with_peer_custom(other_user) + .with_session_security_settings(session_security) + .enable_udp() + .add(); + + let file_path = PathBuf::from("test_file.txt"); + if !file_path.exists() { + tokio::fs::write(&file_path, "Hello, this is a test file!").await?; + } + + // Set up the peer connection kernel + let kernel = PeerConnectionKernel::new( + server_connection_settings, + peer_connection, + move |mut connection, remote| async move { + println!("Connected to server successfully!"); + + // Wait for peer connection + let peer_conn = connection.recv().await.unwrap()?; + let (tx, mut rx) = peer_conn.channel.split(); + let peer_remote = peer_conn.remote; + println!("Connected to peer {:?}!", peer_remote.target_username()); + + let virtual_file_path = "/home/foo/bar/test_file.txt"; + + if is_sender { + // Securely store the file on the remote peer. The peer cannot read the file contents + citadel_sdk::fs::write(&peer_remote, file_path.clone(), virtual_file_path).await?; + // Now, download the contents of the file from the remote peer, deleting the remote + // contents at the same time by taking it + let locally_downloaded_file = + citadel_sdk::fs::take(&peer_remote, virtual_file_path).await?; + // Compare the contents in "file_path" and "locally_downloaded_file" + let file_contents = tokio::fs::read_to_string(&file_path).await?; + let downloaded_file_contents = + tokio::fs::read_to_string(&locally_downloaded_file).await?; + assert_eq!(file_contents, downloaded_file_contents); + // Alert the other side that the file has been successfully processed + tx.send_message(SecBuffer::from("success").into()).await?; + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } else { + let incoming_file_requests = remote.get_incoming_file_transfer_handle().unwrap(); + // There will be two file transfer handles sent to us: one for the file the peer sends to us, + // and another for when the peer requests a file from us. We will accept both. + incoming_file_requests.accept_all(); + // Patiently wait for the "success" message from the sender + let msg = rx.next().await.unwrap(); + assert_eq!(msg.as_ref(), b"success"); + } + + remote.shutdown_kernel().await + }, + ); + + // Build the peer + let node = NodeBuilder::default().build(kernel)?; + + // Run the peer + node.await?; + + Ok(()) +} diff --git a/keys/testing.p12 b/keys/testing.p12 deleted file mode 100644 index 18e6d756660d374d525e924a478ae969489bf334..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5725 zcmV-j7NY4ef)-f<0Ru3C79R!)Duzgg_YDCD0ic2w5Cno23^0Ng2rz;WzXk~^hDe6@ z4FLxRpn?#vFoF=Q0s#Opf)J$!2`Yw2hW8Bt2LUh~1_~;MNQU!w94%ZmoP#ek#L|C7&f=Wz0yV8nV<}2-XQzM`n zi(S}WZ@keGHkpzONbD5snB#aK*8BdI^0w5KAu7f;?1b2o`cCV^v?R|>@yLF2=s`Rm z=S;2-2zTjGd5R4iHx0;TCa^J#(-0b_2^+H5vmlt{lm0w;U#!3nuK^&KdT{7<7Ku#g(Uclqk%2HXk+R{=o6kYD% zamrn>nnnoxilHbHzcI=?RCUX2v$^vk?P?EzGhAj?q>-mKWDYT2zCg?*)SDgNJ>ks7g+X z=O%gT%O2%^-9@Uu+a9Um^v-~plt`Ovl)dn7s#U`{)31I^;afJLz)OUlzXPcD+o@or zkQS4_u%jxbE6SG`eZMacDjK|ho~67G>(_NUtTwgw8G3>_)ywueX-X_xUR#ED2*u92 zzs_ckOp7awiDjO1K*9TgldOj5QmA?pA!wUt<&Mu~%$UoxR?G`LlG9^N@h3(gkl9Q&&dvZUj zX)QU#n;u`&;m>bCKiC3c<|Hcixa%IH(CK7heo!7!<)_*Cx}Y~w)cZl--g5+!1!b(#WRRqTq%Y1DX z9AagaMG1p*qK&X~I0U)h$<11oiRaU~;!dvm zse8`->Ds|%-A3_NkOK}`u7APC@ixChB7eJGVlL&| zuTUZU2L!WMm3zuBXj-CEytDzYiF)v7`D~pn%-Z3ZSvq_Zv=DSSQ=~klEsdRX9S;Xb}8b{fN`*LAi4NoxT+Rn5T zB>uPzMT?y^tY~zZ);ap%oDs;3Lrp%x7}^)j@Y4P_KwzR zu~?69FEUp#F~TJ#VPUbX$h{u3Cr=!DS4gZ20qRS{VF^rT6DObjzgcdhjgl~R2Z`mr z%rxKJ4+!qAnRSAGdK(IaI5-fX!>pT!(J-jypNB5nU;Mh--1Lpf%*A`_eFy$}M+P0n0A(GOeXGV<~*Y#6*^ZcN?o@0rarWD}!`0=);rjlful7vC z=W_H`e>cADZg3M`1%gHNa&Ac796RVXh&UJT=vZ~1!3Rc5S;BsD)SWQS(=JBsns3cyG;lm}%ud<<7=@<)`wYP7hTHwuk4ouMTurRG1#ekK zcl{#bywct=iHYQMrg8Tb(E6lkgh=wIC3)|H4x$n5j=?o~#Kz$HtRaqQQ*9;WEaJ9> zgnw(?E=s-#g{_c(!=w0UZmP2{T`s2CYPo2 z{fRo1<-TOY30!qQM}3nsia(7)ZeM`&Mi}>4oJra5+`V547-1}*?xc&=Atd_0%n>~X zV?O2QBT51YVG;`9 zqQ6;Z%W|)(@6H`nY8z=(d%FpBVTJ{(gP45bF9g!OY@_;Q3QvElq0lY|Ws20-Ue84i z8YW#`c0>#?aH?tB^ZhTcQp&PAvbr@6vx4L2k6cxn>qwxJS9j-WtsLv|`J2!R57v!B z3K2rsmv3q~&a_*gwLwr6LfCE;`cz7r{u)eFi-T_5Xm_?w)kQwj%%?o_+{bPq{Q%$Sfz4f>{>b+%v?m{44)hb}gwAU5pBtu<01UEY{|Z38>vQzyu@K>qCQ zlQqJ4O)j5u*7w$Aox~WC4gUeMs;1GI|K$(Td%Ru7V_l8TF9|3Uu2;1y)|sB4 zY$^PzpK5zZN1V9YoLj{ts}x5ImonPx$wXuiPWLGFWy>1>h#3I|2DJ9?TFdg{? z)F3SCb+}Nnhn}}VVMsP}BOlSUu%y_eO`Te%80=YO!`FagyA>GPdv-^?!&T`6rG>{F zOVI=r7-Xw|5Nc*9ipFttvQ5{PR3-G=44!3yG`j$dBD2d)=dcv**x*fUk=M!@=w4GJ zUn`dgm-jX{0=yk)5vpsfTBJBdy5k;^MGJhfj1XJ8?HPw_I!;W_Y3lc9Lr!{!SM3Vl zs4O$lpNT91!i5 zYhvS@ILGMz+m(O%VAiEhx0MlB^KsK^@YW(?e{aOD)BNCpE z6tIh9h6T`i?(a5Lv~}f5_QAA1aOm{W0G2FxIr_rRy|sc~aon31;rmsnPejw$xJ zTPjgftQDC}n!J)`Q}+OPSAb5R;dq^XeHCh59fLv>f3?LWq6)T<^?U6R^wogA{jPwH zSX3x25LP4_I2^OX*1=@>Y|^FUCIZwjMlFHLf|d@{%tK%8`wV5~F@&p`W-FTdsLX63 zL{8H*OqGRgwz=nWPCcxqR;-36ZwloVH_*hqu=;;M4k$*CrDanD2O_Zpm%RibVNM&Qy_|(ptP7rUu)jgb*!P{1lXjKGVt5E7C_U)T(8u?sd#tL`IjPGNd zc0Ep5*w=JL%~faE>DI=n@7EFE4XWQ$%(UAAtam&}aPZqRFV|&Bi51;k4XzvN(L2pHgSkrcbj; zR<8KR=oAU5d-fPlXwG(Gjj!Wucyo<^4uFDoFYVM&TrfDRjn_R_)E`n|VZ+jPPqM@^ z?h&LNQUG0Pmp8$`#{=_t%26R&DgyZwVPH|Btk=Zv% z`I8ea6b%R^`G4QJrrj2IttxkBZmrbjPM`S&U}O_$aVCehE@bjxg7|DqhOUx4 ztag|r-nt6_wM3!;mH_%6&`9M?>~N>J(=(;0qi)LuTwC>Y#YvIk=$bdC7A-N3n=40q zms5zCOu+gjFNLS+xgHDA^*x3EXC}B^=Lb_-a%$ve>n#g_tJ?G(A6qm;XnFhGkybl~ z*mo0s-if`e^Y3-#C!5gc4=4qXSZD68ZXUUPt5G=0cC<*7?eU$XDqg@CJL*2UKH=+H z4gil9G-P@PGpR?7PGAl9U%RBS9o0Q(88ZrXoQI~=U*y%i`g~jbUFghrJOR$-##Buj zZf=0Yb)C(>NY3tiCl9BubD$?cyfAD6K>boay+_;?8-Gvj#a7UM!|B%urt2PX)B2a(&~dG zt$iiZUdv08wkh>{D10|J!_TO!B|PtvY%cDO&_kL>nEXz<44v?UwA=9AQY=b^bC7~0 z*BL(avJXZR0aCrMiX|vB0Y>yH5tTHvOOcuUT>rEXwCKVGI?RkH1?v^q#oh$y#C@S` zEYX?LW9&Aqgpo4QBzfk{Du7R2@5b~wWFWUuxdR@sC=+s%wUWGz7_Rh{p( z=uiRAt%rHHPoh(h7jw5MR56c)?1G4D?gz8(@0WU=ua-wOAvWhzKoqOUvQ13)%65t7 zf(Z7X31_>aF=ImfYaOSUXj6UoIy6#3gsBqg?J; zF2Zw$pRCTi=tWq8D2`Zg+#!epi1~W|T+fU`0iZbFs^u|>(D}L9x4cC@v9mmVXm~|W zvBkYud!Zz^DrTn-mhbR`x&pI5aTi$q#-99KBV5z8V@XMvV|BC3WXTR+l9DjUq{&>e zg-dEZtYY6;{wEiBx?3k42eR;nqDG(zhTr&Y!VoFLhHJ-U3{PI&iUd9^AA!g`NgklA z^3m#DApgQ)uKze0TU4Y;6^O8#W({l^Pe>h2e z_a-Fz0{;%q?GaTgbKdQ$Rp)zd-Z&vW166bsKPWAdf~oAV+?m~D;^-w_`B{YF)kXbt za8Rd^DV{#%^y137$bVS?Hok;*-6g3Vr?N36Fe3&DDuzgg_YDCF6)_eB6e<@YGztgF zi3f|DTTXMNFIz(9ConNEAutIB1uG5%0vZJX1QhW$ljCfN1+uK`2rqJ!5y70ZCMpC7 PzN? Date: Sat, 30 Nov 2024 06:47:24 -0500 Subject: [PATCH 04/12] docs: lots of docs and examples --- Makefile.toml | 20 +- async_ip/src/lib.rs | 66 +- citadel_crypt/src/argon/argon_container.rs | 56 + citadel_crypt/src/argon/autotuner.rs | 52 + .../src/endpoint_crypto_container.rs | 67 +- citadel_crypt/src/entropy_bank.rs | 70 +- citadel_crypt/src/fcm/fcm_ratchet.rs | 54 + citadel_crypt/src/fcm/keys.rs | 45 + citadel_crypt/src/lib.rs | 31 +- citadel_crypt/src/misc.rs | 58 +- citadel_crypt/src/packet_vector.rs | 81 +- citadel_crypt/src/scramble/crypt_splitter.rs | 60 +- citadel_crypt/src/secure_buffer/mod.rs | 32 + .../secure_buffer/partitioned_sec_buffer.rs | 117 +- citadel_crypt/src/secure_buffer/sec_packet.rs | 75 +- citadel_crypt/src/stacked_ratchet.rs | 91 +- .../src/streaming_crypt_scrambler.rs | 72 + citadel_crypt/src/sync_toggle.rs | 66 +- citadel_crypt/src/toolset.rs | 56 + citadel_io/src/lib.rs | 56 + citadel_io/src/standard/locks.rs | 18 + citadel_io/src/wasm/locks.rs | 36 + citadel_io/src/wasm/rng.rs | 38 + citadel_logging/src/lib.rs | 110 +- citadel_pqcrypto/Cargo.toml | 83 +- citadel_pqcrypto/src/bytes_in_place.rs | 35 + citadel_pqcrypto/src/constructor_opts.rs | 45 + citadel_pqcrypto/src/encryption.rs | 46 +- citadel_pqcrypto/src/export.rs | 75 +- citadel_pqcrypto/src/lib.rs | 45 + .../src/replay_attack_container.rs | 53 + citadel_pqcrypto/src/wire.rs | 42 + citadel_proto/src/auth.rs | 46 + citadel_proto/src/constants.rs | 52 + citadel_proto/src/error.rs | 33 + citadel_proto/src/functional.rs | 44 + citadel_proto/src/inner_arg.rs | 48 + .../src/kernel/kernel_communicator.rs | 28 + citadel_proto/src/kernel/kernel_executor.rs | 62 +- citadel_proto/src/kernel/kernel_trait.rs | 30 +- citadel_proto/src/kernel/mod.rs | 53 + citadel_proto/src/lib.rs | 69 +- citadel_proto/src/proto/codec.rs | 41 + .../src/proto/endpoint_crypto_accessor.rs | 22 + .../src/proto/misc/clean_shutdown.rs | 27 + citadel_proto/src/proto/misc/dual_cell.rs | 26 + .../src/proto/misc/dual_late_init.rs | 26 + citadel_proto/src/proto/misc/dual_rwlock.rs | 27 + citadel_proto/src/proto/misc/lock_holder.rs | 28 + citadel_proto/src/proto/misc/mod.rs | 28 + citadel_proto/src/proto/misc/net.rs | 49 +- .../src/proto/misc/ordered_channel.rs | 29 + citadel_proto/src/proto/misc/panic_future.rs | 27 + .../proto/misc/session_security_settings.rs | 28 + .../src/proto/misc/udp_internal_interface.rs | 29 + .../src/proto/misc/underlying_proto.rs | 29 + citadel_proto/src/proto/mod.rs | 34 + citadel_proto/src/proto/node.rs | 44 +- citadel_proto/src/proto/node_request.rs | 26 + citadel_proto/src/proto/node_result.rs | 28 + citadel_proto/src/proto/outbound_sender.rs | 50 + citadel_proto/src/proto/packet.rs | 32 + citadel_proto/src/proto/packet_crafter.rs | 182 +- .../proto/packet_processor/connect_packet.rs | 45 +- .../packet_processor/deregister_packet.rs | 49 + .../packet_processor/disconnect_packet.rs | 49 + .../src/proto/packet_processor/file_packet.rs | 51 + .../src/proto/packet_processor/hole_punch.rs | 50 + .../packet_processor/keep_alive_packet.rs | 43 +- .../src/proto/packet_processor/mod.rs | 105 +- .../packet_processor/peer/group_broadcast.rs | 135 +- .../src/proto/packet_processor/peer/mod.rs | 28 + .../packet_processor/peer/peer_cmd_packet.rs | 69 +- .../proto/packet_processor/peer/server/mod.rs | 27 + .../peer/server/post_connect.rs | 31 + .../peer/server/post_register.rs | 30 + .../peer/signal_handler_interface.rs | 27 + .../packet_processor/preconnect_packet.rs | 51 +- .../packet_processor/primary_group_packet.rs | 52 + .../packet_processor/raw_primary_packet.rs | 70 +- .../proto/packet_processor/register_packet.rs | 54 + .../proto/packet_processor/rekey_packet.rs | 51 + .../src/proto/packet_processor/udp_packet.rs | 29 + citadel_proto/src/proto/peer/channel.rs | 61 + citadel_proto/src/proto/peer/group_channel.rs | 54 + .../peer/hole_punch_compat_sink_stream.rs | 49 + citadel_proto/src/proto/peer/message_group.rs | 50 + citadel_proto/src/proto/peer/mod.rs | 49 + .../src/proto/peer/p2p_conn_handler.rs | 61 + citadel_proto/src/proto/peer/peer_crypt.rs | 53 + citadel_proto/src/proto/peer/peer_layer.rs | 50 + citadel_proto/src/proto/remote.rs | 58 +- citadel_proto/src/proto/session.rs | 62 +- citadel_proto/src/proto/session_manager.rs | 55 +- .../src/proto/session_queue_handler.rs | 31 + citadel_proto/src/proto/state_container.rs | 71 + .../connect_state_container.rs | 40 + .../deregister_state_container.rs | 32 + .../meta_expiry_container.rs | 38 + .../src/proto/state_subcontainers/mod.rs | 55 + .../peer_kem_state_container.rs | 52 + .../preconnect_state_container.rs | 53 + .../register_state_container.rs | 49 + .../state_subcontainers/rekey_container.rs | 38 + citadel_proto/src/proto/transfer_stats.rs | 36 + citadel_proto/src/proto/validation.rs | 37 +- citadel_sdk/src/backend_kv_store.rs | 39 + citadel_sdk/src/builder/mod.rs | 19 + citadel_sdk/src/builder/node_builder.rs | 37 + citadel_sdk/src/fs.rs | 46 + citadel_sdk/src/lib.rs | 4 +- citadel_sdk/src/macros.rs | 35 + citadel_sdk/src/prefabs/client/broadcast.rs | 62 + citadel_sdk/src/prefabs/client/mod.rs | 42 + .../src/prefabs/client/peer_connection.rs | 68 +- .../src/prefabs/client/single_connection.rs | 55 +- citadel_sdk/src/prefabs/mod.rs | 49 +- .../server/accept_file_transfer_kernel.rs | 36 + .../prefabs/server/client_connect_listener.rs | 41 + citadel_sdk/src/prefabs/server/empty.rs | 37 + .../src/prefabs/server/internal_service.rs | 69 +- citadel_sdk/src/prefabs/server/mod.rs | 63 + .../src/prefabs/shared/internal_service.rs | 40 + citadel_sdk/src/prefabs/shared/mod.rs | 22 + citadel_sdk/src/remote_ext.rs | 57 + citadel_sdk/src/responses.rs | 49 +- citadel_sdk/src/test_common.rs | 40 + citadel_types/src/crypto/mod.rs | 59 + citadel_types/src/lib.rs | 73 + citadel_types/src/utils/mod.rs | 8 +- citadel_user/src/account_loader.rs | 49 + citadel_user/src/account_manager.rs | 95 + citadel_user/src/auth/mod.rs | 33 + citadel_user/src/auth/proposed_credentials.rs | 40 + .../src/backend/filesystem_backend.rs | 154 +- citadel_user/src/backend/memory.rs | 40 + citadel_user/src/backend/mod.rs | 40 + citadel_user/src/backend/redis_backend.rs | 52 +- citadel_user/src/backend/sql_backend.rs | 62 +- citadel_user/src/backend/utils/mod.rs | 40 + citadel_user/src/client_account.rs | 89 + citadel_user/src/connection_metadata.rs | 103 + citadel_user/src/credentials.rs | 111 +- citadel_user/src/directory_store.rs | 85 + .../src/external_services/google_auth.rs | 43 + citadel_user/src/external_services/mod.rs | 45 + citadel_user/src/external_services/rtdb.rs | 49 + .../external_services/service_interface.rs | 49 + citadel_user/src/hypernode_account.rs | 74 + citadel_user/src/lib.rs | 86 +- citadel_user/src/misc.rs | 37 + citadel_user/src/network_account.rs | 38 - citadel_user/src/serialization.rs | 82 + citadel_user/src/server_misc_settings.rs | 41 + citadel_wire/src/error.rs | 40 + citadel_wire/src/hypernode_type.rs | 43 + citadel_wire/src/lib.rs | 31 + citadel_wire/src/standard/misc.rs | 81 + citadel_wire/src/standard/mod.rs | 55 + .../src/standard/nat_identification.rs | 49 + citadel_wire/src/standard/quic.rs | 51 + citadel_wire/src/standard/socket_helpers.rs | 55 + citadel_wire/src/standard/tls.rs | 52 + citadel_wire/src/standard/upnp_handler.rs | 56 + .../src/udp_traversal/hole_punch_config.rs | 81 + .../linear/encrypted_config_container.rs | 53 + .../src/udp_traversal/linear/method3.rs | 61 + citadel_wire/src/udp_traversal/linear/mod.rs | 60 + citadel_wire/src/udp_traversal/mod.rs | 55 + citadel_wire/src/udp_traversal/multi/mod.rs | 55 + .../targetted_udp_socket_addr.rs | 76 + .../src/udp_traversal/udp_hole_puncher.rs | 57 + context.ai.json | 2229 +++++++++++++++++ firebase-rtdb/Cargo.toml | 1 + firebase-rtdb/src/lib.rs | 219 +- netbeam/src/lib.rs | 96 +- netbeam/src/multiplex.rs | 66 + netbeam/src/reliable_conn.rs | 96 +- netbeam/src/sync/callback_channel.rs | 63 + netbeam/src/sync/channel/bi_channel.rs | 81 +- netbeam/src/sync/mod.rs | 32 + netbeam/src/sync/network_application.rs | 41 +- netbeam/src/sync/network_endpoint.rs | 39 + netbeam/src/sync/operations/net_join.rs | 42 + netbeam/src/sync/operations/net_select.rs | 45 + netbeam/src/sync/operations/net_select_ok.rs | 49 + netbeam/src/sync/operations/net_try_join.rs | 48 + netbeam/src/sync/primitives/mod.rs | 19 + netbeam/src/sync/primitives/net_mutex.rs | 47 + netbeam/src/sync/primitives/net_rwlock.rs | 520 ++-- netbeam/src/sync/subscription.rs | 39 + netbeam/src/sync/sync_start.rs | 39 + netbeam/src/sync/tracked_callback_channel.rs | 68 + netbeam/src/time_tracker.rs | 52 +- suggestions.ai.json | 2051 +++++++++++++++ 195 files changed, 14360 insertions(+), 528 deletions(-) create mode 100644 citadel_user/src/connection_metadata.rs delete mode 100644 citadel_user/src/network_account.rs create mode 100644 context.ai.json create mode 100644 suggestions.ai.json diff --git a/Makefile.toml b/Makefile.toml index 839a16f85..1bac4cf10 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -10,6 +10,10 @@ args = ["clean"] command = "cargo" args = ["clippy", "--all"] +[tasks.check] +command = "cargo" +args = ["clippy", "--tests", "--examples", "--all", "--", "-D", "warnings"] + [tasks.clippy-tests] command = "cargo" args = ["clippy", "--all", "--tests", "--features=localhost-testing,multi-threaded"] @@ -53,7 +57,7 @@ dependencies = ["install-nextest"] [tasks.test] command = "cargo" description = "Tests all available unit/integration tests locally using SQL/redis backends and appropriate localhost network settings" -condition = { env_set = [ "TESTING_SQL_SERVER_ADDR_CLIENT", "TESTING_SQL_SERVER_ADDR_SERVER" ], env_not_set = ["SKIP_EXT_BACKENDS"] } +condition = { env_set = ["TESTING_SQL_SERVER_ADDR_CLIENT", "TESTING_SQL_SERVER_ADDR_SERVER"], env_not_set = ["SKIP_EXT_BACKENDS"] } args = ["nextest", "run", "--features", "localhost-testing"] dependencies = ["install-nextest"] @@ -250,41 +254,41 @@ run_task = [ install_crate = { crate_name = "cargo-workspaces", binary = "cargo", test_arg = ["workspaces", "--help"] } [tasks.publish-bump-version-patch] -condition = { env_set = [ "CARGO_REGISTRY_TOKEN" ] } +condition = { env_set = ["CARGO_REGISTRY_TOKEN"] } command = "cargo" args = ["workspaces", "version", "-y", "minor"] [tasks.publish-bump-version-minor] -condition = { env_set = [ "CARGO_REGISTRY_TOKEN" ] } +condition = { env_set = ["CARGO_REGISTRY_TOKEN"] } command = "cargo" args = ["workspaces", "version", "-y", "minor"] [tasks.publish-bump-version-major] -condition = { env_set = [ "CARGO_REGISTRY_TOKEN" ] } +condition = { env_set = ["CARGO_REGISTRY_TOKEN"] } command = "cargo" args = ["workspaces", "version", "-y", "major"] [tasks.publish-patch] command = "cargo" -condition = { env_set = [ "CARGO_REGISTRY_TOKEN" ] } +condition = { env_set = ["CARGO_REGISTRY_TOKEN"] } args = ["workspaces", "publish", "-y", "--token", "${CARGO_REGISTRY_TOKEN}", "--allow-dirty", "patch", "${@}"] dependencies = ["publish-install-deps"] [tasks.publish-minor] command = "cargo" -condition = { env_set = [ "CARGO_REGISTRY_TOKEN" ] } +condition = { env_set = ["CARGO_REGISTRY_TOKEN"] } args = ["workspaces", "publish", "-y", "--token", "${CARGO_REGISTRY_TOKEN}", "--allow-dirty", "minor", "${@}"] dependencies = ["publish-install-deps"] [tasks.publish-major] command = "cargo" -condition = { env_set = [ "CARGO_REGISTRY_TOKEN" ] } +condition = { env_set = ["CARGO_REGISTRY_TOKEN"] } args = ["workspaces", "publish", "-y", "--token", "${CARGO_REGISTRY_TOKEN}", "--allow-dirty", "major", "${@}"] dependencies = ["publish-install-deps"] [tasks.check-docs] command = "cargo" -args = ["test", "--package", "citadel_sdk", "--doc"] +args = ["test", "--package", "citadel_sdk", "--doc", "${@}"] [tasks.install-cargo-wasix] command = "cargo" diff --git a/async_ip/src/lib.rs b/async_ip/src/lib.rs index cff509c9f..44cea8a45 100644 --- a/async_ip/src/lib.rs +++ b/async_ip/src/lib.rs @@ -1,4 +1,64 @@ -//! An asynchronous client used to obtain one's global Ipv6 or Ipv4 address +//! # Async IP Resolution +//! +//! A lightweight, asynchronous client for obtaining global IPv4 and IPv6 addresses. +//! This crate provides reliable IP address resolution using multiple fallback services +//! and concurrent requests for improved reliability. +//! +//! ## Features +//! +//! - Async IP address resolution +//! - Support for both IPv4 and IPv6 +//! - Multiple fallback services +//! - Concurrent resolution for improved reliability +//! - Internal IP address detection +//! - WebAssembly support +//! - Custom HTTP client support +//! +//! ## Usage +//! +//! ```rust,no_run +//! use async_ip::get_all; +//! use citadel_io::tokio; +//! +//! #[tokio::main(flavor = "current_thread")] +//! async fn main() -> Result<(), async_ip::IpRetrieveError> { +//! // Get both internal and external IP addresses +//! use reqwest::Client; +//! let ip_info = get_all::(None).await?; +//! println!("External IPv6: {:?}", ip_info.external_ipv6); +//! println!("Internal IPv4: {:?}", ip_info.internal_ip); +//! Ok(()) +//! } +//! ``` +//! +//! ## Advanced Usage +//! +//! ```rust,no_run +//! use async_ip::{get_all_multi_concurrent, get_default_client}; +//! use citadel_io::tokio; +//! +//! #[tokio::main(flavor = "current_thread")] +//! async fn main() -> Result<(), async_ip::IpRetrieveError> { +//! // Use multiple services concurrently with a custom client +//! let client = get_default_client(); +//! let ip_info = get_all_multi_concurrent(Some(client)).await?; +//! println!("External IPs: {:?}", ip_info); +//! Ok(()) +//! } +//! ``` +//! +//! ## WebAssembly Support +//! +//! When compiled with the `wasm` target, this crate uses a lightweight HTTP client +//! suitable for WebAssembly environments. The functionality remains the same, but +//! some features (like internal IP detection) may be limited. +//! +//! ## Error Handling +//! +//! The crate uses a custom `IpRetrieveError` type that wraps various error +//! conditions that may occur during IP resolution, including network errors +//! and parsing failures. + #![deny( missing_docs, trivial_numeric_casts, @@ -176,10 +236,12 @@ fn addr(addr: &str) -> Option { } #[cfg(not(target_family = "wasm"))] -fn get_default_client() -> Client { +/// Returns a default client +pub fn get_default_client() -> Client { Client::builder().tcp_nodelay(true).build().unwrap() } #[cfg(target_family = "wasm")] +/// Returns a default client fn get_default_client() -> UreqClient { UreqClient } diff --git a/citadel_crypt/src/argon/argon_container.rs b/citadel_crypt/src/argon/argon_container.rs index f8093adbf..dbfdea160 100644 --- a/citadel_crypt/src/argon/argon_container.rs +++ b/citadel_crypt/src/argon/argon_container.rs @@ -1,3 +1,59 @@ +//! # Argon2 Password Hashing Container +//! +//! This module provides a secure, asynchronous wrapper around the Argon2 password hashing +//! algorithm. It implements both client-side and server-side password handling with +//! configurable parameters and secure memory management. +//! +//! ## Features +//! - Asynchronous password hashing and verification +//! - Configurable Argon2id parameters (memory, time, parallelism) +//! - Secure memory handling with SecBuffer +//! - Automatic salt generation +//! - Support for associated data and secret keys +//! - Client and server container types +//! +//! ## Usage Example +//! ```rust +//! use citadel_crypt::argon::argon_container::{ArgonSettings, ClientArgonContainer, AsyncArgon}; +//! use citadel_types::crypto::SecBuffer; +//! +//! async fn hash_password() { +//! // Create settings with default parameters +//! let settings = ArgonSettings::new_defaults(vec![1, 2, 3]); // Associated data +//! +//! // Create client container +//! let client = ClientArgonContainer::from(settings); +//! +//! // Hash a password +//! let password = SecBuffer::from("my_secure_password"); +//! let hashed = client.hash_insecure_input(password).await.unwrap(); +//! +//! // Verify a password (server-side) +//! let server_container = ServerArgonContainer::new( +//! client.settings.clone(), +//! hashed +//! ); +//! +//! let verify_result = AsyncArgon::verify( +//! SecBuffer::from("my_secure_password"), +//! server_container +//! ).await.unwrap(); +//! } +//! ``` +//! +//! ## Important Notes +//! - Uses Argon2id variant for optimal security +//! - Memory-hard algorithm with configurable cost parameters +//! - Handles password hashing on blocking threads +//! - Provides secure memory zeroing through SecBuffer +//! - Supports custom associated data for domain separation +//! +//! ## Related Components +//! - [`SecBuffer`](citadel_types::crypto::SecBuffer): Secure memory management +//! - [`AsyncArgon`]: Asynchronous hashing interface +//! - [`ArgonSettings`]: Configuration parameters +//! - Argon2 password hashing algorithm + use argon2::Config; use citadel_io::tokio; use citadel_types::crypto::SecBuffer; diff --git a/citadel_crypt/src/argon/autotuner.rs b/citadel_crypt/src/argon/autotuner.rs index f2388cb3a..0919c16a4 100644 --- a/citadel_crypt/src/argon/autotuner.rs +++ b/citadel_crypt/src/argon/autotuner.rs @@ -1,3 +1,55 @@ +//! # Argon2 Parameter Auto-tuner +//! +//! Automatically determines optimal Argon2 parameters for password hashing based on system capabilities +//! and performance requirements. This module implements the recommendations from ORY's Argon2 parameter +//! selection guidelines. +//! +//! ## Features +//! +//! * Dynamic parameter tuning based on available system memory +//! * Multi-threaded optimization using available CPU cores +//! * Memory-first tuning strategy for optimal security +//! * Iterative time-cost adjustment +//! * Configurable minimum execution time +//! * Support for custom hash lengths and secret keys +//! +//! ## Usage Example +//! +//! ```rust +//! use citadel_crypt::argon::autotuner::calculate_optimal_argon_params; +//! +//! async fn configure_argon() -> Result<(), CryptError> { +//! // Configure Argon2 to take at least 500ms +//! let optimal_params = calculate_optimal_argon_params( +//! 500, // minimum milliseconds +//! None, // use default hash length +//! Some(b"secret".to_vec()) // optional secret key +//! ).await?; +//! +//! println!("Optimal parameters:"); +//! println!("Memory cost: {} KB", optimal_params.mem_cost); +//! println!("Time cost: {}", optimal_params.time_cost); +//! println!("Parallelism: {}", optimal_params.lanes); +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * Should be run in release mode for accurate results +//! * Memory cost is automatically capped at available system memory +//! * Parameters are tuned for the specific CPU running the autotuner +//! * Memory-cost is prioritized over time-cost for better security +//! * Results may vary between runs due to system load +//! +//! ## Related Components +//! +//! * `argon_container`: Core Argon2 implementation and settings +//! * `SecBuffer`: Secure memory management for passwords +//! * `CryptError`: Error handling for cryptographic operations +//! + use crate::argon::argon_container::{ ArgonDefaultServerSettings, ArgonSettings, ArgonStatus, AsyncArgon, DEFAULT_HASH_LENGTH, }; diff --git a/citadel_crypt/src/endpoint_crypto_container.rs b/citadel_crypt/src/endpoint_crypto_container.rs index b5f770dc8..3c0a0d6fa 100644 --- a/citadel_crypt/src/endpoint_crypto_container.rs +++ b/citadel_crypt/src/endpoint_crypto_container.rs @@ -1,3 +1,67 @@ +//! Peer Session Cryptographic Container +//! +//! This module provides a thread-safe container for managing cryptographic state +//! between peer sessions. It handles ratchet updates, version management, and +//! concurrent access control for secure peer-to-peer communication. +//! +//! # Features +//! +//! - Thread-safe cryptographic state management +//! - Ratchet version control and updates +//! - Concurrent update conflict resolution +//! - Session key management and rotation +//! - Atomic state transitions +//! - Rolling object and group ID management +//! - Post-quantum cryptography support +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::endpoint_crypto_container::{PeerSessionCrypto, KemTransferStatus}; +//! use citadel_crypt::toolset::Toolset; +//! use citadel_crypt::stacked_ratchet::StackedRatchet; +//! +//! fn setup_session() -> Result<(), CryptError> { +//! // Create toolset with default parameters +//! let toolset = Toolset::new_with_defaults(); +//! +//! // Initialize peer session (as initiator) +//! let mut session = PeerSessionCrypto::new(toolset, true); +//! +//! // Get constructor for next update +//! if let Some(constructor) = session.get_next_constructor(false) { +//! // Perform update +//! let status = session.update_sync_safe( +//! constructor, +//! true, // is_alice +//! 1234 // local_cid +//! )?; +//! +//! // Handle transfer status +//! if status.has_some() { +//! println!("Update requires transfer"); +//! } +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Updates are atomic and thread-safe +//! - Version conflicts resolved by initiator priority +//! - Requires proper lock management +//! - State transitions must be synchronized +//! - Supports multiple ratchet versions +//! +//! # Related Components +//! +//! - [`crate::toolset::Toolset`] - Cryptographic toolset +//! - [`crate::stacked_ratchet::StackedRatchet`] - Ratchet implementation +//! - [`crate::misc::CryptError`] - Error handling +//! + #![allow(missing_docs)] use crate::misc::CryptError; @@ -355,4 +419,5 @@ mod tests { } } } -}*/ +} +*/ diff --git a/citadel_crypt/src/entropy_bank.rs b/citadel_crypt/src/entropy_bank.rs index 7555ea0f7..877a26db1 100644 --- a/citadel_crypt/src/entropy_bank.rs +++ b/citadel_crypt/src/entropy_bank.rs @@ -1,3 +1,60 @@ +//! Entropy Bank: Dynamic Cryptographic State Management +//! +//! This module implements a secure entropy bank system that provides dynamic, +//! evolving cryptographic states for packet protection and nonce generation. +//! It ensures replay attack prevention and maintains secure packet ordering. +//! +//! # Features +//! +//! - Dynamic nonce generation and management +//! - Packet encryption and decryption +//! - Replay attack prevention +//! - Ordered packet delivery enforcement +//! - Random scrambling of ciphertext bytes +//! - Post-quantum cryptography support +//! - Transient counter management +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::entropy_bank::EntropyBank; +//! use citadel_pqcrypto::PostQuantumContainer; +//! +//! fn protect_data() -> Result<(), CryptError> { +//! // Create new entropy bank +//! let bank = EntropyBank::new( +//! 1234, // Client ID +//! 1, // Version +//! EncryptionAlgorithm::default() +//! )?; +//! +//! // Create quantum container +//! let container = PostQuantumContainer::new(); +//! +//! // Encrypt data +//! let ciphertext = bank.encrypt(&container, b"secret data")?; +//! +//! // Decrypt data +//! let plaintext = bank.decrypt(&container, &ciphertext)?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Nonce reuse is prevented by design +//! - Ordered packet delivery is mandatory +//! - Port mappings are randomized for security +//! - Thread-safe transient counter handling +//! - Serialization support for persistence +//! +//! # Related Components +//! +//! - [`citadel_pqcrypto::PostQuantumContainer`] - Post-quantum crypto operations +//! - [`crate::misc::CryptError`] - Error handling +//! - [`crate::misc::create_port_mapping`] - Port scrambling + use crate::misc::{create_port_mapping, CryptError}; use byteorder::{BigEndian, ByteOrder}; use rand::{thread_rng, Rng, RngCore}; @@ -10,7 +67,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use citadel_pqcrypto::{EncryptionAlgorithmExt, PostQuantumContainer}; use rand::prelude::ThreadRng; -pub const PORT_RANGE: usize = 14; +pub const DRILL_RANGE: usize = 14; pub const BYTES_PER_STORE: usize = LARGEST_NONCE_LEN; /// The default endianness for byte storage @@ -23,7 +80,7 @@ impl EntropyBank { version: u32, algorithm: EncryptionAlgorithm, ) -> Result> { - Self::generate_raw_3d_array().map(|bytes| { + Self::generate_random_array().map(|bytes| { let port_mappings = create_port_mapping(); let transient_counter = Default::default(); EntropyBank { @@ -231,7 +288,7 @@ impl EntropyBank { } /// Downloads the data necessary to create a drill - fn generate_raw_3d_array() -> Result<[u8; BYTES_PER_STORE], CryptError> { + fn generate_random_array() -> Result<[u8; BYTES_PER_STORE], CryptError> { let mut bytes: [u8; BYTES_PER_STORE] = [0u8; BYTES_PER_STORE]; let mut trng = thread_rng(); trng.fill_bytes(&mut bytes); @@ -239,11 +296,6 @@ impl EntropyBank { Ok(bytes) } - /// Gets randmonized port mappings which contain the true information. Other ports may get bogons - pub fn get_port_mapping(&self) -> &Vec<(u16, u16)> { - &self.scramble_mappings - } - /// Serializes self to a vector pub fn serialize_to_vec(&self) -> Result, CryptError> { bincode::serialize(self).map_err(|err| CryptError::DrillUpdateError(err.to_string())) @@ -277,7 +329,7 @@ pub struct EntropyBank { /// Returns the approximate number of bytes needed to serialize a Drill pub const fn get_approx_serialized_drill_len() -> usize { - 4 + 8 + BYTES_PER_STORE + (PORT_RANGE * 16 * 2) + 4 + 8 + BYTES_PER_STORE + (DRILL_RANGE * 16 * 2) } impl Debug for EntropyBank { diff --git a/citadel_crypt/src/fcm/fcm_ratchet.rs b/citadel_crypt/src/fcm/fcm_ratchet.rs index 4f635632f..10d57002b 100644 --- a/citadel_crypt/src/fcm/fcm_ratchet.rs +++ b/citadel_crypt/src/fcm/fcm_ratchet.rs @@ -1,3 +1,57 @@ +//! # Firebase Cloud Messaging (FCM) Ratchet +//! +//! This module implements a specialized cryptographic ratchet optimized for Firebase Cloud +//! Messaging (FCM) communication. It provides a lightweight, size-constrained implementation +//! that adheres to FCM's 4KB message limit while maintaining strong security guarantees. +//! +//! ## Features +//! - Compact ratchet implementation for FCM messages +//! - Size-optimized cryptographic operations +//! - Post-quantum cryptography support +//! - Secure key evolution and management +//! - Message protection and validation +//! - Support for local encryption/decryption +//! +//! ## Usage Example +//! ```rust +//! use citadel_crypt::fcm::fcm_ratchet::{ThinRatchet, ThinRatchetConstructor}; +//! use citadel_crypt::stacked_ratchet::Ratchet; +//! use citadel_pqcrypto::constructor_opts::ConstructorOpts; +//! use citadel_types::crypto::SecurityLevel; +//! +//! // Create a new ratchet for Alice +//! let cid = 12345; +//! let version = 0; +//! let opts = ConstructorOpts::new_default(); +//! let constructor = ThinRatchetConstructor::new_alice( +//! cid, +//! version, +//! opts, +//! ).unwrap(); +//! +//! // Build the ratchet +//! let ratchet = constructor.finish().unwrap(); +//! +//! // Use the ratchet for encryption/decryption +//! let message = b"Hello FCM!"; +//! let encrypted = ratchet.encrypt(message).unwrap(); +//! let decrypted = ratchet.decrypt(&encrypted).unwrap(); +//! assert_eq!(message.as_ref(), decrypted.as_slice()); +//! ``` +//! +//! ## Important Notes +//! - Optimized for FCM's 4KB message size limit +//! - Uses FireSaber for post-quantum security +//! - Implements the Ratchet trait for compatibility +//! - Supports both synchronous and asynchronous operations +//! - Maintains perfect forward secrecy +//! +//! ## Related Components +//! - [`FcmKeys`](super::keys::FcmKeys): FCM credential management +//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Entropy source +//! - [`PostQuantumContainer`](citadel_pqcrypto::PostQuantumContainer): PQ crypto operations +//! - [`Ratchet`](crate::stacked_ratchet::Ratchet): Base ratchet trait + use crate::endpoint_crypto_container::EndpointRatchetConstructor; use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; diff --git a/citadel_crypt/src/fcm/keys.rs b/citadel_crypt/src/fcm/keys.rs index 5cf94c9cb..be29dfb08 100644 --- a/citadel_crypt/src/fcm/keys.rs +++ b/citadel_crypt/src/fcm/keys.rs @@ -1,3 +1,48 @@ +//! # Firebase Cloud Messaging (FCM) Keys Management +//! +//! This module provides secure management of Firebase Cloud Messaging credentials, +//! implementing thread-safe access to API keys and client IDs while ensuring proper +//! memory management and serialization capabilities. +//! +//! ## Features +//! - Thread-safe storage of FCM credentials +//! - Efficient memory management through Arc +//! - Serialization support for persistence +//! - Type-safe key construction +//! - Debug formatting with sensitive data handling +//! +//! ## Usage Example +//! ```rust +//! use citadel_crypt::fcm::keys::FcmKeys; +//! +//! // Create new FCM keys +//! let keys = FcmKeys::new( +//! "your-api-key-here", +//! "your-client-id-here" +//! ); +//! +//! // Access keys (thread-safe) +//! println!("API Key: {}", keys.api_key); +//! println!("Client ID: {}", keys.client_id); +//! +//! // Keys can be cloned efficiently (only clones Arc) +//! let keys_clone = keys.clone(); +//! +//! // Serialize for storage if needed +//! let serialized = serde_json::to_string(&keys).unwrap(); +//! ``` +//! +//! ## Important Notes +//! - Uses Arc for efficient sharing across threads +//! - Implements Debug trait with safe credential display +//! - Keys are immutable after creation for security +//! - Supports serialization for persistent storage +//! +//! ## Related Components +//! - [`FcmClient`](super::client::FcmClient): FCM client implementation +//! - [`FcmMessage`](super::message::FcmMessage): FCM message structure +//! - Firebase Cloud Messaging service integration + use serde::__private::Formatter; use serde::{Deserialize, Serialize}; use std::ops::Deref; diff --git a/citadel_crypt/src/lib.rs b/citadel_crypt/src/lib.rs index ea39dc3a3..a52ba371f 100644 --- a/citadel_crypt/src/lib.rs +++ b/citadel_crypt/src/lib.rs @@ -1,4 +1,33 @@ -//! Hyxe Cryptography is a crypto crate designed for use in the Lusna Protocol +//! # Citadel Cryptographic Core (citadel_crypt) +//! +//! A comprehensive cryptographic framework providing secure communication primitives for the Citadel Protocol. +//! This crate serves as the cryptographic backbone, implementing various security mechanisms including +//! post-quantum cryptography, perfect forward secrecy, and anti-replay protection. +//! +//! ## Features +//! +//! * **Post-Quantum Security**: Integration with quantum-resistant cryptographic algorithms +//! * **Perfect Forward Secrecy**: Implemented through stacked ratchet mechanisms +//! * **Secure Memory Management**: Zero-copy secure buffer implementations for sensitive data +//! * **Entropy Management**: Sophisticated entropy banking system for secure key derivation +//! * **Network Security**: Packet vectorization and port scrambling for enhanced communication security +//! * **FCM (Forward Chain Messaging)**: Cryptographic primitives for secure message forwarding +//! * **Argon2 Integration**: Memory-hard key derivation with auto-tuning capabilities +//! +//! ## Important Notes +//! +//! * All cryptographic operations are designed to be thread-safe and memory-efficient +//! * The crate implements defense-in-depth with multiple layers of security +//! * Zero-copy operations are used where possible to minimize exposure of sensitive data +//! * Automatic memory zeroing is implemented for sensitive data structures +//! +//! ## Related Components +//! +//! * `citadel_pqcrypto`: Post-quantum cryptographic primitives +//! * `citadel_types`: Common type definitions used across the Citadel Protocol +//! * `citadel_wire`: Network protocol implementation +//! + #![deny( trivial_numeric_casts, unused_extern_crates, diff --git a/citadel_crypt/src/misc.rs b/citadel_crypt/src/misc.rs index 91f4a2a70..2cd966601 100644 --- a/citadel_crypt/src/misc.rs +++ b/citadel_crypt/src/misc.rs @@ -1,4 +1,50 @@ -use crate::entropy_bank::PORT_RANGE; +//! Cryptographic Utility Functions and Error Types +//! +//! This module provides common utility functions and error types used throughout +//! the cryptographic operations. It includes port mapping generation and a +//! flexible error handling system for cryptographic operations. +//! +//! # Features +//! +//! - Generic cryptographic error type +//! - Error conversion utilities +//! - Random port mapping generation +//! - Security level validation +//! - Debug and Display implementations +//! - Type-safe error conversions +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::misc::{CryptError, create_port_mapping}; +//! +//! fn handle_crypto_error() { +//! // Create and convert error +//! let error = CryptError::Encrypt("Failed to encrypt data"); +//! let error_string = error.into_string(); +//! +//! // Generate random port mappings +//! let port_pairs = create_port_mapping(); +//! for (src, dst) in port_pairs { +//! println!("Mapping port {} to {}", src, dst); +//! } +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Error types are generic over the error message type +//! - Port mappings are cryptographically random +//! - Error messages are safely convertible +//! - Debug and Display implementations are provided +//! - Port range is configurable via constants +//! +//! # Related Components +//! +//! - [`crate::entropy_bank`] - Uses port mappings +//! - [`citadel_types::crypto::SecurityLevel`] - Security settings + +use crate::entropy_bank::DRILL_RANGE; use rand::prelude::SliceRandom; use rand::thread_rng; use std::fmt::{Display, Formatter}; @@ -60,10 +106,10 @@ impl> Display for CryptError { /// Creates a port pair mapping at random pub fn create_port_mapping() -> Vec<(u16, u16)> { - let mut input_ports = Vec::with_capacity(PORT_RANGE); - let mut output_ports = Vec::with_capacity(PORT_RANGE); + let mut input_ports = Vec::with_capacity(DRILL_RANGE); + let mut output_ports = Vec::with_capacity(DRILL_RANGE); - for i in 0..PORT_RANGE { + for i in 0..DRILL_RANGE { input_ports.push(i); output_ports.push(i); } @@ -72,8 +118,8 @@ pub fn create_port_mapping() -> Vec<(u16, u16)> { input_ports.as_mut_slice().shuffle(&mut rng); output_ports.as_mut_slice().shuffle(&mut rng); - let mut output_vec = Vec::with_capacity(PORT_RANGE); - for i in 0..PORT_RANGE { + let mut output_vec = Vec::with_capacity(DRILL_RANGE); + for i in 0..DRILL_RANGE { output_vec.push((input_ports[i] as u16, output_ports[i] as u16)); } diff --git a/citadel_crypt/src/packet_vector.rs b/citadel_crypt/src/packet_vector.rs index 1c278e7d0..465cd2f7f 100644 --- a/citadel_crypt/src/packet_vector.rs +++ b/citadel_crypt/src/packet_vector.rs @@ -1,8 +1,62 @@ +//! Packet Vector: Secure Packet Sequencing and Port Mapping +//! +//! This module provides secure packet sequencing and port mapping functionality +//! through wave-based packet organization and scrambled port assignments. It ensures +//! ordered packet delivery while obscuring true sequence information. +//! +//! # Features +//! +//! - Wave-based packet organization +//! - Scrambled port assignments +//! - Secure sequence tracking +//! - Port mapping coordination +//! - Zero-knowledge sequence hiding +//! - Automatic memory zeroing +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::packet_vector::{PacketVector, generate_packet_vector}; +//! use citadel_crypt::entropy_bank::EntropyBank; +//! +//! fn coordinate_packets() -> Result<(), anyhow::Error> { +//! // Create entropy bank for port scrambling +//! let bank = EntropyBank::new(1234, 1, Default::default())?; +//! +//! // Generate packet vector for sequence 0 +//! let vector = generate_packet_vector(0, 5678, &bank); +//! +//! // Access vector properties +//! println!("Wave ID: {}", vector.wave_id); +//! println!("Local Port: {}", vector.local_port); +//! println!("Remote Port: {}", vector.remote_port); +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Port assignments are cryptographically scrambled +//! - True sequence numbers are never transmitted +//! - Memory is automatically zeroed on drop +//! - Wave IDs wrap around at u32::MAX +//! - Port ranges must be non-repeating +//! +//! # Related Components +//! +//! - [`EntropyBank`] - Provides port scrambling +//! - [`crate::secure_buffer::sec_packet`] - Packet buffer implementation +//! + use crate::entropy_bank::EntropyBank; +use num_integer::Integer; use zeroize::ZeroizeOnDrop; + /// The `scrambled_sequence` that is returned by `get_packet_coordinates` is scrambled; the true value of the sequence /// is NOT given, because it is expected that the values be imprinted upon the packet header and thus are public-facing #[derive(Debug, Default, Clone, ZeroizeOnDrop)] +/// Represents a packet vector with secure sequencing and port mapping information. pub struct PacketVector { /// The group ID of this packet pub group_id: u64, @@ -21,9 +75,17 @@ pub struct PacketVector { pub true_sequence: usize, } -use num_integer::Integer; - -/// The true sequence should just be the exact order of the data without any consideration of sequence nor wave-ID +/// Generates a packet vector with secure sequencing and port mapping information. +/// +/// # Parameters +/// +/// * `true_sequence`: The original true sequence number. +/// * `group_id`: The group ID of the packet. +/// * `drill`: The entropy bank used for port scrambling. +/// +/// # Returns +/// +/// A `PacketVector` instance with secure sequencing and port mapping information. pub fn generate_packet_vector( true_sequence: usize, group_id: u64, @@ -44,7 +106,18 @@ pub fn generate_packet_vector( } } -/// This will return None if the values are invalid +/// Generates packet coordinates from wave ID, source port, local port, and scramble drill. +/// +/// # Parameters +/// +/// * `wave_id`: The wave ID of the packet. +/// * `src_port`: The source port of the packet. +/// * `local_port`: The local port of the packet. +/// * `scramble_drill`: The entropy bank used for port scrambling. +/// +/// # Returns +/// +/// The true sequence number if the values are valid, otherwise `None`. #[inline] pub fn generate_packet_coordinates_inv( wave_id: u32, diff --git a/citadel_crypt/src/scramble/crypt_splitter.rs b/citadel_crypt/src/scramble/crypt_splitter.rs index 0afb49742..073ff832d 100644 --- a/citadel_crypt/src/scramble/crypt_splitter.rs +++ b/citadel_crypt/src/scramble/crypt_splitter.rs @@ -1,3 +1,61 @@ +//! # Cryptographic Packet Splitting and Scrambling +//! +//! This module implements secure packet splitting and scrambling for encrypted data transmission. +//! It provides mechanisms to split large messages into smaller encrypted packets that can be +//! transmitted securely and reassembled at the destination. +//! +//! ## Features +//! - Splits large messages into smaller encrypted packets +//! - Implements wave-based packet organization for efficient transmission +//! - Provides packet scrambling for enhanced security +//! - Supports dynamic packet sizing based on security level +//! - Implements timeout mechanisms for both individual waves and entire groups +//! - Handles packet reconstruction with missing packet detection +//! +//! ## Usage Example +//! ```rust +//! use citadel_crypt::scramble::crypt_splitter::{ +//! GroupSenderDevice, GroupReceiver, GroupReceiverConfig, +//! SecurityLevel, TransferType +//! }; +//! +//! // Create sender configuration +//! let cfg = GroupReceiverConfig::new_refresh( +//! group_id, // Unique group identifier +//! object_id, // Object being transferred +//! header_size, // Size of packet headers +//! plaintext_length, // Length of data to send +//! max_packet_size, // Maximum size per packet +//! full_waves, // Number of full waves +//! partial_waves, // Number of partial waves +//! max_bytes_per_wave,// Maximum bytes per wave +//! last_wave_bytes, // Bytes in last wave +//! packets_per_wave, // Packets per wave +//! last_cipher_len, // Last wave ciphertext length +//! &transfer_type, // Transfer type +//! false, // Empty transfer flag +//! ); +//! +//! // Create receiver +//! let mut receiver = GroupReceiver::new( +//! cfg.clone(), +//! wave_timeout_ms, // Wave timeout in milliseconds +//! group_timeout_ms, // Group timeout in milliseconds +//! ); +//! ``` +//! +//! ## Important Notes +//! - Maximum packet size is determined by the security level and encryption algorithm +//! - Wave-based transmission allows for efficient packet organization and retransmission +//! - Implements timeout mechanisms to prevent indefinite waiting for missing packets +//! - Supports both encrypted and unencrypted packet transmission +//! +//! ## Related Components +//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Provides cryptographic entropy +//! - [`PacketVector`](crate::packet_vector::PacketVector): Handles packet orientation +//! - [`Ratchet`](crate::stacked_ratchet::Ratchet): Manages encryption keys +//! - [`PostQuantumContainer`](citadel_pqcrypto::PostQuantumContainer): Post-quantum cryptography + use std::borrow::Cow; use std::collections::HashMap; use std::ops::{Range, RangeBounds}; @@ -436,7 +494,7 @@ pub struct GroupReceiverConfig { pub header_size_bytes: u64, pub group_id: u64, pub object_id: ObjectId, - // only relevant for files. Note: if transfer type is RemoteVirtualFileystem, then, + // only relevant for files. Note: if transfer type is RemoteVirtualFilesystem, then, // the receiving endpoint won't decrypt the first level of encryption since the goal // is to keep the file remotely encrypted pub transfer_type: Option, diff --git a/citadel_crypt/src/secure_buffer/mod.rs b/citadel_crypt/src/secure_buffer/mod.rs index 3d6e08778..e336b7d8f 100644 --- a/citadel_crypt/src/secure_buffer/mod.rs +++ b/citadel_crypt/src/secure_buffer/mod.rs @@ -1,3 +1,35 @@ +//! Secure Buffer Management +//! +//! This module provides secure buffer implementations for efficient and safe +//! handling of sensitive data. It includes specialized buffers for both general +//! data and packet-specific operations. +//! +//! # Features +//! +//! - Memory-safe buffer implementations +//! - Zero-copy data handling where possible +//! - Automatic memory zeroing on drop +//! - Efficient packet writing utilities +//! - Partitioned buffer support +//! +//! # Components +//! +//! - [`partitioned_sec_buffer`]: Efficient partitioned buffer implementation +//! - [`sec_packet`]: Secure packet buffer implementation +//! +//! # Important Notes +//! +//! - All buffers implement automatic zeroing +//! - Memory is allocated only when needed +//! - Thread-safe implementations available +//! - Optimized for both small and large data sets +//! +//! # Related Components +//! +//! - [`crate::streaming_crypt_scrambler`] - Uses secure buffers for streaming +//! - [`crate::packet_vector`] - Packet handling utilities +//! + /// For efficient writing to data pub mod partitioned_sec_buffer; /// For efficient writing of data onto packets diff --git a/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs b/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs index d7aab13b2..0be1e575f 100644 --- a/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs +++ b/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs @@ -1,8 +1,65 @@ +//! Partitioned Secure Buffer Implementation +//! +//! This module provides a secure buffer implementation that supports fixed-size +//! partitioning for efficient memory management and data isolation. Each partition +//! maintains its own boundaries while sharing a single underlying buffer. +//! +//! # Features +//! +//! - Generic over number of partitions +//! - Zero-copy partition access +//! - Boundary-checked partition operations +//! - Memory-safe partition windows +//! - Automatic buffer zeroing +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::secure_buffer::partitioned_sec_buffer::PartitionedSecBuffer; +//! +//! // Create a buffer with 2 partitions +//! let mut buffer = PartitionedSecBuffer::<2>::new().unwrap(); +//! +//! // Reserve space in partitions +//! buffer.reserve_partition(0, 32).unwrap(); // 32 bytes for first partition +//! buffer.reserve_partition(1, 64).unwrap(); // 64 bytes for second partition +//! +//! // Access partition windows +//! let mut window = buffer.partition_window(0).unwrap(); +//! window.copy_from_slice(&[0u8; 32]); // Write to first partition +//! ``` +//! +//! # Important Notes +//! +//! - Partitions must be reserved in order +//! - Partition boundaries are strictly enforced +//! - Buffer is automatically zeroed on drop +//! - Windows provide safe partition access +//! +//! # Related Components +//! +//! - [`SecBuffer`] - Underlying secure buffer +//! - [`crate::secure_buffer::sec_packet`] - Packet buffer implementation +//! + use bytes::{BufMut, BytesMut}; use citadel_types::crypto::SecBuffer; use std::ops::{Deref, DerefMut, Range}; -/// N determines the number of partitions in the buffer +/// A secure buffer implementation with N fixed-size partitions +/// +/// The buffer is divided into N partitions, each with its own size and boundaries. +/// Partitions must be reserved in order and provide safe, isolated access to their +/// respective memory regions. +/// +/// # Type Parameters +/// +/// * `N` - Number of partitions in the buffer +/// +/// # Fields +/// +/// * `layout` - Array storing the size of each partition +/// * `buffer` - Underlying secure buffer storing all partition data #[derive(Debug)] pub struct PartitionedSecBuffer { layout: [u32; N], @@ -10,6 +67,11 @@ pub struct PartitionedSecBuffer { } impl PartitionedSecBuffer { + /// Creates a new partitioned secure buffer with N partitions + /// + /// # Errors + /// + /// Returns an error if the number of partitions is zero pub fn new() -> std::io::Result { if N != 0 { Ok(Self { @@ -24,7 +86,17 @@ impl PartitionedSecBuffer { } } - /// This should only be called once, and called in order. Adds 'len' bytes to buffer at partition index 'idx' + /// Reserves space for a partition at the specified index + /// + /// # Parameters + /// + /// * `idx` - Index of the partition to reserve + /// * `len` - Size of the partition in bytes + /// + /// # Errors + /// + /// Returns an error if the index is out of bounds, the partition is already reserved, + /// or if previous partitions have not been reserved pub fn reserve_partition(&mut self, idx: usize, len: u32) -> std::io::Result<()> { self.check_reserve(idx)?; self.buffer.handle().put_bytes(0, len as _); @@ -33,7 +105,15 @@ impl PartitionedSecBuffer { Ok(()) } - /// Returns a window to the partition slice + /// Returns a window to the partition slice at the specified index + /// + /// # Parameters + /// + /// * `idx` - Index of the partition to access + /// + /// # Errors + /// + /// Returns an error if the index is out of bounds pub fn partition_window(&mut self, idx: usize) -> std::io::Result { let range = self.get_range(idx)?; Ok(SliceHandle { @@ -42,6 +122,15 @@ impl PartitionedSecBuffer { }) } + /// Returns the range of the partition at the specified index + /// + /// # Parameters + /// + /// * `idx` - Index of the partition to access + /// + /// # Errors + /// + /// Returns an error if the index is out of bounds fn get_range(&self, idx: usize) -> std::io::Result> { self.check_index(idx)?; let start_idx = self.layout.iter().take(idx).copied().sum::() as usize; // at 0, we get 0. At 1, we get the sum of the first partition width @@ -56,6 +145,15 @@ impl PartitionedSecBuffer { Ok(start_idx..end_idx) } + /// Checks if the index is within bounds + /// + /// # Parameters + /// + /// * `idx` - Index to check + /// + /// # Errors + /// + /// Returns an error if the index is out of bounds fn check_index(&self, idx: usize) -> std::io::Result<()> { if idx >= N { Err(std::io::Error::new( @@ -67,6 +165,16 @@ impl PartitionedSecBuffer { } } + /// Checks if the partition can be reserved + /// + /// # Parameters + /// + /// * `idx` - Index of the partition to reserve + /// + /// # Errors + /// + /// Returns an error if the index is out of bounds, the partition is already reserved, + /// or if previous partitions have not been reserved fn check_reserve(&self, idx: usize) -> std::io::Result<()> { self.check_index(idx)?; // make sure current value is unset @@ -100,15 +208,18 @@ impl PartitionedSecBuffer { Ok(()) } + /// Consumes the buffer and returns the underlying bytes pub fn into_buffer(self) -> BytesMut { self.buffer.into_buffer() } + /// Returns a reference to the partition layout pub fn layout(&self) -> &[u32; N] { &self.layout } } +/// A handle to a partition slice pub struct SliceHandle<'a> { pub(crate) range: Range, ptr: &'a mut SecBuffer, diff --git a/citadel_crypt/src/secure_buffer/sec_packet.rs b/citadel_crypt/src/secure_buffer/sec_packet.rs index 887534f86..5ac5783c7 100644 --- a/citadel_crypt/src/secure_buffer/sec_packet.rs +++ b/citadel_crypt/src/secure_buffer/sec_packet.rs @@ -1,3 +1,58 @@ +//! Secure Packet Buffer Implementation +//! +//! This module provides specialized buffer implementations for handling secure +//! network packets. It supports structured packet layouts with header, payload, +//! and extended payload sections while maintaining memory safety and efficiency. +//! +//! # Features +//! +//! - Three-part packet structure (header, payload, extension) +//! - Zero-copy packet construction +//! - Memory-safe section access +//! - Ordered write operations +//! - Automatic buffer cleanup +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::secure_buffer::sec_packet::SecureMessagePacket; +//! +//! // Create a new packet +//! let mut packet = SecureMessagePacket::<32>::new().unwrap(); +//! +//! // Write payload first +//! packet.write_payload(64, |buf| { +//! buf.copy_from_slice(&[0u8; 64]); +//! Ok(()) +//! }).unwrap(); +//! +//! // Write header second +//! packet.write_header(|buf| { +//! buf[0] = 1; // packet type +//! Ok(()) +//! }).unwrap(); +//! +//! // Write extension last and get final bytes +//! let bytes = packet.write_payload_extension(32, |buf| { +//! buf.copy_from_slice(&[0u8; 32]); +//! Ok(()) +//! }).unwrap(); +//! ``` +//! +//! # Important Notes +//! +//! - Writes must occur in order: payload, header, extension +//! - All sections are automatically zeroed on drop +//! - Buffer sizes are fixed after reservation +//! - Thread-safe implementation available +//! +//! # Related Components +//! +//! - [`PartitionedSecBuffer`] - Underlying buffer implementation +//! - [`crate::packet_vector`] - Packet vector operations +//! - [`crate::streaming_crypt_scrambler`] - Streaming encryption +//! + use crate::secure_buffer::partitioned_sec_buffer::{PartitionedSecBuffer, SliceHandle}; use byteorder::{BigEndian, ByteOrder}; use bytes::BytesMut; @@ -18,36 +73,44 @@ const PAYLOAD_PART: usize = 1; const PAYLOAD_EXT: usize = 2; impl SecurePacket { + /// Creates a new secure packet. pub fn new() -> Self { Self { inner: PartitionedSecBuffer::<3>::new().unwrap(), } } + /// Prepares the header section for writing. pub fn prepare_header(&mut self, len: u32) -> std::io::Result<()> { self.inner.reserve_partition(HEADER_PART, len) } + /// Returns a mutable reference to the header section. pub fn header(&mut self) -> std::io::Result { self.inner.partition_window(HEADER_PART) } + /// Prepares the payload section for writing. pub fn prepare_payload(&mut self, len: u32) -> std::io::Result<()> { self.inner.reserve_partition(PAYLOAD_PART, len) } + /// Returns a mutable reference to the payload section. pub fn payload(&mut self) -> std::io::Result { self.inner.partition_window(PAYLOAD_PART) } + /// Prepares the extended payload section for writing. pub fn prepare_extended_payload(&mut self, len: u32) -> std::io::Result<()> { self.inner.reserve_partition(PAYLOAD_EXT, len) } + /// Returns a mutable reference to the extended payload section. pub fn extended_payload(&mut self) -> std::io::Result { self.inner.partition_window(PAYLOAD_EXT) } + /// Consumes the packet and returns the underlying buffer. pub fn into_packet(self) -> BytesMut { self.inner.into_buffer() } @@ -67,19 +130,20 @@ pub enum SecureMessagePacket { } impl SecureMessagePacket { + /// Creates a new secure message packet. pub fn new() -> std::io::Result { let mut init = SecurePacket::new(); init.prepare_header(N as _)?; Ok(Self::PayloadNext(init)) } - /// Takes a raw input and splits it into the payload and payload-extension + /// Takes a raw input and splits it into the payload and payload-extension. pub fn decompose_payload_raw(input: &mut BytesMut) -> std::io::Result<(BytesMut, BytesMut)> { let payload = Self::extract_payload(input)?; Ok((payload, input.split())) } - /// Takes a raw input and splits it into the payload and payload-extension + /// Takes a raw input and splits it into the payload and payload-extension. pub fn extract_payload(input: &mut BytesMut) -> std::io::Result { if input.len() < 4 { return Err(std::io::Error::new( @@ -102,7 +166,7 @@ impl SecureMessagePacket { Ok(payload) } - /// The first write to the buffer should be the payload + /// The first write to the buffer should be the payload. pub fn write_payload( &mut self, len: u32, @@ -125,7 +189,7 @@ impl SecureMessagePacket { } } - /// The second write to the buffer should be the header + /// The second write to the buffer should be the header. pub fn write_header( &mut self, fx: impl FnOnce(&mut [u8]) -> std::io::Result<()>, @@ -144,7 +208,7 @@ impl SecureMessagePacket { } } - /// The final write to the buffer should be the payload extension. This consumes self and returns bytes + /// The final write to the buffer should be the payload extension. This consumes self and returns bytes. pub fn write_payload_extension( self, len: u32, @@ -164,6 +228,7 @@ impl SecureMessagePacket { } } + /// Returns the length of the message. pub fn message_len(&self) -> usize { match self { Self::FinalPayloadExt(p) | Self::HeaderNext(p) | Self::PayloadNext(p) => { diff --git a/citadel_crypt/src/stacked_ratchet.rs b/citadel_crypt/src/stacked_ratchet.rs index ad73b7a03..b1b8a3a8e 100644 --- a/citadel_crypt/src/stacked_ratchet.rs +++ b/citadel_crypt/src/stacked_ratchet.rs @@ -1,3 +1,64 @@ +//! Stacked Ratchet: Perfect Forward Secrecy with Key Evolution +//! +//! This module implements a stacked ratchet system that provides perfect forward +//! secrecy through continuous key evolution. It supports both message protection +//! and scrambling operations with independent keys. +//! +//! # Features +//! +//! - Perfect forward secrecy +//! - Independent message and scramble keys +//! - Post-quantum cryptography support +//! - Key evolution and ratcheting +//! - Security level configuration +//! - Anti-replay attack protection +//! - Ordered packet delivery +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::stacked_ratchet::{StackedRatchet, constructor::StackedRatchetConstructor}; +//! use citadel_pqcrypto::constructor_opts::ConstructorOpts; +//! use citadel_types::crypto::SecurityLevel; +//! +//! fn setup_ratchet() -> Option { +//! // Create constructor options +//! let opts = vec![ConstructorOpts::default()]; +//! +//! // Initialize Alice's constructor +//! let constructor = StackedRatchetConstructor::new_alice( +//! opts, +//! 1234, // Client ID +//! 1, // Version +//! Some(SecurityLevel::Standard) +//! )?; +//! +//! // Generate Alice's initial ratchet +//! let ratchet = constructor.finish()?; +//! +//! // Use ratchet for packet protection +//! let mut packet = vec![0u8; 64]; +//! ratchet.protect_message_packet(None, 32, &mut packet).ok()?; +//! +//! Some(ratchet) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Keys evolve after each use +//! - Message and scramble keys are independent +//! - Security levels affect key composition +//! - Packet order must be maintained +//! - Anti-replay protection is automatic +//! +//! # Related Components +//! +//! - [`EntropyBank`] - Provides entropy for key evolution +//! - [`PostQuantumContainer`] - Post-quantum cryptography +//! - [`crate::endpoint_crypto_container`] - Endpoint state management +//! + use crate::endpoint_crypto_container::EndpointRatchetConstructor; use crate::entropy_bank::EntropyBank; use crate::fcm::fcm_ratchet::ThinRatchet; @@ -25,22 +86,39 @@ pub struct StackedRatchet { pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + 'static { type Constructor: EndpointRatchetConstructor + Serialize + for<'a> Deserialize<'a>; + /// Returns the client ID fn get_cid(&self) -> u64; + + /// Returns the version fn version(&self) -> u32; + + /// Determines if any of the ratchets have verified packets fn has_verified_packets(&self) -> bool; + + /// Resets the anti-replay attack counters fn reset_ara(&self); + + /// Returns the default security level fn get_default_security_level(&self) -> SecurityLevel; + + /// Returns the message PQC and drill for the specified index fn message_pqc_drill(&self, idx: Option) -> (&PostQuantumContainer, &EntropyBank); + + /// Returns the scramble drill fn get_scramble_drill(&self) -> &EntropyBank; + /// Returns the next constructor options fn get_next_constructor_opts(&self) -> Vec; + /// Protects a message packet fn protect_message_packet( &self, security_level: Option, header_len_bytes: usize, packet: &mut T, ) -> Result<(), CryptError>; + + /// Validates a message packet fn validate_message_packet, T: EzBuffer>( &self, security_level: Option, @@ -48,6 +126,7 @@ pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + ' packet: &mut T, ) -> Result<(), CryptError>; + /// Returns the next Alice constructor fn next_alice_constructor(&self) -> Option { Self::Constructor::new_alice( self.get_next_constructor_opts(), @@ -57,11 +136,14 @@ pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + ' ) } + /// Encrypts data locally fn local_encrypt<'a, T: Into>>( &self, contents: T, security_level: SecurityLevel, ) -> Result, CryptError>; + + /// Decrypts data locally fn local_decrypt<'a, T: Into>>( &self, contents: T, @@ -76,6 +158,7 @@ pub enum RatchetType { } impl RatchetType { + /// Returns the default ratchet if it exists pub fn assume_default(self) -> Option { if let RatchetType::Default(r) = self { Some(r) @@ -917,14 +1000,14 @@ pub mod constructor { } /// Updates the internal version - pub fn update_version(&mut self, proposed_version: u32) -> Option<()> { - self.new_version = proposed_version; + pub fn update_version(&mut self, version: u32) -> Option<()> { + self.new_version = version; for container in self.message.inner.iter_mut() { - container.drill.as_mut()?.version = proposed_version; + container.drill.as_mut()?.version = version; } - self.scramble.drill.as_mut()?.version = proposed_version; + self.scramble.drill.as_mut()?.version = version; Some(()) } diff --git a/citadel_crypt/src/streaming_crypt_scrambler.rs b/citadel_crypt/src/streaming_crypt_scrambler.rs index bcb383e01..b38d8f9ce 100644 --- a/citadel_crypt/src/streaming_crypt_scrambler.rs +++ b/citadel_crypt/src/streaming_crypt_scrambler.rs @@ -1,3 +1,75 @@ +//! # Streaming Cryptographic Scrambler +//! +//! This module provides asynchronous streaming encryption and scrambling capabilities for large data sources. +//! It enables secure transmission of data streams by breaking them into encrypted groups and managing their +//! transmission with backpressure support. +//! +//! ## Features +//! - Asynchronous streaming encryption of large data sources +//! - Support for file-based and in-memory data sources +//! - Configurable group sizes for optimal performance +//! - Backpressure support through async/await +//! - Custom header inscription for packets +//! - Progress tracking and cancellation support +//! +//! ## Usage Example +//! ```rust +//! use citadel_crypt::{ +//! streaming_crypt_scrambler::{scramble_encrypt_source, BytesSource}, +//! SecurityLevel, ObjectId, TransferType +//! }; +//! use tokio::sync::mpsc; +//! use tokio::sync::oneshot; +//! +//! async fn encrypt_stream() { +//! // Create channels for group sending and stopping +//! let (group_sender, mut group_receiver) = mpsc::channel(10); +//! let (stop_sender, stop_receiver) = oneshot::channel(); +//! +//! // Create a source (e.g., from bytes) +//! let data = vec![0u8; 1024]; +//! let source = BytesSource::from(data); +//! +//! // Header inscriber function +//! let header_inscriber = |vector: &PacketVector, +//! bank: &EntropyBank, +//! obj_id: ObjectId, +//! cid: u64, +//! bytes: &mut BytesMut| { +//! // Inscribe header data +//! }; +//! +//! // Start encryption +//! let result = scramble_encrypt_source( +//! source, +//! None, // Use default group size +//! ObjectId::new(), +//! group_sender, +//! stop_receiver, +//! SecurityLevel::Standard, +//! hyper_ratchet, +//! static_aux_ratchet, +//! header_size, +//! target_cid, +//! group_id, +//! TransferType::Standard, +//! header_inscriber, +//! ).await; +//! } +//! ``` +//! +//! ## Important Notes +//! - Maximum group size is limited to prevent excessive memory usage +//! - Sources are consumed during streaming and cannot be reused +//! - Implements efficient buffering for optimal performance +//! - Supports both filesystem and in-memory sources +//! +//! ## Related Components +//! - [`crypt_splitter`](crate::scramble::crypt_splitter): Core encryption and packet splitting +//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Cryptographic entropy source +//! - [`PacketVector`](crate::packet_vector::PacketVector): Packet orientation management +//! - [`StackedRatchet`](crate::stacked_ratchet::StackedRatchet): Key management + use bytes::BytesMut; use citadel_io::tokio; use futures::task::Context; diff --git a/citadel_crypt/src/sync_toggle.rs b/citadel_crypt/src/sync_toggle.rs index 3ac82ed07..5258496fa 100644 --- a/citadel_crypt/src/sync_toggle.rs +++ b/citadel_crypt/src/sync_toggle.rs @@ -1,5 +1,59 @@ +//! Thread-Safe Toggle State Management +//! +//! This module provides a thread-safe toggle implementation for managing atomic +//! state transitions. It supports one-way state changes with detection of previous +//! state, making it ideal for managing cryptographic state transitions. +//! +//! # Features +//! +//! - Thread-safe state management +//! - Atomic state transitions +//! - One-way toggle operations +//! - State change detection +//! - Serialization support +//! - Default state handling +//! +//! # Examples +//! +//! ```rust +//! use citadel_crypt::sync_toggle::{SyncToggle, CurrentToggleState}; +//! +//! fn manage_state() { +//! // Create new toggle +//! let toggle = SyncToggle::new(); +//! +//! // Try to toggle on +//! match toggle.toggle_on_if_untoggled() { +//! CurrentToggleState::JustToggled => println!("First toggle"), +//! CurrentToggleState::AlreadyToggled => println!("Already on"), +//! CurrentToggleState::Untoggled => println!("Still off"), +//! } +//! +//! // Check current state +//! let state = toggle.get(); +//! +//! // Reset to off +//! toggle.toggle_off(); +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Uses sequential consistency ordering +//! - Thread-safe through atomic operations +//! - Serializes to untoggled state by default +//! - No blocking operations +//! - Safe for concurrent access +//! +//! # Related Components +//! +//! - [`crate::entropy_bank`] - Uses toggle for state transitions +//! - [`crate::stacked_ratchet`] - Ratchet state management +//! + use serde::{Deserialize, Serialize}; +/// A thread-safe toggle for managing atomic state transitions. #[derive(Default, Serialize, Deserialize, Debug, Clone)] pub struct SyncToggle { // when serializing this, always reset to default (untoggled/false) @@ -9,22 +63,28 @@ pub struct SyncToggle { const ORDERING: std::sync::atomic::Ordering = std::sync::atomic::Ordering::SeqCst; +/// Represents the current state of a toggle operation. #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum CurrentToggleState { + /// Toggle was just changed from false to true JustToggled, + /// Toggle was already in true state AlreadyToggled, + /// Toggle is in false state Untoggled, } impl SyncToggle { + /// Creates a new toggle instance. pub fn new() -> Self { Self { inner: Default::default(), } } - // Returns true if value has already been toggled to true, - // returns false otherwise, leaving a true value in its place + /// Attempts to toggle the state to true if it's currently false. + /// + /// Returns `JustToggled` if the state was changed to true, `AlreadyToggled` if the state was already true, and `Untoggled` if the state is still false. pub fn toggle_on_if_untoggled(&self) -> CurrentToggleState { if self.inner.fetch_nand(false, ORDERING) { CurrentToggleState::AlreadyToggled @@ -33,10 +93,12 @@ impl SyncToggle { } } + /// Resets the toggle state to false. pub fn toggle_off(&self) { self.inner.store(false, ORDERING) } + /// Returns the current state of the toggle. pub fn get(&self) -> CurrentToggleState { if self.inner.load(ORDERING) { CurrentToggleState::AlreadyToggled diff --git a/citadel_crypt/src/toolset.rs b/citadel_crypt/src/toolset.rs index 17abe5c8e..c9d9c7e39 100644 --- a/citadel_crypt/src/toolset.rs +++ b/citadel_crypt/src/toolset.rs @@ -1,3 +1,59 @@ +//! # Cryptographic Toolset Management +//! +//! This module provides a management layer for cryptographic ratchets, handling version control, +//! synchronization, and lifecycle management of encryption keys. It maintains a rolling window +//! of active ratchets while ensuring secure key evolution. +//! +//! ## Features +//! - Manages multiple versions of cryptographic ratchets +//! - Provides automatic version control and synchronization +//! - Implements memory-bounded storage with configurable limits +//! - Supports static auxiliary ratchet for persistent encryption +//! - Handles secure ratchet updates and deregistration +//! - Ensures thread-safe access to cryptographic primitives +//! +//! ## Usage Example +//! ```rust +//! use citadel_crypt::toolset::{Toolset, UpdateStatus}; +//! use citadel_crypt::stacked_ratchet::StackedRatchet; +//! +//! // Create a new toolset with initial ratchet +//! let cid = 12345; +//! let initial_ratchet = StackedRatchet::new(cid, 0); +//! let mut toolset = Toolset::new(cid, initial_ratchet); +//! +//! // Create and add a new ratchet version +//! let new_ratchet = StackedRatchet::new(cid, 1); +//! match toolset.update_from(new_ratchet) { +//! Some(UpdateStatus::Committed { new_version }) => { +//! println!("Updated to version {}", new_version); +//! } +//! Some(UpdateStatus::CommittedNeedsSynchronization { new_version, old_version }) => { +//! println!("Updated to {} but need to sync version {}", new_version, old_version); +//! // Implement synchronization logic +//! toolset.deregister_oldest_hyper_ratchet(old_version).unwrap(); +//! } +//! None => println!("Update failed"), +//! } +//! +//! // Access ratchets +//! if let Some(current) = toolset.get_most_recent_hyper_ratchet() { +//! // Use current ratchet for encryption +//! } +//! ``` +//! +//! ## Important Notes +//! - Maximum number of ratchets in memory is configurable and environment-dependent +//! - Static auxiliary ratchet provides persistent encryption for stored data +//! - Version synchronization is required when maximum capacity is reached +//! - Thread-safe operations for concurrent access +//! +//! ## Related Components +//! - [`StackedRatchet`](crate::stacked_ratchet::StackedRatchet): Core ratchet implementation +//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Entropy source for ratchets +//! - [`CryptError`](crate::misc::CryptError): Error handling for cryptographic operations +//! - [`ClientNetworkAccount`]: High-level account management + use std::collections::VecDeque; use serde::{Deserialize, Serialize}; diff --git a/citadel_io/src/lib.rs b/citadel_io/src/lib.rs index bb8a1434e..cbe281f02 100644 --- a/citadel_io/src/lib.rs +++ b/citadel_io/src/lib.rs @@ -1,3 +1,56 @@ +//! # Citadel IO +//! +//! A cross-platform I/O utility crate that provides consistent interfaces for both native and WebAssembly targets. +//! This crate abstracts platform-specific implementations of common I/O operations, synchronization primitives, +//! and random number generation. +//! +//! ## Features +//! +//! - Cross-platform synchronization primitives (`Mutex`, `RwLock`) +//! - Platform-specific random number generation +//! - Deadlock detection (native only) +//! - Async runtime abstractions via Tokio +//! - WebAssembly-compatible implementations +//! +//! ## Platform Support +//! +//! ### Native (non-WASM) +//! +//! On native platforms, this crate uses: +//! - `parking_lot` for high-performance synchronization primitives +//! - Standard Tokio for async runtime +//! - System random number generator +//! - Optional deadlock detection +//! +//! ### WebAssembly +//! +//! On WASM targets, this crate provides: +//! - WebAssembly-compatible synchronization primitives +//! - WASM-specific random number generation +//! - WASM-compatible Tokio implementation +//! +//! ## Usage +//! +//! ```rust +//! use citadel_io::{Mutex, RwLock, ThreadRng}; +//! +//! // Create thread-safe synchronization primitives +//! let mutex = Mutex::new(42); +//! let rwlock = RwLock::new(String::new()); +//! +//! // Use locks safely across threads +//! { +//! let mut guard = mutex.lock(); +//! *guard += 1; +//! } +//! +//! // Read-write lock usage +//! { +//! let mut writer = rwlock.write(); +//! writer.push_str("Hello"); +//! } +//! ``` + #[cfg(not(target_family = "wasm"))] pub mod standard; #[cfg(target_family = "wasm")] @@ -20,11 +73,14 @@ pub use rand::prelude::*; #[cfg(target_family = "wasm")] pub use wasm::rng::{WasmRng as ThreadRng, *}; +/// Represents errors that can occur during I/O operations #[derive(Debug)] pub enum Error { + /// Wraps a standard I/O error IoError(std::io::Error), } +// Re-export Tokio and related crates with platform-specific implementations #[cfg(not(target_family = "wasm"))] pub use tokio; diff --git a/citadel_io/src/standard/locks.rs b/citadel_io/src/standard/locks.rs index de812d029..47a016739 100644 --- a/citadel_io/src/standard/locks.rs +++ b/citadel_io/src/standard/locks.rs @@ -1,5 +1,23 @@ +//! Native platform synchronization primitives using parking_lot. +//! +//! This module provides high-performance synchronization primitives for native platforms +//! by re-exporting parking_lot's implementations. These primitives are more efficient +//! than the standard library's synchronization types. + +/// A mutual exclusion primitive useful for protecting shared data. +/// Re-exported from parking_lot for better performance. pub type Mutex = parking_lot::Mutex; + +/// RAII guard for a mutex. The data protected by the mutex can be accessed +/// through this guard. The lock is automatically released when the guard is dropped. pub type MutexGuard<'a, T> = parking_lot::MutexGuard<'a, T>; + +/// A reader-writer lock, allowing multiple readers or a single writer at any point in time. +/// Re-exported from parking_lot for better performance. pub type RwLock = parking_lot::RwLock; + +/// RAII guard for read access to an RwLock. Multiple read guards can exist at the same time. pub type RwLockReadGuard<'a, T> = parking_lot::RwLockReadGuard<'a, T>; + +/// RAII guard for write access to an RwLock. Only one write guard can exist at a time. pub type RwLockWriteGuard<'a, T> = parking_lot::RwLockWriteGuard<'a, T>; diff --git a/citadel_io/src/wasm/locks.rs b/citadel_io/src/wasm/locks.rs index a5de9008b..c59e077af 100644 --- a/citadel_io/src/wasm/locks.rs +++ b/citadel_io/src/wasm/locks.rs @@ -1,43 +1,79 @@ +//! WebAssembly-compatible synchronization primitives. +//! +//! This module provides synchronization primitives that work in WebAssembly environments +//! by wrapping the standard library's implementations. While WebAssembly is single-threaded, +//! these primitives are still useful for maintaining API compatibility with native code +//! and for potential future multi-threading support. + +/// Type alias for WebAssembly-compatible RwLock implementation pub type RwLock = RwLockWasm; +/// Type alias for WebAssembly-compatible read guard pub type RwLockReadGuard<'a, T> = std::sync::RwLockReadGuard<'a, T>; +/// Type alias for WebAssembly-compatible write guard pub type RwLockWriteGuard<'a, T> = std::sync::RwLockWriteGuard<'a, T>; +/// Type alias for WebAssembly-compatible Mutex implementation pub type Mutex = MutexWasm; +/// Type alias for WebAssembly-compatible mutex guard pub type MutexGuard<'a, T> = std::sync::MutexGuard<'a, T>; +/// A WebAssembly-compatible reader-writer lock implementation. +/// +/// This type wraps the standard library's RwLock to provide a WebAssembly-safe +/// synchronization primitive. While WebAssembly is currently single-threaded, +/// this implementation maintains API compatibility with native code. #[derive(Default)] pub struct RwLockWasm { inner: std::sync::RwLock, } +/// A WebAssembly-compatible mutex implementation. +/// +/// This type wraps the standard library's Mutex to provide a WebAssembly-safe +/// synchronization primitive. While WebAssembly is currently single-threaded, +/// this implementation maintains API compatibility with native code. #[derive(Default)] pub struct MutexWasm { inner: std::sync::Mutex, } impl RwLockWasm { + /// Creates a new RwLock in an unlocked state ready for use. pub fn new(t: T) -> Self { Self { inner: std::sync::RwLock::new(t), } } + /// Acquires a read lock, blocking the current thread until it is available. + /// + /// The lock will be automatically released when the returned guard is dropped. + /// Since WebAssembly is single-threaded, this will never actually block. pub fn read(&self) -> RwLockReadGuard { self.inner.read().unwrap() } + /// Acquires a write lock, blocking the current thread until it is available. + /// + /// The lock will be automatically released when the returned guard is dropped. + /// Since WebAssembly is single-threaded, this will never actually block. pub fn write(&self) -> RwLockWriteGuard { self.inner.write().unwrap() } } impl MutexWasm { + /// Creates a new Mutex in an unlocked state ready for use. pub fn new(t: T) -> Self { Self { inner: std::sync::Mutex::new(t), } } + /// Acquires a mutex, blocking the current thread until it is available. + /// + /// The lock will be automatically released when the returned guard is dropped. + /// Since WebAssembly is single-threaded, this will never actually block. pub fn lock(&self) -> MutexGuard { self.inner.lock().unwrap() } diff --git a/citadel_io/src/wasm/rng.rs b/citadel_io/src/wasm/rng.rs index f1db89d0e..573e04e6c 100644 --- a/citadel_io/src/wasm/rng.rs +++ b/citadel_io/src/wasm/rng.rs @@ -1,25 +1,63 @@ +//! WebAssembly-compatible random number generation. +//! +//! This module provides a cryptographically secure random number generator +//! that works in WebAssembly environments. It uses the Web Crypto API through +//! the `getrandom` crate to generate random numbers. + use rand::{CryptoRng, Error, RngCore}; +/// A WebAssembly-compatible random number generator that provides +/// cryptographically secure random numbers using the Web Crypto API. +/// +/// This type implements both `RngCore` and `CryptoRng` traits, making it +/// suitable for both general-purpose and cryptographic use cases. +/// +/// # Example +/// +/// ```rust +/// use citadel_io::WasmRng; +/// use rand::RngCore; +/// +/// let mut rng = WasmRng::default(); +/// let random_number = rng.next_u32(); +/// let mut buffer = [0u8; 32]; +/// rng.fill_bytes(&mut buffer); +/// ``` #[derive(Default)] pub struct WasmRng; impl RngCore for WasmRng { + /// Generates a random 32-bit unsigned integer. fn next_u32(&mut self) -> u32 { u32::from_be_bytes(random_array::<4>()) } + + /// Generates a random 64-bit unsigned integer. fn next_u64(&mut self) -> u64 { u64::from_be_bytes(random_array::<8>()) } + + /// Fills the provided buffer with random bytes. fn fill_bytes(&mut self, dest: &mut [u8]) { getrandom::getrandom(dest).unwrap(); } + + /// Attempts to fill the provided buffer with random bytes. + /// Returns an error if the operation fails. fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { getrandom::getrandom(dest).map_err(|err| Error::from(err.code())) } } +// Implement CryptoRng to indicate this is a cryptographically secure RNG impl CryptoRng for WasmRng {} +/// Helper function to generate a fixed-size array of random bytes. +/// +/// # Panics +/// +/// This function will panic if the underlying random number generator fails. +/// This should never happen in practice when using the Web Crypto API. fn random_array() -> [u8; N] { let mut bytes = [0u8; N]; getrandom::getrandom(&mut bytes).unwrap(); diff --git a/citadel_logging/src/lib.rs b/citadel_logging/src/lib.rs index c27a25511..9d197f0a3 100644 --- a/citadel_logging/src/lib.rs +++ b/citadel_logging/src/lib.rs @@ -1,10 +1,92 @@ +//! # Citadel Logging +//! +//! A structured logging facade for the Citadel Protocol, built on top of +//! the `tracing` ecosystem. This crate provides consistent logging setup +//! and configuration across all Citadel Protocol components. +//! +//! ## Features +//! +//! - Structured logging with spans and events +//! - File and line number information +//! - Environment-based log level filtering +//! - Panic handling with logging +//! - Async-aware instrumentation +//! +//! ## Usage +//! +//! ```rust +//! use citadel_logging::{setup_log, info, debug, error}; +//! +//! // Initialize logging +//! setup_log(); +//! +//! // Log at different levels +//! info!(target: "citadel", "Starting application..."); +//! debug!(target: "citadel", "Configuration loaded: {:?}", config); +//! error!(target: "citadel", "Failed to connect: {}", error); +//! +//! // Use instrumentation for async functions +//! #[instrument] +//! async fn process_request() { +//! debug!(target: "citadel", "Processing request..."); +//! // ... processing ... +//! } +//! ``` +//! +//! ## Log Levels +//! +//! The following log levels are available, in order of increasing severity: +//! +//! - `trace`: Very detailed information for debugging +//! - `debug`: Useful debugging information +//! - `info`: General information about program execution +//! - `warn`: Potentially harmful situations +//! - `error`: Error conditions that should be addressed +//! +//! ## Environment Configuration +//! +//! Log levels can be configured via the `RUST_LOG` environment variable: +//! +//! ```bash +//! # Enable debug logging for citadel components +//! RUST_LOG=citadel=debug +//! +//! # Enable different levels for different components +//! RUST_LOG=citadel=debug,citadel_wire=trace +//! ``` +//! +//! ## Panic Handling +//! +//! By default, `setup_log()` installs a panic hook that logs the panic +//! information before exiting. If you need to use a custom panic hook, +//! use `setup_log_no_panic_hook()` instead. + pub use tracing::{self, debug, error, info, instrument, trace, warn}; use tracing_subscriber::fmt::format::FmtSpan; use tracing_subscriber::fmt::SubscriberBuilder; use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::EnvFilter; -/// Sets up the logging for any crate +/// Sets up logging with panic handling for any Citadel Protocol crate. +/// +/// This function: +/// 1. Installs a panic hook that logs panic information +/// 2. Configures structured logging with file and line information +/// 3. Sets up environment-based log filtering +/// +/// # Example +/// +/// ```rust +/// use citadel_logging::setup_log; +/// +/// setup_log(); +/// // Your application code here +/// ``` +/// +/// # Panics +/// +/// When a panic occurs, it will be logged at the error level before +/// the process exits with status code 1. pub fn setup_log() { std::panic::set_hook(Box::new(|info| { error!(target: "citadel", "Panic occurred: {}", info); @@ -14,12 +96,30 @@ pub fn setup_log() { setup_log_no_panic_hook() } +/// Sets up logging without installing a panic hook. +/// +/// This function provides the same logging setup as `setup_log()` but +/// without modifying the panic hook. Use this if you need to use a +/// custom panic hook or if the default panic behavior is desired. +/// +/// # Example +/// +/// ```rust +/// use citadel_logging::setup_log_no_panic_hook; +/// +/// // Install custom panic hook +/// std::panic::set_hook(Box::new(|info| { +/// // Custom panic handling +/// })); +/// +/// setup_log_no_panic_hook(); +/// ``` pub fn setup_log_no_panic_hook() { let _ = SubscriberBuilder::default() - .with_line_number(true) - .with_file(true) - .with_span_events(FmtSpan::NONE) - .with_env_filter(EnvFilter::from_default_env()) + .with_line_number(true) // Include line numbers in log output + .with_file(true) // Include file names in log output + .with_span_events(FmtSpan::NONE) // Don't log span lifecycle events + .with_env_filter(EnvFilter::from_default_env()) // Use RUST_LOG env var .finish() .try_init(); } diff --git a/citadel_pqcrypto/Cargo.toml b/citadel_pqcrypto/Cargo.toml index 99186f6a6..28bc55fbd 100644 --- a/citadel_pqcrypto/Cargo.toml +++ b/citadel_pqcrypto/Cargo.toml @@ -1,3 +1,23 @@ +# Citadel Protocol Post-Quantum Cryptography Library +# +# This crate provides the core cryptographic functionality for the Citadel Protocol, +# implementing post-quantum cryptographic algorithms and protocols. It is designed +# to be resistant to both classical and quantum computer attacks. +# +# Features: +# - Post-quantum key exchange (Kyber) +# - Post-quantum signatures (Falcon) +# - Hybrid classical/post-quantum encryption +# - AEAD encryption (AES-GCM, ChaCha20-Poly1305, Ascon) +# - Zero-knowledge proofs +# - Anti-replay attack protection +# +# Security: +# - All sensitive data uses zeroize for secure cleanup +# - No unsafe code allowed +# - Constant-time operations where possible +# - Memory-safe implementations + [package] name = "citadel_pqcrypto" version = "0.11.3" @@ -15,40 +35,51 @@ exclude = [ "./target/*" ] +# Feature flags for different compilation targets and configurations [features] +# Default features include standard library support default = ["std"] + +# Standard library support with all optimizations std = [ - "serde/std", - "aes-gcm/std", - "aes-gcm/alloc", - "chacha20poly1305/alloc", - "chacha20poly1305/std", - "bytes/std", - "kyber-pke/std", - "pqcrypto-falcon-wasi/std", - "pqcrypto-traits-wasi/std", - "rand/std", - "sha3/std" + "serde/std", # Serialization support + "aes-gcm/std", # AES-GCM encryption + "aes-gcm/alloc", # Heap allocation for AES + "chacha20poly1305/alloc", # Heap allocation for ChaCha + "chacha20poly1305/std", # ChaCha20-Poly1305 encryption + "bytes/std", # Byte buffer operations + "kyber-pke/std", # Post-quantum key exchange + "pqcrypto-falcon-wasi/std", # Post-quantum signatures + "pqcrypto-traits-wasi/std", # Common crypto traits + "rand/std", # Random number generation + "sha3/std" # Hash functions ] +# WebAssembly target support wasm = [] +# Dependencies with specific version requirements and features [dependencies] -citadel_io = { workspace = true } -generic-array = { workspace = true, features = ["serde"] } -serde = { workspace = true, features = ["derive", "rc"] } -bincode = { workspace = true } -aes-gcm = { workspace = true, features = ["heapless", "aes", "alloc"]} -chacha20poly1305 = { workspace = true, features = ["heapless", "alloc"] } -bytes = { workspace = true } -parking_lot = { workspace = true, features = ["serde"] } -log = { workspace = true } -sha3 = { workspace = true } -kyber-pke = { workspace = true, features=["90s"] } -rand = { workspace = true } -serde-big-array = { workspace = true } -ascon-aead = { workspace = true } -zeroize = { workspace = true, features = ["zeroize_derive", "alloc", "serde"] } +# Core functionality +citadel_io = { workspace = true } # I/O operations +generic-array = { workspace = true, features = ["serde"] } # Fixed-size arrays +serde = { workspace = true, features = ["derive", "rc"] } # Serialization +bincode = { workspace = true } # Binary encoding + +# Cryptographic algorithms +aes-gcm = { workspace = true, features = ["heapless", "aes", "alloc"]} # AES-GCM encryption +chacha20poly1305 = { workspace = true, features = ["heapless", "alloc"] } # ChaCha20-Poly1305 +kyber-pke = { workspace = true, features=["90s"] } # Post-quantum key exchange +ascon-aead = { workspace = true } # Lightweight AEAD + +# Utility libraries +bytes = { workspace = true } # Byte buffer operations +parking_lot = { workspace = true, features = ["serde"] } # Synchronization primitives +log = { workspace = true } # Logging support +sha3 = { workspace = true } # Hash functions +rand = { workspace = true } # Random number generation +serde-big-array = { workspace = true } # Large array serialization +zeroize = { workspace = true, features = ["zeroize_derive", "alloc", "serde"] } # Secure memory wiping citadel_types = { workspace = true } [target.'cfg(not(target_family = "wasm"))'.dependencies] diff --git a/citadel_pqcrypto/src/bytes_in_place.rs b/citadel_pqcrypto/src/bytes_in_place.rs index 0ffa4f0a1..3e690e86c 100644 --- a/citadel_pqcrypto/src/bytes_in_place.rs +++ b/citadel_pqcrypto/src/bytes_in_place.rs @@ -1,3 +1,38 @@ +//! In-place buffer operations with window-based access control. +//! +//! This module provides utilities for performing memory-efficient and secure +//! in-place buffer operations. It features: +//! +//! - Window-based buffer access control +//! - Zero-copy buffer manipulation +//! - Support for both `Vec` and `BytesMut` types +//! - Memory-safe buffer operations +//! +//! # Examples +//! +//! ``` +//! use citadel_pqcrypto::bytes_in_place::{InPlaceBuffer, EzBuffer}; +//! use aes_gcm::aead::Buffer; +//! +//! // Create a buffer with window-based access +//! let mut data = vec![0u8; 32]; +//! let window = 0..16; +//! let mut buffer = InPlaceBuffer::new(&mut data, window).unwrap(); +//! +//! // Perform operations only on the windowed portion +//! Buffer::extend_from_slice(&mut buffer, &[1, 2, 3]).unwrap(); +//! +//! // Use EzBuffer trait for efficient operations +//! let mut buf = vec![0u8; 32]; +//! let second_half = buf.split_off(16); +//! ``` +//! +//! # Security Considerations +//! +//! - All buffer operations are bounds-checked +//! - Window-based access prevents buffer overflow +//! - In-place operations minimize data copying +//! - Memory is properly managed to prevent leaks use std::ops::Range; use aes_gcm::aead::{Buffer, Error as AesError}; diff --git a/citadel_pqcrypto/src/constructor_opts.rs b/citadel_pqcrypto/src/constructor_opts.rs index 5f63fb43c..883089599 100644 --- a/citadel_pqcrypto/src/constructor_opts.rs +++ b/citadel_pqcrypto/src/constructor_opts.rs @@ -1,3 +1,48 @@ +//! Post-Quantum Cryptography Construction Options +//! +//! This module provides configuration and initialization options for post-quantum +//! cryptographic (PQC) operations in the Citadel Protocol. It includes structures for: +//! +//! - Configuring PQC instance parameters +//! - Managing recursive key derivation chains +//! - Handling shared secrets between participants +//! +//! # Features +//! +//! - Flexible cryptographic parameter configuration +//! - Secure recursive key derivation chain management +//! - Support for multi-round key exchanges +//! - Memory-safe secret handling +//! +//! # Examples +//! +//! ```rust +//! use citadel_pqcrypto::constructor_opts::{ConstructorOpts, RecursiveChain}; +//! use citadel_types::crypto::CryptoParameters; +//! +//! // Create initial constructor options +//! let opts = ConstructorOpts::new_init(Some(CryptoParameters::default())); +//! +//! // Create chain for key derivation +//! let chain = [1u8; 32]; +//! let alice = [2u8; 32]; +//! let bob = [3u8; 32]; +//! let chain = RecursiveChain::new(chain, alice, bob, true).unwrap(); +//! ``` +//! +//! # Security Considerations +//! +//! - All cryptographic parameters should be chosen based on security requirements +//! - Chain values must be protected and never exposed outside secure contexts +//! - Previous shared secrets should be carefully managed and zeroized after use +//! - Memory safety is critical for protecting sensitive key material +//! +//! # Related Components +//! +//! - [`citadel_types::crypto`] - Core cryptographic types and parameters +//! - [`citadel_pqcrypto::wire`] - Wire protocol for PQC operations +//! - [`citadel_pqcrypto::key_store`] - Secure key storage functionality + use citadel_types::crypto::CryptoParameters; use serde::{Deserialize, Serialize}; diff --git a/citadel_pqcrypto/src/encryption.rs b/citadel_pqcrypto/src/encryption.rs index 10cd3f7b0..01e74005e 100644 --- a/citadel_pqcrypto/src/encryption.rs +++ b/citadel_pqcrypto/src/encryption.rs @@ -1,3 +1,41 @@ +//! Post-quantum cryptographic encryption module. +//! +//! This module provides a comprehensive set of encryption primitives that are +//! resistant to both classical and quantum computer attacks. It features: +//! +//! - Authenticated Encryption with Associated Data (AEAD) +//! - Post-quantum key exchange using Kyber +//! - Multiple cipher implementations (AES-GCM, ChaCha20-Poly1305, Ascon80pq) +//! - Local-user-only encryption for endpoint privacy +//! +//! # Examples +//! +//! ```no_run +//! use citadel_pqcrypto::encryption::{AeadModule, aes_impl::AesModule}; +//! use citadel_types::crypto::KemAlgorithm; +//! +//! // Create an AES-GCM cipher +//! let cipher = get_aes_module(); +//! +//! // Encrypt data +//! let nonce = [0u8; 12]; +//! let data = b"Hello, World!"; +//! let ciphertext = cipher.encrypt(&nonce, data).unwrap(); +//! +//! // Decrypt data +//! let plaintext = cipher.decrypt(&nonce, &ciphertext).unwrap(); +//! assert_eq!(&plaintext, data); +//! # fn get_aes_module() -> AesModule { unimplemented!() } +//! ``` +//! +//! # Security Considerations +//! +//! - All sensitive data is automatically zeroized when dropped +//! - Nonces must never be reused with the same key +//! - Associated data is authenticated but not encrypted +//! - Local-user encryption provides endpoint privacy +//! - Post-quantum algorithms are used for future-proofing + use aes_gcm::aead::Buffer; use citadel_types::errors::Error; @@ -52,7 +90,7 @@ pub trait AeadModule: Send + Sync { } } -pub(crate) mod aes_impl { +pub mod aes_impl { use crate::encryption::AeadModule; use crate::PostQuantumMetaKex; use aes_gcm::aead::generic_array::GenericArray; @@ -68,7 +106,7 @@ pub(crate) mod aes_impl { crate::impl_basic_aead_module!(AesModule, citadel_types::crypto::AES_GCM_NONCE_LENGTH_BYTES); } -pub(crate) mod chacha_impl { +pub mod chacha_impl { use crate::encryption::AeadModule; use crate::PostQuantumMetaKex; use aes_gcm::aead::Buffer; @@ -88,7 +126,7 @@ pub(crate) mod chacha_impl { ); } -pub(crate) mod ascon_impl { +pub mod ascon_impl { use crate::encryption::AeadModule; use crate::PostQuantumMetaKex; use aes_gcm::aead::Buffer; @@ -105,7 +143,7 @@ pub(crate) mod ascon_impl { crate::impl_basic_aead_module!(AsconModule, citadel_types::crypto::ASCON_NONCE_LENGTH_BYTES); } -pub(crate) mod kyber_module { +pub mod kyber_module { #[cfg(target_family = "wasm")] use crate::functions::AsSlice; use crate::wire::ScramCryptDictionary; diff --git a/citadel_pqcrypto/src/export.rs b/citadel_pqcrypto/src/export.rs index aec4404c6..13de6a6ad 100644 --- a/citadel_pqcrypto/src/export.rs +++ b/citadel_pqcrypto/src/export.rs @@ -1,3 +1,23 @@ +/// Module for handling key store serialization and deserialization. +/// +/// This module provides functionality for exporting and importing key stores, +/// which contain cryptographic keys and parameters used in the post-quantum +/// cryptographic protocol. It includes: +/// +/// - Secure serialization of key material +/// - Custom serialization implementations +/// - Key conversion utilities +/// - AEAD module generation +/// +/// # Security Considerations +/// +/// - All sensitive key material is automatically zeroized when dropped +/// - Serialized data should be protected at rest (e.g., encrypted) +/// - Key stores should be regenerated periodically +/// - Node types (Alice/Bob) must be preserved during serialization +/// - Cryptographic parameters must match during deserialization +/// - AEAD modules are generated with appropriate key separation +/// - Local-user keys provide additional endpoint privacy use crate::encryption::aes_impl::AesModule; use crate::encryption::ascon_impl::AsconModule; use crate::encryption::chacha_impl::ChaChaModule; @@ -9,6 +29,19 @@ use citadel_types::crypto::{CryptoParameters, EncryptionAlgorithm}; use generic_array::GenericArray; use serde::{Deserialize, Serialize}; +/// Intermediate structure for serializing and deserializing key stores. +/// +/// This structure acts as a bridge between the in-memory representation of +/// a key store and its serialized form. It contains all the necessary +/// cryptographic parameters and keys. +/// +/// # Fields +/// * `alice_key` - Alice's 32-byte key +/// * `bob_key` - Bob's 32-byte key +/// * `kex` - Post-quantum key exchange parameters +/// * `sig` - Optional post-quantum signature parameters +/// * `pq_node` - Node type (Alice or Bob) +/// * `params` - Cryptographic parameters #[derive(Serialize, Deserialize)] struct KeyStoreIntermediate { alice_key: GenericArray, @@ -24,6 +57,10 @@ pub(crate) mod custom_serde { use crate::KeyStore; use serde::{Deserialize, Deserializer, Serialize, Serializer}; + /// Custom serialization implementation for KeyStore. + /// + /// This implementation converts the KeyStore into an intermediate form + /// that can be safely serialized and deserialized. impl Serialize for KeyStore { fn serialize(&self, s: S) -> Result<::Ok, ::Error> where @@ -41,6 +78,10 @@ pub(crate) mod custom_serde { } } + /// Custom deserialization implementation for KeyStore. + /// + /// This implementation converts the intermediate form back into a + /// fully functional KeyStore. impl<'de> Deserialize<'de> for KeyStore { fn deserialize(d: D) -> Result>::Error> where @@ -48,13 +89,17 @@ pub(crate) mod custom_serde { { Ok(KeyStore::from( KeyStoreIntermediate::deserialize(d) - .map_err(|_| serde::de::Error::custom("PQExport Deser err"))? - as KeyStoreIntermediate, + .map_err(|_| serde::de::Error::custom("PQExport Deser err"))?, )) } } } +/// Conversion implementation from KeyStoreIntermediate to KeyStore. +/// +/// This implementation handles the conversion of the intermediate form +/// back into a fully functional KeyStore, including the creation of +/// appropriate AEAD modules based on the cryptographic parameters. impl From for KeyStore { fn from(int: KeyStoreIntermediate) -> Self { let (alice_symmetric_key, bob_symmetric_key) = keys_to_aead_store( @@ -79,8 +124,24 @@ impl From for KeyStore { } } +/// Type alias for a pair of optional AEAD modules. +/// +/// The first module is typically used for standard encryption/decryption, +/// while the second module is used for local-user-only operations. pub type AeadStore = (Option>, Option>); +/// Converts keys and parameters into AEAD modules. +/// +/// # Arguments +/// * `alice` - Alice's 32-byte key +/// * `bob` - Bob's 32-byte key +/// * `kex` - Post-quantum key exchange parameters +/// * `params` - Cryptographic parameters +/// * `sig` - Optional post-quantum signature parameters +/// * `pq_node` - Node type (Alice or Bob) +/// +/// # Returns +/// A pair of optional AEAD modules for standard and local-user-only operations pub(crate) fn keys_to_aead_store( alice: &GenericArray, bob: &GenericArray, @@ -152,6 +213,16 @@ pub(crate) fn keys_to_aead_store( } } +/// Generates symmetric AES modules for encryption/decryption. +/// +/// # Arguments +/// * `pq_node` - Node type (Alice or Bob) +/// * `alice` - Alice's 32-byte key +/// * `bob` - Bob's 32-byte key +/// * `kex` - Post-quantum key exchange parameters +/// +/// # Returns +/// A pair of AES modules for standard and local-user-only operations fn generate_symmetric_aes_module( pq_node: PQNode, alice: &GenericArray, diff --git a/citadel_pqcrypto/src/lib.rs b/citadel_pqcrypto/src/lib.rs index 86a72a522..6281edb77 100644 --- a/citadel_pqcrypto/src/lib.rs +++ b/citadel_pqcrypto/src/lib.rs @@ -1,5 +1,50 @@ #![allow(non_camel_case_types)] #![forbid(unsafe_code)] +//! Post-quantum cryptographic library for secure communication. +//! +//! This crate provides a comprehensive implementation of post-quantum cryptographic +//! primitives and protocols, designed to be resistant to attacks from both classical +//! and quantum computers. It supports various encryption algorithms, key exchange +//! mechanisms, and signature schemes. +//! +//! # Features +//! - Post-quantum key exchange using NIST Round 3 algorithms +//! - Hybrid classical/post-quantum encryption +//! - Authenticated encryption with associated data (AEAD) +//! - Anti-replay attack protection +//! - Zero-knowledge proofs +//! - Secure serialization and deserialization +//! +//! # Security Considerations +//! - All sensitive data is wrapped in `Zeroizing` to ensure secure cleanup +//! - No unsafe code is allowed (enforced by `forbid(unsafe_code)`) +//! - Anti-replay attack protection is enabled by default +//! - Cryptographic operations are constant-time where possible +//! +//! # Examples +//! ``` +//! use citadel_pqcrypto::prelude::*; +//! use citadel_pqcrypto::constructor_opts::ConstructorOpts; +//! use citadel_types::crypto::{KemAlgorithm, SigAlgorithm}; +//! +//! // Define the cryptographic parameters +//! let opts = ConstructorOpts::default(); +//! +//! // Create a new Alice instance +//! let mut alice = PostQuantumContainer::new_alice( +//! opts.clone(), +//! ).unwrap(); +//! +//! // Create a new Bob instance using Alice's parameters +//! let params = alice.generate_alice_to_bob_transfer().unwrap(); +//! let bob = PostQuantumContainer::new_bob(opts, params, &[]).unwrap(); +//! +//! // Complete the key exchange +//! let bob_params = bob.generate_bob_to_alice_transfer().unwrap(); +//! alice.alice_on_receive_ciphertext(bob_params, &[]).unwrap(); +//! +//! // Now both parties can communicate securely +//! ``` use crate::bytes_in_place::{EzBuffer, InPlaceBuffer}; use crate::constructor_opts::{ConstructorOpts, RecursiveChain}; diff --git a/citadel_pqcrypto/src/replay_attack_container.rs b/citadel_pqcrypto/src/replay_attack_container.rs index 0f4256eef..8ded6ecd3 100644 --- a/citadel_pqcrypto/src/replay_attack_container.rs +++ b/citadel_pqcrypto/src/replay_attack_container.rs @@ -1,3 +1,56 @@ +//! Replay Attack Prevention for Secure Communications +//! +//! This module provides protection against replay attacks in cryptographic +//! communications by tracking and validating packet identifiers (PIDs). +//! +//! # Features +//! +//! - Thread-safe packet ID generation and tracking +//! - Circular buffer for efficient history management +//! - Support for out-of-order packet delivery +//! - Protection against delayed replay attacks +//! - Automatic state reset on re-keying +//! +//! # How It Works +//! +//! 1. Each outgoing packet is assigned a unique, monotonically increasing PID +//! 2. PIDs are encrypted along with packet payloads +//! 3. Received PIDs are checked against a history window +//! 4. Duplicate or out-of-window PIDs are rejected as replay attacks +//! +//! # Examples +//! +//! ```rust +//! use citadel_pqcrypto::replay_attack_container::AntiReplayAttackContainer; +//! +//! // Create a new container +//! let container = AntiReplayAttackContainer::default(); +//! +//! // Generate PID for outgoing packet +//! let pid = container.get_next_pid(); +//! +//! // Check incoming packet's PID +//! if container.on_pid_received(pid) { +//! // Process packet - it's valid +//! } else { +//! // Reject packet - potential replay attack +//! } +//! ``` +//! +//! # Security Considerations +//! +//! - Window size affects protection against out-of-order delivery +//! - PIDs must be encrypted to prevent tampering +//! - State should be reset when re-keying occurs +//! - Thread-safety is critical for concurrent access +//! - Memory usage scales with window size +//! +//! # Related Components +//! +//! - [`citadel_pqcrypto::wire`] - Wire protocol implementation +//! - [`citadel_pqcrypto::key_store`] - Key management +//! - [`citadel_types::crypto`] - Cryptographic types + use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use std::collections::HashSet; diff --git a/citadel_pqcrypto/src/wire.rs b/citadel_pqcrypto/src/wire.rs index 65b0be147..9bff76fb2 100644 --- a/citadel_pqcrypto/src/wire.rs +++ b/citadel_pqcrypto/src/wire.rs @@ -1,3 +1,45 @@ +//! Wire Protocol for Post-Quantum Cryptographic Parameter Transfer +//! +//! This module implements the secure wire protocol for parameter transfer between +//! Alice and Bob nodes in the post-quantum cryptographic protocol. It provides: +//! +//! # Features +//! +//! - Secure parameter transfer structures for key exchange +//! - Data scrambling for additional security and obfuscation +//! - Support for both symmetric and asymmetric encryption modes +//! - Memory-safe handling of sensitive cryptographic parameters +//! - Zero-copy buffer operations where possible +//! +//! # Examples +//! +//! ```rust +//! use citadel_pqcrypto::wire::{AliceToBobTransferParameters, ScramCryptDictionary}; +//! +//! // Create a scrambling dictionary for secure data transfer +//! let dict = ScramCryptDictionary::<32>::new().unwrap(); +//! +//! // Example data buffer +//! let mut data = vec![0u8; 32]; +//! +//! // Scramble the data for secure transfer +//! dict.scramble_in_place(&mut data).unwrap(); +//! ``` +//! +//! # Security Considerations +//! +//! - All sensitive parameters are automatically zeroized when dropped +//! - Scrambling provides additional protection against side-channel attacks +//! - Parameter signatures ensure authenticity and integrity +//! - Memory safety is enforced through Rust's ownership system +//! - Buffer operations are bounds-checked to prevent overflows +//! +//! # Related Components +//! +//! - [`citadel_types::crypto`] - Core cryptographic types and parameters +//! - [`citadel_pqcrypto::bytes_in_place`] - In-place buffer operations +//! - [`citadel_pqcrypto::key_store`] - Secure key storage functionality + use aes_gcm::aead::Buffer; use citadel_types::crypto::{KemAlgorithm, SigAlgorithm}; use citadel_types::errors::Error; diff --git a/citadel_proto/src/auth.rs b/citadel_proto/src/auth.rs index 6d73bb8ba..9172d270a 100644 --- a/citadel_proto/src/auth.rs +++ b/citadel_proto/src/auth.rs @@ -1,3 +1,49 @@ +//! Authentication Request Types for Citadel Protocol +//! +//! This module defines the authentication request types and structures used for establishing +//! connections in the Citadel Protocol. It supports both credential-based and passwordless +//! authentication methods. +//! +//! # Features +//! - **Credential Authentication**: Username/password-based authentication +//! - **Passwordless Authentication**: Device-based transient connections +//! - **Secure Credential Handling**: Uses SecBuffer for password protection +//! - **User Identification**: Supports both CID and username-based identification +//! - **Server Address Management**: Handles server connection information +//! +//! # Usage Example +//! ```rust +//! use citadel_proto::auth::AuthenticationRequest; +//! use citadel_types::user::UserIdentifier; +//! use citadel_types::crypto::SecBuffer; +//! use std::net::SocketAddr; +//! use uuid::Uuid; +//! +//! // Credential-based authentication +//! let cred_auth = AuthenticationRequest::credentialed( +//! UserIdentifier::from("username"), +//! SecBuffer::from("password") +//! ); +//! +//! // Passwordless authentication +//! let server_addr: SocketAddr = "127.0.0.1:8080".parse().unwrap(); +//! let uuid = Uuid::new_v4(); +//! let transient_auth = AuthenticationRequest::transient(uuid, server_addr); +//! ``` +//! +//! # Important Notes +//! - Passwords are always handled using SecBuffer for secure memory management +//! - Transient connections use device-specific cryptographic bundles +//! - CID extraction is only available for credential-based authentication +//! - Server addresses are required for passwordless authentication +//! +//! # Related Components +//! - `citadel_types::crypto::SecBuffer`: Secure credential storage +//! - `citadel_types::user::UserIdentifier`: User identification types +//! - `proto::packet_processor::connect_packet`: Connection handling +//! - `proto::validation`: Authentication validation +//! + use citadel_types::crypto::SecBuffer; use citadel_types::user::UserIdentifier; use serde::{Deserialize, Serialize}; diff --git a/citadel_proto/src/constants.rs b/citadel_proto/src/constants.rs index 93566f043..81da28e8a 100644 --- a/citadel_proto/src/constants.rs +++ b/citadel_proto/src/constants.rs @@ -1,3 +1,55 @@ +//! Protocol Constants for Citadel Protocol +//! +//! This module defines the core constants used throughout the Citadel Protocol implementation. +//! These constants control protocol behavior, networking parameters, timing, and security settings. +//! +//! # Features +//! - **Version Management**: Protocol version control using semantic versioning +//! - **Network Parameters**: MTU sizes, header lengths, and payload limits +//! - **Timing Constants**: Keep-alive intervals, timeouts, and update frequencies +//! - **Buffer Settings**: Codec and group size limitations +//! - **Port Configuration**: Network port ranges and defaults +//! - **Security Levels**: Update frequency bases for different security levels +//! +//! # Usage Example +//! ```rust +//! use citadel_proto::constants::{ +//! PROTOCOL_VERSION, +//! MTU, +//! MAX_PAYLOAD_SIZE_IPV4, +//! MAX_PAYLOAD_SIZE_IPV6, +//! PRIMARY_PORT +//! }; +//! +//! // Check protocol version compatibility +//! let version = *PROTOCOL_VERSION; +//! +//! // Calculate maximum payload size based on IP version +//! let max_payload = if uses_ipv6 { +//! MAX_PAYLOAD_SIZE_IPV6 +//! } else { +//! MAX_PAYLOAD_SIZE_IPV4 +//! }; +//! +//! // Ensure payload fits within MTU +//! assert!(payload.len() <= max_payload); +//! assert!(max_payload < MTU); +//! ``` +//! +//! # Important Notes +//! - Protocol version uses semantic versioning (major.minor.patch) +//! - All timing constants are in nanoseconds unless specified +//! - MTU is set for IPv6 compatibility (1280 bytes) +//! - Buffer sizes are optimized for typical use cases +//! - Security level update frequencies are configurable +//! +//! # Related Components +//! - `proto::packet`: Uses header and payload size constants +//! - `proto::codec`: Uses buffer capacity constants +//! - `proto::validation`: Uses timing constants +//! - `proto::state_subcontainers`: Uses security level constants +//! + use crate::proto::packet::HdpHeader; use citadel_types::proto::UdpMode; use embedded_semver::prelude::*; diff --git a/citadel_proto/src/error.rs b/citadel_proto/src/error.rs index 918286093..f303b03da 100644 --- a/citadel_proto/src/error.rs +++ b/citadel_proto/src/error.rs @@ -1,3 +1,36 @@ +//! Error Types and Handling for Citadel Protocol +//! +//! This module provides the core error handling functionality for the Citadel Protocol, +//! primarily through the `NetworkError` enum. It handles various error scenarios that +//! can occur during network operations, packet processing, and inter-node communication. +//! +//! # Features +//! - Comprehensive error type (`NetworkError`) for network-related operations +//! - Error conversion implementations from common error types +//! - Detailed error messages and debugging information +//! - Support for timeout, socket, packet, and internal error scenarios +//! +//! # Usage +//! ```rust +//! use citadel_proto::NetworkError; +//! +//! // Create a generic error with a message +//! let error = NetworkError::msg("Connection failed"); +//! +//! // Convert error to string for logging or display +//! let error_string = error.into_string(); +//! ``` +//! +//! # Important Notes +//! - All error variants include descriptive messages for debugging +//! - Implements standard error traits (`Error`, `Debug`, `Display`) +//! - Provides automatic conversion from common error types like `std::io::Error` and `CryptError` +//! +//! # Related Components +//! - `citadel_crypt::misc::CryptError` - For cryptography-related errors +//! - `citadel_user::misc::AccountError` - For user account-related errors +//! - `NodeRequest` - For node communication errors + use crate::prelude::NodeRequest; use citadel_crypt::misc::CryptError; use citadel_io::tokio::sync::mpsc::error::SendError; diff --git a/citadel_proto/src/functional.rs b/citadel_proto/src/functional.rs index d991cf910..6ed9b81c1 100644 --- a/citadel_proto/src/functional.rs +++ b/citadel_proto/src/functional.rs @@ -1,3 +1,47 @@ +//! Functional Programming Utilities for Citadel Protocol +//! +//! This module provides functional programming utilities and extensions to enhance +//! code readability and maintainability. It implements monadic-style operations +//! and conditional chaining for Rust's native types. +//! +//! # Features +//! +//! - Method chaining with `Then` trait +//! - Conditional branching with `IfEq` and `IfTrue` +//! - Tuple mapping with `PairMap` +//! - Lazy evaluation support +//! - Type-safe conditional operations +//! +//! # Usage Example +//! +//! ```rust +//! use citadel_proto::functional::Then; +//! use citadel_proto::functional::IfTrueConditional; +//! +//! // Method chaining +//! let result = 42.then(|x| x * 2) +//! .then(|x| x.to_string()); +//! +//! // Conditional operations +//! let value = true.if_true(1) +//! .if_false(0); +//! assert_eq!(value, 1); +//! ``` +//! +//! # Important Notes +//! +//! - All operations are zero-cost abstractions +//! - Implements `FnOnce` for lazy evaluation +//! - Preserves type safety through generics +//! - Supports both eager and lazy evaluation +//! +//! # Related Components +//! +//! - Used throughout the codebase for functional patterns +//! - Integrates with error handling and option types +//! - Supports protocol state management +//! - Enhances packet processing readability + pub trait Then U> where Self: Sized, diff --git a/citadel_proto/src/inner_arg.rs b/citadel_proto/src/inner_arg.rs index 3cd1677f0..c176199c5 100644 --- a/citadel_proto/src/inner_arg.rs +++ b/citadel_proto/src/inner_arg.rs @@ -1,3 +1,51 @@ +//! Inner Parameter Type System for Citadel Protocol +//! +//! This module provides a type-safe way to handle inner parameter references in the +//! Citadel Protocol. It implements wrapper types that enforce proper dereferencing +//! behavior and type safety for both mutable and immutable references. +//! +//! # Features +//! +//! - Type-safe parameter wrapping +//! - Mutable and immutable reference support +//! - Automatic dereferencing behavior +//! - Zero-cost abstractions +//! - Generic over target types +//! +//! # Usage Example +//! +//! ```rust +//! use citadel_proto::inner_arg::{InnerParameter, InnerParameterMut}; +//! use std::ops::{Deref, DerefMut}; +//! +//! struct Wrapper(T); +//! impl Deref for Wrapper { +//! type Target = T; +//! fn deref(&self) -> &T { &self.0 } +//! } +//! impl DerefMut for Wrapper { +//! fn deref_mut(&mut self) -> &mut T { &mut self.0 } +//! } +//! +//! let mut value = Wrapper(42); +//! let param = InnerParameterMut::from(&mut value); +//! assert_eq!(*param, 42); +//! ``` +//! +//! # Important Notes +//! +//! - Zero runtime overhead +//! - Preserves mutability constraints +//! - Uses PhantomData for type safety +//! - Implements standard traits (From, Deref) +//! +//! # Related Components +//! +//! - Used in packet processing for safe references +//! - Supports protocol state management +//! - Integrates with validation system +//! - Used in cryptographic operations + use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; diff --git a/citadel_proto/src/kernel/kernel_communicator.rs b/citadel_proto/src/kernel/kernel_communicator.rs index 1255ab7f7..dcd24e6d9 100644 --- a/citadel_proto/src/kernel/kernel_communicator.rs +++ b/citadel_proto/src/kernel/kernel_communicator.rs @@ -1,3 +1,31 @@ +//! Kernel Communication Handler +//! +//! This module implements the communication layer between kernel components, +//! managing message passing, callbacks, and event handling within the Citadel Protocol. +//! +//! # Features +//! +//! - Asynchronous message handling +//! - Event callback management +//! - Channel-based communication +//! - Error propagation +//! - Resource cleanup +//! +//! # Important Notes +//! +//! - Uses Tokio channels for communication +//! - Maintains thread safety for callbacks +//! - Handles resource cleanup on drop +//! - Supports both sync and async callbacks +//! - Manages message ordering guarantees +//! +//! # Related Components +//! +//! - `kernel_executor.rs`: Task execution +//! - `kernel_trait.rs`: Core interfaces +//! - `mod.rs`: Module coordination +//! - `error.rs`: Error handling + use crate::error::NetworkError; use crate::proto::node_result::NodeResult; use crate::proto::remote::Ticket; diff --git a/citadel_proto/src/kernel/kernel_executor.rs b/citadel_proto/src/kernel/kernel_executor.rs index cf246d796..7e06ca834 100644 --- a/citadel_proto/src/kernel/kernel_executor.rs +++ b/citadel_proto/src/kernel/kernel_executor.rs @@ -1,3 +1,43 @@ +//! Kernel Executor Implementation +//! +//! This module provides the runtime execution environment for the Citadel Protocol kernel. +//! It manages concurrent operations, handles protocol message routing, and coordinates +//! between different components of the system. +//! +//! # Features +//! +//! - Asynchronous task execution +//! - Configurable concurrency limits +//! - Message routing and dispatch +//! - Resource cleanup management +//! - Session state tracking +//! - Protocol type handling +//! +//! # Usage Example +//! +//! ```rust +//! use citadel_proto::kernel::KernelExecutorSettings; +//! +//! // Configure execution settings +//! let settings = KernelExecutorSettings::default() +//! .with_max_concurrency(Some(10)); +//! ``` +//! +//! # Important Notes +//! +//! - Executor runs on Tokio runtime +//! - Handles both client and server operations +//! - Maintains thread safety through message passing +//! - Performs automatic resource cleanup +//! - Manages protocol version compatibility +//! +//! # Related Components +//! +//! - `kernel_trait.rs`: Core trait definitions +//! - `kernel_communicator.rs`: Message handling +//! - `mod.rs`: Module coordination +//! - `AccountManager`: User session management + use std::pin::Pin; use citadel_io::tokio::runtime::Handle; @@ -88,7 +128,7 @@ impl KernelExecutor { let shutdown_alerter_rx = self.shutdown_alerter_rx.take().unwrap(); let callback_handler = self.callback_handler.take().unwrap(); - let (_rt, hdp_server, _localset_opt) = self.context.take().unwrap(); + let (_rt, citadel_server, _localset_opt) = self.context.take().unwrap(); log::trace!(target: "citadel", "KernelExecutor::execute is now executing ..."); @@ -104,21 +144,21 @@ impl KernelExecutor { #[cfg(feature = "multi-threaded")] { use crate::proto::misc::panic_future::ExplicitPanicFuture; - let hdp_server_future = ExplicitPanicFuture::new(_rt.spawn(hdp_server)); + let citadel_server_future = ExplicitPanicFuture::new(_rt.spawn(citadel_server)); citadel_io::tokio::select! { ret0 = kernel_future => ret0, - ret1 = hdp_server_future => ret1.map_err(|err| NetworkError::Generic(err.to_string()))? + ret1 = citadel_server_future => ret1.map_err(|err| NetworkError::Generic(err.to_string()))? } } #[cfg(not(feature = "multi-threaded"))] { let localset = _localset_opt.unwrap(); - //let _ = localset.spawn_local(hdp_server); - let hdp_server_future = localset.run_until(hdp_server); - //let hdp_server_future = localset; + //let _ = localset.spawn_local(citadel_server); + let citadel_server_future = localset.run_until(citadel_server); + //let citadel_server_future = localset; citadel_io::tokio::select! { ret0 = kernel_future => ret0, - ret1 = hdp_server_future => ret1 + ret1 = citadel_server_future => ret1 } } }; @@ -131,16 +171,16 @@ impl KernelExecutor { async fn kernel_inner_loop( kernel: &mut K, mut server_to_kernel_rx: UnboundedReceiver, - hdp_server_remote: NodeRemote, + citadel_server_remote: NodeRemote, shutdown: citadel_io::tokio::sync::oneshot::Receiver<()>, callback_handler: KernelAsyncCallbackHandler, kernel_settings: KernelExecutorSettings, ) -> Result<(), NetworkError> { - let hdp_server_remote = &hdp_server_remote; + let citadel_server_remote = &citadel_server_remote; let callback_handler = &callback_handler; log::trace!(target: "citadel", "Kernel multithreaded environment executed ..."); // Load the remote into the kernel - kernel.load_remote(hdp_server_remote.clone())?; + kernel.load_remote(citadel_server_remote.clone())?; let (ref clean_stop_tx, mut clean_stop_rx) = citadel_io::tokio::sync::mpsc::channel::<()>(1); @@ -169,7 +209,7 @@ impl KernelExecutor { if let Err(err) = kernel_ref.on_node_event_received(message).await { log::error!(target: "citadel", "Kernel threw an error: {:?}. Will end", &err); // calling this will cause server_to_kernel_rx to receive a shutdown message - hdp_server_remote.clone().shutdown().await?; + citadel_server_remote.clone().shutdown().await?; Err(err) } else { Ok(()) diff --git a/citadel_proto/src/kernel/kernel_trait.rs b/citadel_proto/src/kernel/kernel_trait.rs index 4e90571ff..85ff0b17c 100644 --- a/citadel_proto/src/kernel/kernel_trait.rs +++ b/citadel_proto/src/kernel/kernel_trait.rs @@ -1,3 +1,31 @@ +//! Kernel Trait Definitions for Citadel Protocol +//! +//! This module defines the core trait interfaces that all kernel implementations must satisfy. +//! It provides the contract between the high-level protocol operations and the underlying +//! network implementations. +//! +//! # Features +//! +//! - Abstract network kernel interface definition +//! - Protocol-agnostic message handling +//! - Asynchronous operation support +//! - Session management traits +//! - Error handling specifications +//! +//! # Important Notes +//! +//! - All kernel implementations must be thread-safe +//! - Methods are asynchronous and return Futures +//! - Implementations must handle connection state +//! - Error types must implement std::error::Error +//! +//! # Related Components +//! +//! - `kernel/mod.rs`: Main kernel module implementation +//! - `kernel_executor.rs`: Execution runtime +//! - `kernel_communicator.rs`: Communication handling +//! - `error.rs`: Protocol error definitions + use async_trait::async_trait; use crate::error::NetworkError; @@ -5,7 +33,7 @@ use crate::proto::node_result::NodeResult; use crate::proto::remote::NodeRemote; use auto_impl::auto_impl; -/// The [NetKernel] is the thread-safe interface between the single-threaded OR multi-threaded async +/// The [`NetKernel`] is the thread-safe interface between the single-threaded OR multi-threaded async /// protocol and your network application #[async_trait] #[auto_impl(Box, &mut)] diff --git a/citadel_proto/src/kernel/mod.rs b/citadel_proto/src/kernel/mod.rs index 21a37269f..2169625d3 100644 --- a/citadel_proto/src/kernel/mod.rs +++ b/citadel_proto/src/kernel/mod.rs @@ -1,3 +1,56 @@ +//! Kernel Module for Citadel Protocol +//! +//! This module implements the core kernel functionality that bridges the high-level API +//! with the low-level protocol implementation. It provides runtime management, +//! asynchronous execution, and event handling for the Citadel Protocol. +//! +//! # Features +//! +//! - Asynchronous runtime management +//! - Event-driven architecture +//! - Multi-threaded execution support +//! - Configurable concurrency control +//! - Account management integration +//! - Protocol type abstraction +//! - Session security management +//! +//! # Usage Example +//! +//! ```rust +//! use citadel_proto::kernel::{KernelExecutor, KernelExecutorSettings}; +//! use citadel_proto::kernel::kernel_trait::NetKernel; +//! use citadel_wire::hypernode_type::NodeType; +//! +//! // Configure kernel settings +//! let settings = KernelExecutorSettings::default() +//! .with_max_concurrency(Some(10)); +//! +//! // Create kernel executor with settings +//! let executor = KernelExecutor::new( +//! runtime.handle(), +//! NodeType::default(), +//! account_manager, +//! kernel, +//! settings +//! ); +//! ``` +//! +//! # Important Notes +//! +//! - Single-threaded lower level, multi-threaded upper level +//! - Configurable concurrency limits for event handling +//! - Handles both client and server node types +//! - Integrates with account management system +//! - Supports multiple underlying protocols +//! +//! # Related Components +//! +//! - `kernel_communicator`: Asynchronous callback handling +//! - `kernel_executor`: Multi-threaded runtime management +//! - `kernel_trait`: High-level API interface +//! - `HdpServer`: Low-level protocol implementation +//! - `AccountManager`: User account management + use citadel_io::tokio::macros::support::Future; use citadel_io::tokio::runtime::Handle; use citadel_user::account_manager::AccountManager; diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index 6d7d2a67a..13365eef9 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -1,6 +1,73 @@ +//! # Citadel Protocol Core Implementation +//! +//! The `citadel_proto` crate provides the core implementation of the Citadel Protocol, a secure and +//! efficient networking protocol designed for peer-to-peer communication with post-quantum cryptographic +//! security guarantees. +//! +//! ## Key Features +//! +//! - **Post-Quantum Security**: Built with resistance against both classical and quantum attacks +//! - **Peer-to-Peer Communication**: Direct peer connections with NAT traversal capabilities +//! - **Session Management**: Robust session handling with automatic reconnection and state management +//! - **Secure File Transfer**: Built-in support for secure file transfers with configurable security levels +//! - **Group Communication**: Support for secure group messaging and broadcasts +//! - **UDP/TCP Support**: Flexible transport layer with support for both UDP and TCP connections +//! - **Zero-Copy Design**: Optimized for performance with minimal memory overhead +//! - **Async/Await Support**: Built on modern Rust async primitives +//! - **Cross-Platform**: Works on all major platforms including mobile +//! +//! ## Architecture +//! +//! The protocol is built around several key components: +//! +//! - **Kernel Layer**: Core event loop and task scheduling via [`kernel`] module +//! - **Protocol Layer**: Protocol implementation and packet handling in [`proto`] module +//! - **Session Management**: Connection lifecycle via [`CitadelSession`] +//! - **Security Layer**: Cryptographic operations and security settings +//! - **Network Layer**: Transport abstraction and connection management +//! +//! ## Module Organization +//! +//! - [`kernel`]: Core event loop and task scheduling +//! - [`proto`]: Protocol implementation and packet handling +//! - [`prelude`]: Common imports for working with the crate +//! - [`error`]: Error types and handling +//! +//! ## Security Considerations +//! +//! - All sensitive data is automatically zeroed when dropped +//! - The crate forbids unsafe code by default +//! - Implements defense-in-depth with multiple security layers +//! - Uses post-quantum cryptographic primitives +//! - Provides configurable security levels +//! +//! ## Performance +//! +//! The crate is designed for high performance with: +//! - Zero-copy packet handling where possible +//! - Efficient memory management +//! - Optimized async/await implementations +//! - Minimal allocations in hot paths +//! +//! ## Examples +//! +//! See the `examples/` directory in the repository for complete usage examples. +//! For quick start guides and tutorials, visit the official documentation. +//! +//! ## Feature Flags +//! +//! - `multi-threaded`: Enables multi-threaded support (default) +//! - `compression`: Enables packet compression +//! - `file-transfer`: Enables secure file transfer support +//! - `group-chat`: Enables group communication features +//! +//! ## Version Compatibility +//! +//! This crate maintains semantic versioning and documents breaking changes +//! in the changelog. It is recommended to specify exact version requirements +//! in your `Cargo.toml`. #![doc(html_no_source)] #![forbid(unsafe_code)] -//! Core networking components for SatoriNET #![deny( trivial_numeric_casts, unused_extern_crates, diff --git a/citadel_proto/src/proto/codec.rs b/citadel_proto/src/proto/codec.rs index 8a52449c4..500a61eee 100644 --- a/citadel_proto/src/proto/codec.rs +++ b/citadel_proto/src/proto/codec.rs @@ -1,3 +1,44 @@ +//! Bytes Codec Implementation for Citadel Protocol +//! +//! This module provides a basic bytes codec implementation for raw data transmission in the Citadel Protocol. +//! The codec handles efficient encoding and decoding of raw bytes with configurable buffer capacity. +//! +//! # Features +//! - Configurable buffer capacity for memory efficiency +//! - Zero-copy split operations for optimal performance +//! - Automatic buffer resizing when capacity is insufficient +//! - Implementation of tokio_util's Encoder and Decoder traits +//! +//! # Usage +//! ```rust +//! use citadel_proto::proto::codec::BytesCodec; +//! use bytes::{Bytes, BytesMut}; +//! use citadel_io::tokio_util::codec::{Decoder, Encoder}; +//! +//! // Create a new codec with specified buffer capacity +//! let mut codec = BytesCodec::new(8192); +//! let mut buf = BytesMut::new(); +//! +//! // Encode some bytes +//! let data = Bytes::from("Hello"); +//! codec.encode(data, &mut buf).unwrap(); +//! +//! // Decode the bytes +//! if let Ok(Some(decoded)) = codec.decode(&mut buf) { +//! assert_eq!(decoded, "Hello"); +//! } +//! ``` +//! +//! # Important Notes +//! - The codec maintains a minimum buffer size defined by `CODEC_MIN_BUFFER` +//! - Buffer capacity is automatically increased if it falls below the minimum +//! - The codec performs best with buffer sizes optimized for your use case +//! +//! # Related Components +//! - `citadel_io::tokio_util::codec` - Provides the core codec traits +//! - `crate::constants` - Defines important buffer size constants +//! - Network transport layers that utilize this codec for raw byte transmission + use std::io; use bytes::BufMut; diff --git a/citadel_proto/src/proto/endpoint_crypto_accessor.rs b/citadel_proto/src/proto/endpoint_crypto_accessor.rs index 3e6a1a183..8ad7df408 100644 --- a/citadel_proto/src/proto/endpoint_crypto_accessor.rs +++ b/citadel_proto/src/proto/endpoint_crypto_accessor.rs @@ -1,3 +1,25 @@ +//! Endpoint Cryptographic Access Management +//! +//! This module provides secure access to cryptographic state for both peer-to-peer (P2P) +//! and client-to-server (C2S) communication channels within the Citadel Protocol. +//! +//! # Features +//! - Safe access to cryptographic state through controlled borrowing +//! - Support for both P2P and C2S communication modes +//! - Version-aware cryptographic state management +//! - Thread-safe state container access +//! - Automatic error handling for missing or invalid states +//! +//! # Important Notes +//! - All cryptographic operations are performed using post-quantum secure algorithms +//! - State containers are protected against concurrent access +//! - Version control ensures forward compatibility +//! +//! # Related Components +//! - `StateContainer`: Manages the underlying cryptographic state +//! - `StackedRatchet`: Provides the core cryptographic operations +//! - `NetworkError`: Error handling for cryptographic operations + #![allow(dead_code)] use crate::error::NetworkError; use crate::inner_arg::ExpectedInnerTargetMut; diff --git a/citadel_proto/src/proto/misc/clean_shutdown.rs b/citadel_proto/src/proto/misc/clean_shutdown.rs index 707ef7447..76353e3d9 100644 --- a/citadel_proto/src/proto/misc/clean_shutdown.rs +++ b/citadel_proto/src/proto/misc/clean_shutdown.rs @@ -1,3 +1,30 @@ +//! Clean Shutdown Handler +//! +//! This module provides utilities for gracefully shutting down components in the Citadel Protocol. +//! It ensures proper cleanup of resources and notifies dependent components during shutdown. +//! +//! # Features +//! +//! - Asynchronous shutdown coordination +//! - Resource cleanup management +//! - Shutdown notification broadcasting +//! - Timeout-based forced shutdown +//! - Shutdown state tracking +//! +//! # Important Notes +//! +//! - All shutdown operations are asynchronous +//! - Components must respond to shutdown signals promptly +//! - Forced shutdown occurs after timeout +//! - Thread-safe shutdown coordination +//! +//! # Related Components +//! +//! - `kernel_executor.rs`: Kernel shutdown handling +//! - `session.rs`: Session cleanup +//! - `node.rs`: Node shutdown coordination +//! - `lock_holder.rs`: Resource locking + use crate::macros::ContextRequirements; use citadel_io::tokio::io::{AsyncRead, AsyncWrite}; use citadel_io::tokio_util::codec::{Decoder, Encoder, Framed}; diff --git a/citadel_proto/src/proto/misc/dual_cell.rs b/citadel_proto/src/proto/misc/dual_cell.rs index 7c621c0bb..470bd9d56 100644 --- a/citadel_proto/src/proto/misc/dual_cell.rs +++ b/citadel_proto/src/proto/misc/dual_cell.rs @@ -1,3 +1,29 @@ +//! Dual-Mode Cell Implementation +//! +//! This module provides a thread-safe cell type that can operate in both single-threaded +//! and multi-threaded contexts. It automatically selects the appropriate implementation +//! based on compile-time feature flags. +//! +//! # Features +//! +//! - Compile-time thread safety selection +//! - Interior mutability +//! - Zero-cost abstraction +//! - Automatic feature detection +//! +//! # Important Notes +//! +//! - Uses std::cell::Cell in single-threaded mode +//! - Uses atomic types in multi-threaded mode +//! - No runtime overhead for thread safety checks +//! - Requires Send + Sync for multi-threaded use +//! +//! # Related Components +//! +//! - `dual_rwlock.rs`: Read-write lock implementation +//! - `dual_late_init.rs`: Late initialization +//! - `lock_holder.rs`: Resource locking + use crate::macros::ContextRequirements; use bytemuck::NoUninit; diff --git a/citadel_proto/src/proto/misc/dual_late_init.rs b/citadel_proto/src/proto/misc/dual_late_init.rs index 5301349e5..ad3306ee4 100644 --- a/citadel_proto/src/proto/misc/dual_late_init.rs +++ b/citadel_proto/src/proto/misc/dual_late_init.rs @@ -1,3 +1,29 @@ +//! Late Initialization Container +//! +//! This module provides a container type for values that must be initialized after +//! construction. It ensures thread safety and proper initialization semantics in +//! both single-threaded and multi-threaded contexts. +//! +//! # Features +//! +//! - Safe late initialization +//! - Thread-safe value access +//! - Initialization state tracking +//! - Panic-free value access +//! +//! # Important Notes +//! +//! - Values must be initialized before access +//! - Thread-safe in multi-threaded mode +//! - Panics on double initialization +//! - Zero overhead in single-threaded mode +//! +//! # Related Components +//! +//! - `dual_cell.rs`: Thread-safe cell +//! - `dual_rwlock.rs`: Read-write locking +//! - `lock_holder.rs`: Resource locking + use crate::macros::ContextRequirements; use std::ops::Deref; diff --git a/citadel_proto/src/proto/misc/dual_rwlock.rs b/citadel_proto/src/proto/misc/dual_rwlock.rs index 88a3e72af..909da36e4 100644 --- a/citadel_proto/src/proto/misc/dual_rwlock.rs +++ b/citadel_proto/src/proto/misc/dual_rwlock.rs @@ -1,3 +1,30 @@ +//! Dual-Mode Read-Write Lock +//! +//! This module provides a read-write lock implementation that can operate in both +//! single-threaded and multi-threaded contexts. It automatically selects the +//! appropriate locking mechanism based on compile-time feature flags. +//! +//! # Features +//! +//! - Compile-time thread safety selection +//! - Multiple reader support +//! - Exclusive writer access +//! - Deadlock prevention +//! - Zero-cost abstraction +//! +//! # Important Notes +//! +//! - Uses RefCell in single-threaded mode +//! - Uses RwLock in multi-threaded mode +//! - No runtime overhead for thread safety +//! - Requires Send + Sync for multi-threaded use +//! +//! # Related Components +//! +//! - `dual_cell.rs`: Thread-safe cell +//! - `dual_late_init.rs`: Late initialization +//! - `lock_holder.rs`: Resource locking + use crate::macros::{ContextRequirements, WeakBorrowType}; use std::ops::Deref; diff --git a/citadel_proto/src/proto/misc/lock_holder.rs b/citadel_proto/src/proto/misc/lock_holder.rs index 2135b6d61..b5bfe4c1d 100644 --- a/citadel_proto/src/proto/misc/lock_holder.rs +++ b/citadel_proto/src/proto/misc/lock_holder.rs @@ -1,3 +1,31 @@ +//! Lock Holder Implementation +//! +//! This module provides a safe way to hold and manage locks across asynchronous +//! boundaries. It ensures proper lock acquisition and release while preventing +//! deadlocks and maintaining thread safety. +//! +//! # Features +//! +//! - Safe async lock management +//! - Deadlock prevention +//! - RAII-style lock handling +//! - Automatic lock cleanup +//! - Lock state tracking +//! +//! # Important Notes +//! +//! - Locks are automatically released on drop +//! - Supports both sync and async contexts +//! - Thread-safe lock management +//! - Prevents lock leakage +//! +//! # Related Components +//! +//! - `dual_rwlock.rs`: Read-write locking +//! - `dual_cell.rs`: Thread-safe cells +//! - `clean_shutdown.rs`: Resource cleanup +//! - `dual_late_init.rs`: Late initialization + #![allow(dead_code)] use std::marker::PhantomData; diff --git a/citadel_proto/src/proto/misc/mod.rs b/citadel_proto/src/proto/misc/mod.rs index 78567c25c..5c2f4a9c7 100644 --- a/citadel_proto/src/proto/misc/mod.rs +++ b/citadel_proto/src/proto/misc/mod.rs @@ -1,3 +1,31 @@ +//! Miscellaneous Utilities for Citadel Protocol +//! +//! This module provides various utility types and functions used throughout the +//! Citadel Protocol implementation. It includes thread-safe containers, network +//! utilities, and resource management tools. +//! +//! # Features +//! +//! - Thread-safe data structures +//! - Network utility functions +//! - Resource management tools +//! - Async utilities +//! - Protocol type definitions +//! +//! # Important Notes +//! +//! - Supports both single and multi-threaded operation +//! - Provides zero-cost abstractions +//! - Ensures proper resource cleanup +//! - Maintains thread safety guarantees +//! +//! # Related Components +//! +//! - `proto/node.rs`: Network node implementation +//! - `proto/session.rs`: Session management +//! - `kernel/mod.rs`: Kernel implementation +//! - `error.rs`: Error handling + use crate::error::NetworkError; use bytes::Bytes; use citadel_io::tokio::io::{AsyncRead, AsyncWrite}; diff --git a/citadel_proto/src/proto/misc/net.rs b/citadel_proto/src/proto/misc/net.rs index 2b10bed50..b5fe9c1e4 100644 --- a/citadel_proto/src/proto/misc/net.rs +++ b/citadel_proto/src/proto/misc/net.rs @@ -1,3 +1,32 @@ +//! Network Utilities for Citadel Protocol +//! +//! This module provides core networking utilities and types used throughout the +//! Citadel Protocol. It handles network connections, address resolution, and +//! protocol-specific networking operations. +//! +//! # Features +//! +//! - Socket management +//! - Address resolution +//! - Connection handling +//! - Protocol negotiation +//! - Network error handling +//! - Transport selection +//! +//! # Important Notes +//! +//! - Supports TCP and UDP transports +//! - Handles IPv4 and IPv6 addresses +//! - Implements connection timeouts +//! - Provides error recovery +//! +//! # Related Components +//! +//! - `underlying_proto.rs`: Protocol implementation +//! - `udp_internal_interface.rs`: UDP handling +//! - `session.rs`: Session management +//! - `node.rs`: Node implementation + use crate::error::NetworkError; use crate::macros::{ContextRequirements, SyncContextRequirements}; use crate::proto::misc::clean_shutdown::{ @@ -432,19 +461,7 @@ impl QuicListener { let acceptor_stream = async_stream::stream! { loop { - let res = server.next_connection().await.map_err(|err| generic_error(err.to_string())); - match res { - Err(res) => { - if res.to_string().contains(citadel_wire::quic::QUIC_LISTENER_DIED) { - // terminate by yielding error - yield Err(res) - } else { - log::warn!(target: "citadel", "QUIC accept err: {:?}", res); - } - } - - res => yield res - } + yield server.next_connection().await.map_err(|err| generic_error(err.to_string())); } }; @@ -471,9 +488,6 @@ impl QuicListener { } } -trait StreamOutputImpl: Future> + SyncContextRequirements {} -impl> + SyncContextRequirements> StreamOutputImpl for T {} - impl Stream for QuicListener { type Item = std::io::Result<(Connection, SendStream, RecvStream, SocketAddr, Endpoint)>; @@ -591,3 +605,6 @@ impl Stream for DualListener { Pin::new(recv).poll_recv(cx) } } + +trait StreamOutputImpl: Future> + SyncContextRequirements {} +impl> + SyncContextRequirements> StreamOutputImpl for T {} diff --git a/citadel_proto/src/proto/misc/ordered_channel.rs b/citadel_proto/src/proto/misc/ordered_channel.rs index 319c2daaf..f6b14b397 100644 --- a/citadel_proto/src/proto/misc/ordered_channel.rs +++ b/citadel_proto/src/proto/misc/ordered_channel.rs @@ -1,3 +1,32 @@ +//! Ordered Channel Implementation +//! +//! This module provides an implementation of a channel that maintains message ordering +//! guarantees. It ensures that messages are delivered in the same order they were sent, +//! which is crucial for protocol operations. +//! +//! # Features +//! +//! - Strict message ordering +//! - Asynchronous operation +//! - Backpressure support +//! - Error propagation +//! - Channel state tracking +//! +//! # Important Notes +//! +//! - Messages are delivered in order +//! - Supports multiple producers +//! - Single consumer design +//! - Thread-safe operation +//! - Handles channel closure +//! +//! # Related Components +//! +//! - `kernel_communicator.rs`: Message handling +//! - `session.rs`: Session management +//! - `clean_shutdown.rs`: Resource cleanup +//! - `net.rs`: Network operations + use crate::error::NetworkError; use crate::proto::outbound_sender::UnboundedSender; use citadel_types::crypto::SecBuffer; diff --git a/citadel_proto/src/proto/misc/panic_future.rs b/citadel_proto/src/proto/misc/panic_future.rs index a7e975819..c1d4913f3 100644 --- a/citadel_proto/src/proto/misc/panic_future.rs +++ b/citadel_proto/src/proto/misc/panic_future.rs @@ -1,3 +1,30 @@ +//! Panic-Safe Future Implementation +//! +//! This module provides a future wrapper that safely handles panics in async code. +//! It ensures that panics are caught and converted into proper error results +//! rather than propagating and potentially crashing the entire process. +//! +//! # Features +//! +//! - Panic catching +//! - Error conversion +//! - Future wrapping +//! - Safe unwinding +//! +//! # Important Notes +//! +//! - Converts panics to errors +//! - Thread-safe operation +//! - Zero overhead when no panic +//! - Preserves error types +//! +//! # Related Components +//! +//! - `kernel_executor.rs`: Task execution +//! - `clean_shutdown.rs`: Resource cleanup +//! - `error.rs`: Error handling +//! - `ordered_channel.rs`: Message handling + use citadel_io::tokio::macros::support::{Pin, Poll}; use citadel_io::tokio::task::{JoinError, JoinHandle}; use futures::task::Context; diff --git a/citadel_proto/src/proto/misc/session_security_settings.rs b/citadel_proto/src/proto/misc/session_security_settings.rs index 40e84d17c..81acf3de3 100644 --- a/citadel_proto/src/proto/misc/session_security_settings.rs +++ b/citadel_proto/src/proto/misc/session_security_settings.rs @@ -1,3 +1,31 @@ +//! Session Security Settings +//! +//! This module defines the security configuration options for Citadel Protocol sessions. +//! It provides settings for encryption, authentication, and other security-related +//! parameters that control how sessions operate. +//! +//! # Features +//! +//! - Encryption configuration +//! - Authentication settings +//! - Key management options +//! - Security level control +//! - Protocol version settings +//! +//! # Important Notes +//! +//! - Settings affect session security +//! - Some options require specific features +//! - Default settings are secure +//! - Settings are immutable after session start +//! +//! # Related Components +//! +//! - `session.rs`: Session management +//! - `underlying_proto.rs`: Protocol implementation +//! - `node.rs`: Node configuration +//! - `error.rs`: Error handling + use citadel_types::crypto::{CryptoParameters, SecrecyMode, SecurityLevel}; use citadel_types::proto::SessionSecuritySettings; diff --git a/citadel_proto/src/proto/misc/udp_internal_interface.rs b/citadel_proto/src/proto/misc/udp_internal_interface.rs index f1a4ed022..806916936 100644 --- a/citadel_proto/src/proto/misc/udp_internal_interface.rs +++ b/citadel_proto/src/proto/misc/udp_internal_interface.rs @@ -1,3 +1,32 @@ +//! UDP Internal Interface +//! +//! This module provides the internal interface for UDP communication in the Citadel Protocol. +//! It handles UDP packet processing, hole punching, and NAT traversal operations. +//! +//! # Features +//! +//! - UDP packet handling +//! - NAT traversal support +//! - Hole punching operations +//! - Connection management +//! - Error handling +//! - Packet encryption +//! +//! # Important Notes +//! +//! - Requires proper NAT configuration +//! - Handles both IPv4 and IPv6 +//! - Supports multiple UDP streams +//! - Manages connection timeouts +//! - Handles packet fragmentation +//! +//! # Related Components +//! +//! - `net.rs`: Network operations +//! - `session.rs`: Session management +//! - `underlying_proto.rs`: Protocol implementation +//! - `node.rs`: Node management + use crate::constants::CODEC_BUFFER_CAPACITY; use crate::error::NetworkError; use crate::functional::PairMap; diff --git a/citadel_proto/src/proto/misc/underlying_proto.rs b/citadel_proto/src/proto/misc/underlying_proto.rs index 88a214dbe..9ab987a47 100644 --- a/citadel_proto/src/proto/misc/underlying_proto.rs +++ b/citadel_proto/src/proto/misc/underlying_proto.rs @@ -1,3 +1,32 @@ +//! Underlying Protocol Implementation +//! +//! This module defines the low-level protocol implementation details for the Citadel Protocol. +//! It handles protocol negotiation, packet framing, and transport selection. +//! +//! # Features +//! +//! - Protocol negotiation +//! - Transport selection +//! - Packet framing +//! - Protocol versioning +//! - Error handling +//! - Security settings +//! +//! # Important Notes +//! +//! - Supports multiple transports +//! - Handles protocol upgrades +//! - Maintains backward compatibility +//! - Ensures secure defaults +//! - Validates protocol settings +//! +//! # Related Components +//! +//! - `net.rs`: Network operations +//! - `session_security_settings.rs`: Security configuration +//! - `session.rs`: Session management +//! - `node.rs`: Node implementation + use crate::error::NetworkError; use crate::proto::node::TlsDomain; use citadel_io::Mutex; diff --git a/citadel_proto/src/proto/mod.rs b/citadel_proto/src/proto/mod.rs index c316cb9b7..f0a9c5830 100644 --- a/citadel_proto/src/proto/mod.rs +++ b/citadel_proto/src/proto/mod.rs @@ -1,3 +1,37 @@ +//! # Citadel Protocol Core Implementation +//! +//! This module implements the core networking and protocol functionality of the Citadel Protocol. +//! It provides a comprehensive set of components for secure communication, session management, +//! and packet processing. +//! +//! ## Features +//! - **Session Management**: Handles connection lifecycles and state +//! - **Packet Processing**: Efficient packet encoding, validation, and routing +//! - **Security**: Implements encryption, key rotation, and validation +//! - **Peer Communication**: Manages peer-to-peer and group channels +//! - **State Management**: Tracks connection and operation states +//! +//! ## Module Structure +//! - `codec`: Custom BytesCodec implementation +//! - `node`: Core HyperNode implementation +//! - `packet`: Fundamental packet types and processing +//! - `peer`: Peer-to-peer communication layer +//! - `session`: Connection session management +//! - `validation`: Packet validation and security +//! +//! ## Important Notes +//! - All packet processing is inlined for performance +//! - Session management is CID-based +//! - State containers handle different protocol stages +//! - Transfer stats track performance metrics +//! +//! ## Related Components +//! - `citadel_crypt`: Provides cryptographic primitives +//! - `citadel_wire`: Handles low-level networking +//! - `citadel_types`: Common type definitions +//! - `citadel_user`: User management and authentication +//! + use crate::proto::outbound_sender::OutboundPrimaryStreamSender; use crate::proto::packet::HdpHeader; use crate::proto::session::CitadelSession; diff --git a/citadel_proto/src/proto/node.rs b/citadel_proto/src/proto/node.rs index a13347bc4..857997057 100644 --- a/citadel_proto/src/proto/node.rs +++ b/citadel_proto/src/proto/node.rs @@ -1,3 +1,31 @@ +//! Network Node Implementation +//! +//! This module implements the core networking functionality for Citadel Protocol nodes. +//! It provides the foundation for both server and peer nodes, handling connection +//! establishment, protocol negotiation, and secure communication. +//! +//! # Features +//! +//! - **Flexible Transport**: Supports TCP, TLS, and QUIC protocols +//! - **NAT Traversal**: Implements hole punching for P2P connections +//! - **Session Management**: Handles multiple concurrent sessions +//! - **Security**: Post-quantum cryptography and pre-shared key authentication +//! - **Dual Mode**: Supports both client-server and peer-to-peer architectures +//! +//! # Important Notes +//! +//! - Server nodes require proper bind address configuration +//! - Client nodes automatically handle protocol negotiation +//! - Pre-shared keys are required for server authentication +//! - QUIC support requires valid TLS certificates +//! +//! # Related Components +//! +//! - `SessionManager`: Manages active network sessions +//! - `NodeRemote`: Provides remote control interface +//! - `KernelCommunicator`: Handles kernel message passing +//! - `NetworkListener`: Manages network socket listeners +//! use std::io; use std::net::SocketAddr; use std::net::ToSocketAddrs; @@ -38,7 +66,7 @@ use crate::proto::packet_processor::includes::Duration; use crate::proto::peer::p2p_conn_handler::generic_error; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::{CitadelSession, HdpSessionInitMode}; -use crate::proto::session_manager::HdpSessionManager; +use crate::proto::session_manager::CitadelSessionManager; pub type TlsDomain = Option; @@ -50,7 +78,7 @@ define_outer_struct_wrapper!(Node, NodeInner); pub struct NodeInner { primary_socket: Option, /// Key: cid (to account for multiple clients from the same node) - session_manager: HdpSessionManager, + session_manager: CitadelSessionManager, to_kernel: UnboundedSender, local_node_type: NodeType, // Applies only to listeners, not outgoing connections @@ -92,9 +120,9 @@ impl Node { }; if let Some(local_bind_addr) = bind_addr { - log::trace!(target: "citadel", "HdpServer established on {}", local_bind_addr); + log::info!(target: "citadel", "Citadel server established on {}", local_bind_addr); } else { - log::trace!(target: "citadel", "HdpClient Established") + log::info!(target: "citadel", "Citadel client established") } let client_config = if let Some(config) = client_config { @@ -108,7 +136,7 @@ impl Node { }; let time_tracker = TimeTracker::new(); - let session_manager = HdpSessionManager::new( + let session_manager = CitadelSessionManager::new( local_node_type, to_kernel.clone(), account_manager.clone(), @@ -206,7 +234,7 @@ impl Node { } else { None }; - let peer_container = HdpSessionManager::run_peer_container(session_manager); + let peer_container = CitadelSessionManager::run_peer_container(session_manager); let localset_opt = None; ( outbound_kernel_request_handler, @@ -236,7 +264,7 @@ impl Node { } else { None }; - let peer_container = HdpSessionManager::run_peer_container(session_manager); + let peer_container = CitadelSessionManager::run_peer_container(session_manager); ( outbound_kernel_request_handler, primary_stream_listener, @@ -623,7 +651,7 @@ impl Node { async fn primary_session_creator_loop( to_kernel: UnboundedSender, local_nat_type: NatType, - session_manager: HdpSessionManager, + session_manager: CitadelSessionManager, mut socket: DualListener, server_session_password: PreSharedKey, session_spawner: UnboundedSender>>, diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs index 4032e07d9..230cc64a4 100644 --- a/citadel_proto/src/proto/node_request.rs +++ b/citadel_proto/src/proto/node_request.rs @@ -1,3 +1,29 @@ +//! Node Request Management +//! +//! This module defines the request types and structures used for communication between nodes +//! in the Citadel Protocol. It provides a comprehensive set of request types for managing +//! node connections, authentication, file transfers, and peer-to-peer communication. +//! +//! # Features +//! - Node registration and connection management +//! - Secure file transfer operations +//! - Peer-to-peer command handling +//! - Group broadcast functionality +//! - Session security and authentication +//! - Key rotation and rekeying operations +//! +//! # Important Notes +//! - All requests support ticket tracking for asynchronous response handling +//! - Security settings are configurable per session +//! - File operations support configurable security levels +//! - Password-based authentication uses SHA-256 hashing +//! +//! # Related Components +//! - `SessionManager`: Handles request processing and session management +//! - `AuthenticationRequest`: Manages authentication credentials +//! - `VirtualConnectionType`: Defines connection types between nodes +//! - `SecurityLevel`: Specifies encryption and security parameters +//! use crate::auth::AuthenticationRequest; use crate::prelude::{GroupBroadcast, PeerSignal, VirtualTargetType}; use crate::proto::state_container::VirtualConnectionType; diff --git a/citadel_proto/src/proto/node_result.rs b/citadel_proto/src/proto/node_result.rs index a04f48a8f..67dabc06d 100644 --- a/citadel_proto/src/proto/node_result.rs +++ b/citadel_proto/src/proto/node_result.rs @@ -1,3 +1,31 @@ +//! Node Result Types and Processing +//! +//! This module defines the result types and structures used for handling responses +//! and events in the Citadel Protocol. It provides a comprehensive set of result +//! types for managing node operations, connection states, and event handling. +//! +//! # Features +//! +//! - **Operation Results**: Registration, connection, and deregistration outcomes +//! - **Event Handling**: Peer events, group events, and mailbox deliveries +//! - **Transfer Management**: Object transfer and ReVFS operations +//! - **Error Handling**: Comprehensive error reporting and status codes +//! - **Session Management**: Session state and channel tracking +//! +//! # Important Notes +//! +//! - All results include ticket tracking for asynchronous operation matching +//! - Connection results contain security settings and channel information +//! - Object transfer results include progress tracking capabilities +//! - Error results provide detailed failure information +//! +//! # Related Components +//! +//! - `NodeRequest`: Defines corresponding request types +//! - `SessionManager`: Processes and generates results +//! - `PeerChannel`: Handles peer communication channels +//! - `GroupChannel`: Manages group communication channels +//! use crate::prelude::{GroupBroadcast, GroupChannel, PeerChannel, PeerSignal, UdpChannel}; use crate::proto::peer::peer_layer::MailboxTransfer; use crate::proto::remote::Ticket; diff --git a/citadel_proto/src/proto/outbound_sender.rs b/citadel_proto/src/proto/outbound_sender.rs index 0bf42be14..526e0d9dc 100644 --- a/citadel_proto/src/proto/outbound_sender.rs +++ b/citadel_proto/src/proto/outbound_sender.rs @@ -1,3 +1,53 @@ +//! # Citadel Protocol Outbound Message Handling +//! +//! This module provides functionality for sending outbound messages in the Citadel Protocol. +//! It implements various types of message senders optimized for different use cases, +//! including bounded and unbounded channels, UDP and TCP streams. +//! +//! ## Features +//! +//! - **Multiple Channel Types**: +//! - Unbounded channels for high-throughput scenarios +//! - Bounded channels for rate-limiting and backpressure +//! - UDP-specific senders for datagram-based communication +//! +//! - **Stream Management**: +//! - Primary stream handling for reliable TCP communication +//! - UDP stream handling with keep-alive support +//! - Automatic stream cleanup and resource management +//! +//! - **Error Handling**: +//! - Comprehensive error types for different failure scenarios +//! - Graceful handling of connection issues +//! - Automatic retry mechanisms +//! +//! - **Performance Optimizations**: +//! - Zero-copy buffer management +//! - Efficient async/await support +//! - Minimal allocation overhead +//! +//! ## Components +//! +//! - **UnboundedSender**: High-throughput channel without backpressure +//! - **BoundedSender**: Rate-limited channel with backpressure +//! - **OutboundPrimaryStreamSender**: TCP stream management +//! - **OutboundUdpSender**: UDP datagram handling with keep-alive +//! +//! ## Example Usage +//! +//! ```no_run +//! use citadel_proto::outbound_sender::{UnboundedSender, BoundedSender}; +//! +//! // Create an unbounded channel +//! let (tx, rx) = UnboundedSender::unbounded(); +//! +//! // Create a bounded channel with rate limiting +//! let (tx, rx) = BoundedSender::new(100); +//! +//! // Send messages +//! tx.send(message)?; +//! ``` + use crate::error::NetworkError; use crate::proto::packet::packet_flags; use bytes::BytesMut; diff --git a/citadel_proto/src/proto/packet.rs b/citadel_proto/src/proto/packet.rs index 571a230a3..c07a29bca 100644 --- a/citadel_proto/src/proto/packet.rs +++ b/citadel_proto/src/proto/packet.rs @@ -1,3 +1,35 @@ +//! # Citadel Protocol Packet Structure +//! +//! This module defines the core packet structure and handling for the Citadel Protocol. +//! It implements the Hypernode Data Protocol (HDP) packet format, which provides secure +//! and efficient data transmission between nodes. +//! +//! ## Features +//! +//! * Packet header definition and handling +//! * Command type classification (primary and auxiliary) +//! * Packet size management +//! * Buffer trait implementation for different types +//! * Zero-copy header parsing +//! * Socket address tracking +//! * Packet decomposition and composition +//! +//! ## Important Notes +//! +//! * Headers are fixed-size and aligned +//! * Commands are hierarchically organized +//! * Supports both BytesMut and Vec buffers +//! * Implements zero-copy parsing for efficiency +//! * Maintains packet integrity checks +//! +//! ## Related Components +//! +//! * `packet_processor`: Processes different packet types +//! * `packet_crafter`: Creates protocol packets +//! * `validation`: Validates packet structure +//! * `state_container`: Manages packet state +//! * `session`: Handles packet sessions +//! use crate::constants::HDP_HEADER_BYTE_LEN; use bytes::{BufMut, Bytes, BytesMut}; use std::net::SocketAddr; diff --git a/citadel_proto/src/proto/packet_crafter.rs b/citadel_proto/src/proto/packet_crafter.rs index 21a398d86..29a0e058a 100644 --- a/citadel_proto/src/proto/packet_crafter.rs +++ b/citadel_proto/src/proto/packet_crafter.rs @@ -1,3 +1,62 @@ +//! # Citadel Protocol Packet Crafting +//! +//! This module provides the functionality to create various types of protocol packets +//! used in the Citadel Protocol. Each packet type is carefully crafted with appropriate +//! headers, encryption, and security measures. +//! +//! ## Packet Types +//! +//! The module supports crafting several types of packets: +//! +//! - **Connection Management** +//! - Pre-connect packets for initial handshake +//! - Connect packets for establishing connections +//! - Disconnect packets for clean connection termination +//! +//! - **Authentication** +//! - Register packets for user registration +//! - Deregister packets for account removal +//! +//! - **Data Transfer** +//! - File transfer packets with metadata +//! - Group message packets for broadcast +//! - Channel packets for direct communication +//! +//! - **Maintenance** +//! - Keep-alive packets for connection health +//! - Drill update packets for key rotation +//! - Hole punch packets for NAT traversal +//! +//! ## Security Features +//! +//! Each packet is secured with: +//! - Post-quantum encryption +//! - Integrity protection +//! - Replay attack prevention +//! - Perfect forward secrecy +//! +//! ## Implementation Details +//! +//! The packet crafting process involves: +//! 1. Header construction with appropriate flags +//! 2. Payload serialization and encryption +//! 3. Security level enforcement +//! 4. Timestamp and sequence number management +//! +//! ## Example Usage +//! +//! ```no_run +//! use citadel_proto::packet_crafter::{SecureProtocolPacket, GroupTransmitter}; +//! +//! // Create a secure packet +//! let packet = SecureProtocolPacket::new(); +//! packet.write_payload(data)?; +//! +//! // Create a group transmitter for file transfer +//! let transmitter = GroupTransmitter::new(stream, config)?; +//! transmitter.transmit_group_header(target)?; +//! ``` + use bytes::BytesMut; use citadel_crypt::scramble::crypt_splitter::{GroupReceiverConfig, GroupSenderDevice}; @@ -14,6 +73,28 @@ use citadel_crypt::secure_buffer::sec_packet::SecureMessagePacket; use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; use citadel_types::prelude::ObjectId; +/// A secure packet container that provides zero-copy buffer management and +/// automatic memory zeroing for sensitive data. This structure is used as +/// the base for all protocol packets. +/// +/// # Features +/// +/// - Zero-copy buffer management +/// - Automatic memory zeroing +/// - Efficient payload extraction +/// - Secure message encapsulation +/// +/// # Example +/// +/// ```no_run +/// use citadel_proto::packet_crafter::SecureProtocolPacket; +/// +/// let mut packet = SecureProtocolPacket::new(); +/// packet.write_payload(data_len, |buf| { +/// buf.copy_from_slice(data); +/// Ok(()) +/// })?; +/// ``` #[derive(Debug)] /// A thin wrapper used for convenient creation of zero-copy outgoing buffers pub struct SecureProtocolPacket { @@ -21,22 +102,26 @@ pub struct SecureProtocolPacket { } impl SecureProtocolPacket { + /// Creates a new secure packet pub(crate) fn new() -> Self { Self { inner: SecureMessagePacket::new().unwrap(), } } + /// Extracts the message from a given buffer pub(crate) fn extract_message(input: &mut BytesMut) -> std::io::Result { SecureMessagePacket::::extract_payload(input) } + /// Creates a new secure packet from an inner packet pub(crate) fn from_inner(inner: SecureMessagePacket) -> Self { Self { inner } } } impl> From for SecureProtocolPacket { + /// Creates a new secure packet from a given byte slice fn from(bytes: T) -> Self { let bytes = bytes.as_ref(); let mut this = Self::new(); @@ -51,11 +136,40 @@ impl> From for SecureProtocolPacket { } impl From for SecureMessagePacket { + /// Converts a secure packet to an inner packet fn from(val: SecureProtocolPacket) -> Self { val.inner } } +/// Manages the transmission of group messages and file transfers with support +/// for encryption, scrambling, and packet management. This structure handles +/// the complexities of breaking large transfers into manageable chunks while +/// maintaining security. +/// +/// # Features +/// +/// - Secure group transmission +/// - Automatic packet chunking +/// - Progress tracking +/// - Error handling +/// +/// # Example +/// +/// ```no_run +/// use citadel_proto::packet_crafter::GroupTransmitter; +/// +/// let transmitter = GroupTransmitter::new_message( +/// stream, +/// object_id, +/// ratchet, +/// packet, +/// security_level, +/// group_id, +/// ticket, +/// time_tracker, +/// )?; +/// ``` pub struct GroupTransmitter { pub hyper_ratchet_container: RatchetPacketCrafterContainer, to_primary_stream: OutboundPrimaryStreamSender, @@ -85,6 +199,7 @@ pub struct RatchetPacketCrafterContainer { } impl RatchetPacketCrafterContainer { + /// Creates a new ratchet packet crafter container pub fn new(base: R, base_constructor: Option) -> Self { Self { base, @@ -226,7 +341,6 @@ impl GroupTransmitter { } pub(crate) mod group { - use bytes::{BufMut, BytesMut}; use zerocopy::{I64, U128, U32, U64}; @@ -246,6 +360,7 @@ pub(crate) mod group { use citadel_user::serialization::SyncIO; use std::ops::RangeInclusive; + /// Crafts a group header packet for a given group transmitter and virtual target pub(super) fn craft_group_header_packet( processor: &mut GroupTransmitter, virtual_target: VirtualTargetType, @@ -313,10 +428,7 @@ pub(crate) mod group { packet } - /// `initial_wave_window` should be set the Some if this node is ready to begin receiving the data - /// `message`: Is appended to the end of the payload - /// `fast_msg`: If this is true, then that implies the receiver already got the message. The initiator that gets the header ack - /// needs to only delete the outbound container + /// Crafts a group header acknowledgement packet for a given group transmitter and virtual target #[allow(clippy::too_many_arguments)] pub(crate) fn craft_group_header_ack( hyper_ratchet: &StackedRatchet, @@ -364,8 +476,7 @@ pub(crate) mod group { packet } - /// This is called by the scrambler. NOTE: the scramble_drill MUST have the same drill/cid as the message_drill, otherwise - /// packets will not be rendered on the otherside + /// Crafts a wave payload packet for a given group transmitter and virtual target pub(crate) fn craft_wave_payload_packet_into( coords: &PacketVector, scramble_drill: &EntropyBank, @@ -398,7 +509,7 @@ pub(crate) mod group { buffer.put_u8(remote_port as u8); } - // NOTE: context infos contain the object ID in most of the GROUP packets + /// Crafts a wave acknowledgement packet for a given group transmitter and virtual target #[allow(clippy::too_many_arguments)] pub(crate) fn craft_wave_ack( hyper_ratchet: &StackedRatchet, @@ -453,6 +564,7 @@ pub(crate) mod do_connect { use citadel_user::serialization::SyncIO; use serde::{Deserialize, Serialize}; + /// Crafts a do-connect stage 0 packet for a given proposed credentials and timestamp #[derive(Serialize, Deserialize)] pub struct DoConnectStage0Packet { pub proposed_credentials: ProposedCredentials, @@ -502,6 +614,7 @@ pub(crate) mod do_connect { packet } + /// Crafts a do-connect final status packet for a given mailbox transfer, peers, and post-login object #[derive(Serialize, Deserialize)] pub struct DoConnectFinalStatusPacket<'a> { pub mailbox: Option, @@ -577,6 +690,7 @@ pub(crate) mod do_connect { packet } + /// Crafts a do-connect success acknowledgement packet for a given timestamp and security level #[allow(unused_results)] pub(crate) fn craft_success_ack( hyper_ratchet: &StackedRatchet, @@ -618,6 +732,7 @@ pub(crate) mod keep_alive { use citadel_crypt::stacked_ratchet::StackedRatchet; use citadel_types::crypto::SecurityLevel; + /// Crafts a keep-alive packet for a given timestamp and security level pub(crate) fn craft_keep_alive_packet( hyper_ratchet: &StackedRatchet, timestamp: i64, @@ -659,6 +774,7 @@ pub(crate) mod do_register { use citadel_user::serialization::SyncIO; use serde::{Deserialize, Serialize}; + /// Crafts a do-register stage 0 packet for a given transfer and passwordless flag #[derive(Serialize, Deserialize)] pub(crate) struct DoRegisterStage0 { pub(crate) transfer: AliceToBobTransfer, @@ -703,7 +819,7 @@ pub(crate) mod do_register { packet } - /// Bob crafts a packet with the ciphertext + /// Crafts a do-register stage 1 packet for a given transfer and proposed CID pub(crate) fn craft_stage1( algorithm: u8, timestamp: i64, @@ -733,6 +849,7 @@ pub(crate) mod do_register { packet } + /// Crafts a do-register stage 2 packet for a given credentials and timestamp #[derive(Serialize, Deserialize)] pub struct DoRegisterStage2Packet { pub credentials: ProposedCredentials, @@ -777,7 +894,7 @@ pub(crate) mod do_register { packet } - /// `success_message`: This is NOT encrypted in this closure. Make sure to encrypt it beforehand if necessary + /// Crafts a do-register success packet for a given success message and timestamp pub(crate) fn craft_success>( hyper_ratchet: &StackedRatchet, algorithm: u8, @@ -813,7 +930,7 @@ pub(crate) mod do_register { packet } - /// No encryption used for this packet + /// Crafts a do-register failure packet for a given error message and proposed CID pub(crate) fn craft_failure>( algorithm: u8, timestamp: i64, @@ -845,8 +962,7 @@ pub(crate) mod do_register { } } -/// For creating disconnect packets -pub mod do_disconnect { +pub(crate) mod do_disconnect { use bytes::BytesMut; use zerocopy::{I64, U128, U32, U64}; @@ -856,7 +972,7 @@ pub mod do_disconnect { use citadel_crypt::stacked_ratchet::StackedRatchet; use citadel_types::crypto::SecurityLevel; - /// The drill used should be an unused one. (generate a new drill) + /// Crafts a do-disconnect stage 0 packet for a given ticket, timestamp, and security level #[allow(unused_results)] pub(crate) fn craft_stage0( hyper_ratchet: &StackedRatchet, @@ -887,7 +1003,7 @@ pub mod do_disconnect { packet } - /// Bob sends Alice an message implying the disconnect has been handled + /// Crafts a do-disconnect final packet for a given ticket, timestamp, and security level #[allow(unused_results)] pub(crate) fn craft_final( hyper_ratchet: &StackedRatchet, @@ -911,10 +1027,10 @@ pub mod do_disconnect { }; let mut packet = header.as_packet(); + hyper_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); - packet } } @@ -932,6 +1048,7 @@ pub(crate) mod do_drill_update { use citadel_user::serialization::SyncIO; use serde::{Deserialize, Serialize}; + /// Crafts a do-drill update stage 0 packet for a given transfer, timestamp, target CID, and security level #[allow(unused_results)] pub(crate) fn craft_stage0( hyper_ratchet: &StackedRatchet, @@ -965,6 +1082,7 @@ pub(crate) mod do_drill_update { packet } + /// Crafts a do-drill update stage 1 packet for a given update status, timestamp, target CID, and security level #[derive(Serialize, Deserialize)] pub(crate) struct Stage1UpdatePacket { pub(crate) update_status: KemTransferStatus, @@ -1005,6 +1123,7 @@ pub(crate) mod do_drill_update { packet } + /// Crafts a do-drill update truncate packet for a given truncate version, target CID, timestamp, and security level #[derive(Serialize, Deserialize)] pub(crate) struct TruncatePacket { pub(crate) truncate_version: Option, @@ -1046,6 +1165,7 @@ pub(crate) mod do_drill_update { packet } + /// Crafts a do-drill update truncate acknowledgement packet for a given truncated version, target CID, timestamp, and security level #[derive(Serialize, Deserialize)] pub(crate) struct TruncateAckPacket { pub(crate) truncated_version: u32, @@ -1095,6 +1215,7 @@ pub(crate) mod do_deregister { use citadel_crypt::stacked_ratchet::StackedRatchet; use citadel_types::crypto::SecurityLevel; + /// Crafts a do-deregister stage 0 packet for a given timestamp and security level pub(crate) fn craft_stage0( hyper_ratchet: &StackedRatchet, timestamp: i64, @@ -1124,6 +1245,7 @@ pub(crate) mod do_deregister { packet } + /// Crafts a do-deregister final packet for a given success flag, timestamp, and security level #[allow(unused_results)] pub(crate) fn craft_final( hyper_ratchet: &StackedRatchet, @@ -1182,6 +1304,7 @@ pub(crate) mod pre_connect { use citadel_wire::nat_identification::NatType; use serde::{Deserialize, Serialize}; + /// Crafts a pre-connect SYN packet for a given transfer, NAT type, UDP mode, timestamp, keep-alive timeout, security level, session security settings, peer-only connect protocol, and connect mode #[derive(Serialize, Deserialize)] pub struct SynPacket { pub transfer: AliceToBobTransfer, @@ -1242,6 +1365,7 @@ pub(crate) mod pre_connect { packet } + /// Crafts a pre-connect SYN acknowledgement packet for a given transfer, NAT type, timestamp, and security level #[derive(Serialize, Deserialize)] pub struct SynAckPacket { pub transfer: BobToAliceTransfer, @@ -1284,6 +1408,7 @@ pub(crate) mod pre_connect { packet } + /// Crafts a pre-connect stage 0 packet for a given node type, timestamp, and security level #[derive(Serialize, Deserialize)] pub struct PreConnectStage0 { pub node_type: NodeType, @@ -1325,7 +1450,7 @@ pub(crate) mod pre_connect { packet } - /// If `tcp_only` is set to true, then the primary stream will be used for sharing information instead of the wave ports + /// Crafts a pre-connect final packet for a given success flag, TCP-only flag, timestamp, and security level pub(crate) fn craft_stage_final( hyper_ratchet: &StackedRatchet, success: bool, @@ -1368,6 +1493,7 @@ pub(crate) mod pre_connect { packet } + /// Crafts a pre-connect begin connect packet for a given timestamp and security level pub(crate) fn craft_begin_connect( hyper_ratchet: &StackedRatchet, timestamp: i64, @@ -1395,6 +1521,7 @@ pub(crate) mod pre_connect { packet } + /// Crafts a pre-connect halt packet for a given previous header and fail reason pub fn craft_halt>(prev_header: &HdpHeader, fail_reason: T) -> BytesMut { let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), @@ -1434,6 +1561,7 @@ pub(crate) mod peer_cmd { use serde::Serialize; use zerocopy::{I64, U128, U32, U64}; + /// Crafts a peer signal packet for a given peer command, ticket, timestamp, and security level pub(crate) const C2S_ENCRYPTION_ONLY: u64 = 0; /* @@ -1476,6 +1604,7 @@ pub(crate) mod peer_cmd { packet } + /// Crafts a peer signal endpoint packet for a given peer command, ticket, timestamp, target CID, and security level #[allow(dead_code)] pub(crate) fn craft_peer_signal_endpoint( hyper_ratchet: &StackedRatchet, @@ -1514,7 +1643,7 @@ pub(crate) mod peer_cmd { packet } - /// Channel packets ALWAYS get rerouted, and hence NEED a target_cid + /// Crafts a channel packet for a given payload, ticket, proxy target CID, timestamp, and security level #[allow(dead_code)] pub(crate) fn craft_channel_packet( hyper_ratchet: &StackedRatchet, @@ -1552,7 +1681,7 @@ pub(crate) mod peer_cmd { packet } - /// Group message packets, unlike channel packets, do not always get rerouted + /// Crafts a group message packet for a given payload, ticket, proxy target CID, timestamp, and security level #[allow(dead_code)] pub(crate) fn craft_group_message_packet( hyper_ratchet: &StackedRatchet, @@ -1586,7 +1715,6 @@ pub(crate) mod peer_cmd { hyper_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); - packet } } @@ -1606,6 +1734,7 @@ pub(crate) mod file { use std::path::PathBuf; use zerocopy::{I64, U128, U32, U64}; + /// Crafts a file transfer error packet for a given error message, object ID, ticket, security level, virtual target, timestamp, and transfer type #[derive(Serialize, Deserialize, Debug)] pub struct FileTransferErrorPacket { pub error_message: String, @@ -1652,6 +1781,7 @@ pub(crate) mod file { packet } + /// Crafts a file header packet for a given file metadata, virtual target, local encryption level, group start, ticket, security level, timestamp, and transfer type #[derive(Serialize, Deserialize, Debug)] pub struct FileHeaderPacket { pub file_metadata: VirtualObjectMetadata, @@ -1702,6 +1832,7 @@ pub(crate) mod file { packet } + /// Crafts a file header acknowledgement packet for a given success flag, object ID, target CID, ticket, security level, virtual target, timestamp, and transfer type #[derive(Serialize, Deserialize, Debug)] pub struct FileHeaderAckPacket { pub success: bool, @@ -1756,6 +1887,7 @@ pub(crate) mod file { packet } + /// Crafts a ReVFSPull packet for a given virtual path, delete on pull flag, security level, ticket, timestamp, and target CID #[derive(Serialize, Deserialize, Debug)] pub struct ReVFSPullPacket { pub virtual_path: PathBuf, @@ -1763,8 +1895,6 @@ pub(crate) mod file { pub security_level: SecurityLevel, } - /// This packet will essentially cause the receiving endpoint to emulate - /// a FILE_HEADER with auto-accept on pub fn craft_revfs_pull( hyper_ratchet: &StackedRatchet, security_level: SecurityLevel, @@ -1806,6 +1936,7 @@ pub(crate) mod file { packet } + /// Crafts a ReVFSDelete packet for a given virtual path, ticket, timestamp, and target CID #[derive(Serialize, Deserialize, Debug)] pub struct ReVFSDeletePacket { pub virtual_path: PathBuf, @@ -1847,6 +1978,7 @@ pub(crate) mod file { packet } + /// Crafts a ReVFSAck packet for a given success flag, error message, ticket, timestamp, and target CID #[derive(Serialize, Deserialize, Debug)] pub struct ReVFSAckPacket { pub success: bool, @@ -1890,6 +2022,7 @@ pub(crate) mod file { packet } + /// Crafts a ReVFSPullAck packet for a given payload, ticket, timestamp, and target CID #[derive(Serialize, Deserialize, Debug)] pub enum ReVFSPullAckPacket { Success, @@ -1939,6 +2072,7 @@ pub(crate) mod udp { use citadel_types::crypto::SecurityLevel; use zerocopy::{U32, U64}; + /// Crafts a UDP packet for a given command auxiliary, payload, target CID, and security level pub(crate) fn craft_udp_packet( hyper_ratchet: &StackedRatchet, cmd_aux: u8, @@ -1981,6 +2115,7 @@ pub(crate) mod hole_punch { use citadel_types::crypto::SecurityLevel; use zerocopy::{U32, U64}; + /// Crafts a hole punch packet for a given plaintext, security level, target CID, and hyper ratchet pub fn generate_packet( hyper_ratchet: &StackedRatchet, plaintext: &[u8], @@ -2013,8 +2148,7 @@ pub(crate) mod hole_punch { packet } - /// this is called assuming the CORRECT hyper ratchet is used (i.e., the same one used above) - /// This strips the header, since it's only relevant to the networking protocol and NOT the hole-puncher + /// Decrypts a hole punch packet for a given packet, hyper ratchet, security level, and target CID pub fn decrypt_packet( _hyper_ratchet: &StackedRatchet, packet: &[u8], diff --git a/citadel_proto/src/proto/packet_processor/connect_packet.rs b/citadel_proto/src/proto/packet_processor/connect_packet.rs index 9850ffd46..54c236f9d 100644 --- a/citadel_proto/src/proto/packet_processor/connect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/connect_packet.rs @@ -1,3 +1,36 @@ +//! # Connection Packet Processing +//! +//! This module implements the connection establishment protocol for Citadel. +//! It handles the secure handshake process between nodes, including authentication, +//! capability negotiation, and secure channel establishment. +//! +//! # Protocol Flow +//! +//! 1. **Stage 0**: Initial connection request +//! - Client sends encrypted credentials +//! - Server validates and processes request +//! - Capability negotiation (filesystem, UDP support) +//! +//! 2. **Success Stage**: +//! - Secure channel establishment +//! - Virtual connection initialization +//! - Session security settings configuration +//! - UDP channel setup (if supported) +//! +//! # Security Features +//! +//! - Post-quantum cryptographic handshake +//! - Secure credential transmission +//! - Session-specific security settings +//! - State validation at each stage +//! +//! # Related Components +//! +//! - `PreConnectPacket`: Handles pre-connection setup +//! - `StateContainer`: Manages connection state +//! - `VirtualConnection`: Manages established connections +//! - `SessionSecuritySettings`: Configures connection security +//! use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{ConnectFail, ConnectSuccess, MailboxDelivery}; @@ -8,7 +41,15 @@ use citadel_user::external_services::ServicesObject; use std::sync::atomic::Ordering; /// This will optionally return an HdpPacket as a response if deemed necessary -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = sess_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = sess_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] pub async fn process_connect( sess_ref: &CitadelSession, packet: HdpPacket, @@ -158,7 +199,7 @@ pub async fn process_connect( welcome_message: format!("Client {cid} successfully established a connection to the local HyperNode"), channel, udp_rx_opt: udp_channel_rx, - session_security_settings + session_security_settings, }); // safe unwrap. Store the signal inner_mut_state!(session.state_container) diff --git a/citadel_proto/src/proto/packet_processor/deregister_packet.rs b/citadel_proto/src/proto/packet_processor/deregister_packet.rs index 6dcd8dbf2..688f3ffc5 100644 --- a/citadel_proto/src/proto/packet_processor/deregister_packet.rs +++ b/citadel_proto/src/proto/packet_processor/deregister_packet.rs @@ -1,3 +1,52 @@ +//! Deregistration Packet Processor for Citadel Protocol +//! +//! This module handles the deregistration process for clients in the Citadel Protocol +//! network. It manages the secure removal of client accounts and cleanup of associated +//! resources from both the client and server sides. +//! +//! # Features +//! +//! - Secure client deregistration +//! - Resource cleanup +//! - Session state validation +//! - Ticket-based tracking +//! - Success/failure handling +//! +//! # Important Notes +//! +//! - Client must be connected to deregister +//! - Process is irreversible +//! - Handles both client and server-side cleanup +//! - Maintains security during account removal +//! - Requires valid session state +//! +//! # Related Components +//! +//! - `StateContainer`: Manages deregistration state +//! - `AccountManager`: Handles account removal +//! - `SessionManager`: Manages session cleanup +//! - `KernelInterface`: Reports deregistration results +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::deregister_packet; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! async fn handle_deregister(session: &CitadelSession, packet: HdpPacket) { +//! let header_drill_vers = 1; +//! match deregister_packet::process_deregister(session, packet, header_drill_vers).await { +//! Ok(result) => { +//! // Handle successful deregistration +//! } +//! Err(err) => { +//! // Handle deregistration error +//! } +//! } +//! } +//! ``` + use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::DeRegistration; diff --git a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs index 1cede5d4c..0d9b59956 100644 --- a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs @@ -1,3 +1,52 @@ +//! Disconnect Packet Processor for Citadel Protocol +//! +//! This module handles the graceful disconnection process between nodes in the +//! Citadel Protocol network. It implements a two-stage handshake to ensure both +//! parties properly close their connection. +//! +//! # Features +//! +//! - Two-stage disconnect handshake +//! - Secure packet validation +//! - Session state management +//! - Ticket tracking +//! - Kernel notification +//! +//! # Important Notes +//! +//! - Requires connected session state +//! - All packets must be authenticated +//! - Implements brief delay for packet delivery +//! - Manages session state transitions +//! - Notifies kernel of disconnection +//! +//! # Related Components +//! +//! - `StateContainer`: Manages session state +//! - `KernelInterface`: Handles disconnect signals +//! - `SessionManager`: Tracks session lifecycle +//! - `PrimaryStream`: Handles packet transmission +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::disconnect_packet; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! async fn handle_disconnect(session: &CitadelSession, packet: HdpPacket) { +//! let header_drill_vers = 1; +//! match disconnect_packet::process_disconnect(session, packet, header_drill_vers).await { +//! Ok(result) => { +//! // Handle successful disconnection +//! } +//! Err(err) => { +//! // Handle disconnection error +//! } +//! } +//! } +//! ``` + use super::includes::*; use crate::error::NetworkError; use crate::proto::packet_processor::primary_group_packet::get_proper_hyper_ratchet; diff --git a/citadel_proto/src/proto/packet_processor/file_packet.rs b/citadel_proto/src/proto/packet_processor/file_packet.rs index c1ce943ba..a4b2beba3 100644 --- a/citadel_proto/src/proto/packet_processor/file_packet.rs +++ b/citadel_proto/src/proto/packet_processor/file_packet.rs @@ -1,3 +1,54 @@ +//! File Transfer Packet Processor for Citadel Protocol +//! +//! This module handles secure file transfer operations in the Citadel Protocol network. +//! It manages the entire file transfer lifecycle, including metadata exchange, +//! chunked transfers, error handling, and virtual filesystem operations. +//! +//! # Features +//! +//! - Secure file transfer processing +//! - File metadata handling +//! - Chunked transfer support +//! - Error reporting and recovery +//! - Virtual filesystem integration +//! - Transfer state tracking +//! - Encryption level management +//! +//! # Important Notes +//! +//! - Requires connected session state +//! - All packets must be authenticated +//! - Supports virtual target routing +//! - Handles both direct and proxied transfers +//! - Maintains transfer security levels +//! +//! # Related Components +//! +//! - `StateContainer`: Manages transfer state +//! - `VirtualFileSystem`: Handles file operations +//! - `ObjectTransferHandle`: Tracks transfer progress +//! - `SecurityLevel`: Manages encryption levels +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::file_packet; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! fn handle_file_packet(session: &CitadelSession, packet: HdpPacket) { +//! let proxy_info = None; +//! match file_packet::process_file_packet(session, packet, proxy_info) { +//! Ok(result) => { +//! // Handle successful file operation +//! } +//! Err(err) => { +//! // Handle file operation error +//! } +//! } +//! } +//! ``` + use super::includes::*; use crate::error::NetworkError; use crate::prelude::{InternalServerError, ReVFSResult, Ticket}; diff --git a/citadel_proto/src/proto/packet_processor/hole_punch.rs b/citadel_proto/src/proto/packet_processor/hole_punch.rs index 1164f83ab..584a38a39 100644 --- a/citadel_proto/src/proto/packet_processor/hole_punch.rs +++ b/citadel_proto/src/proto/packet_processor/hole_punch.rs @@ -1,3 +1,53 @@ +//! Hole Punch Packet Processor for Citadel Protocol +//! +//! This module implements NAT traversal functionality through hole punching in the +//! Citadel Protocol network. It enables direct peer-to-peer connections between nodes +//! behind NATs by coordinating connection establishment. +//! +//! # Features +//! +//! - NAT traversal packet processing +//! - Secure packet validation +//! - Peer connection coordination +//! - Connection pipe management +//! - Proxy support +//! +//! # Important Notes +//! +//! - Requires valid peer CID +//! - All packets must be authenticated +//! - Manages hole puncher pipes +//! - Supports proxied connections +//! - Forwards validated packets +//! +//! # Related Components +//! +//! - `StateContainer`: Manages hole punch state +//! - `HolePuncherPipe`: Handles connection establishment +//! - `StackedRatchet`: Provides packet security +//! - `ProxyManager`: Handles proxied connections +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::hole_punch; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! fn handle_hole_punch(session: &CitadelSession, packet: HdpPacket) { +//! let hr_version = 1; +//! let proxy_info = None; +//! match hole_punch::process_hole_punch(session, packet, hr_version, proxy_info) { +//! Ok(result) => { +//! // Handle successful hole punch +//! } +//! Err(err) => { +//! // Handle hole punch error +//! } +//! } +//! } +//! ``` + use super::includes::*; use crate::error::NetworkError; use crate::proto::packet_processor::primary_group_packet::{ diff --git a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs index a1a06208c..abf39e06c 100644 --- a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs +++ b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs @@ -1,3 +1,36 @@ +//! # Keep-Alive Packet Processor +//! +//! Implements connection maintenance through periodic keep-alive packets, +//! ensuring connections remain active and detecting disconnections early. +//! +//! ## Features +//! +//! - **Connection Maintenance**: +//! - Periodic heartbeat packets +//! - Connection liveness detection +//! - Timeout management +//! - Automatic reconnection triggers +//! +//! - **State Management**: +//! - Connection state tracking +//! - Latency monitoring +//! - Jitter calculation +//! - Connection quality metrics +//! +//! ## Important Notes +//! +//! - Configurable keep-alive intervals +//! - Adaptive timing based on network conditions +//! - Minimal bandwidth overhead +//! - Handles both UDP and TCP connections +//! +//! ## Related Components +//! +//! - [`connect_packet`]: Connection establishment +//! - [`disconnect_packet`]: Connection termination +//! - [`session_manager`]: Session management +//! - [`udp_internal_interface`]: UDP transport + use super::includes::*; use crate::error::NetworkError; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; @@ -6,7 +39,15 @@ use std::sync::atomic::Ordering; /// This will handle a keep alive packet. It will automatically send a keep packet after it sleeps for a period of time #[allow(unused_results, unused_must_use)] -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] pub async fn process_keep_alive( session: &CitadelSession, packet: HdpPacket, diff --git a/citadel_proto/src/proto/packet_processor/mod.rs b/citadel_proto/src/proto/packet_processor/mod.rs index 7c8d630af..5f1649053 100644 --- a/citadel_proto/src/proto/packet_processor/mod.rs +++ b/citadel_proto/src/proto/packet_processor/mod.rs @@ -1,3 +1,99 @@ +//! # Packet Processor Module +//! +//! Core packet processing infrastructure for the Citadel Protocol, handling all aspects +//! of packet lifecycle from creation to processing. +//! +//! ## Features +//! +//! - **Packet Type Management**: Handles various packet types including: +//! - Connection establishment packets +//! - Authentication packets +//! - Data transfer packets +//! - Keep-alive packets +//! - Group communication packets +//! +//! - **Processing Pipeline**: +//! - Packet validation and verification +//! - State management during processing +//! - Error handling and recovery +//! - Packet queueing and ordering +//! +//! - **Security**: +//! - Cryptographic verification +//! - Replay attack prevention +//! - Packet integrity checks +//! +//! ## Important Notes +//! +//! - All packet processors implement proper error handling +//! - Processors maintain packet ordering guarantees +//! - Implements backpressure mechanisms +//! - Handles partial packet reconstruction +//! +//! ## Related Components +//! +//! - [`connect_packet`]: Connection establishment +//! - [`register_packet`]: Node registration +//! - [`primary_group_packet`]: Group communication +//! - [`peer_cmd_packet`]: Peer commands +//! - [`file_packet`]: File transfer +//! - [`keep_alive_packet`]: Connection maintenance + +//! # Citadel Protocol Packet Processing +//! +//! This module implements the core packet processing functionality for the Citadel Protocol. +//! It handles various types of packets including connection establishment, data transfer, +//! group communication, and connection maintenance. +//! +//! ## Packet Types +//! +//! The protocol supports several types of packets: +//! +//! - **Connection Packets**: Handle connection establishment and termination +//! - `connect_packet`: Connection establishment +//! - `disconnect_packet`: Connection termination +//! - `preconnect_packet`: Initial connection setup +//! +//! - **Authentication Packets**: +//! - `register_packet`: User registration +//! - `deregister_packet`: User deregistration +//! +//! - **Data Transfer Packets**: +//! - `file_packet`: File transfer operations +//! - `primary_group_packet`: Group communication +//! - `raw_primary_packet`: Raw data transfer +//! +//! - **Maintenance Packets**: +//! - `keep_alive_packet`: Connection maintenance +//! - `rekey_packet`: Key rotation +//! - `hole_punch`: NAT traversal +//! +//! ## Processing Flow +//! +//! 1. Incoming packets are validated and decrypted +//! 2. Packet type is determined from header +//! 3. Packet is processed by appropriate handler +//! 4. Response is generated if needed +//! +//! ## Security +//! +//! - All packets are encrypted using post-quantum cryptography +//! - Headers are protected against tampering +//! - Replay attacks are prevented through sequence numbers +//! +//! ## Example +//! +//! ```no_run +//! use citadel_proto::packet_processor::{PrimaryProcessorResult, HdpPacket}; +//! +//! // Process an incoming packet +//! match process_packet(packet) { +//! PrimaryProcessorResult::Void => { /* No response needed */ } +//! PrimaryProcessorResult::ReplyToSender(response) => { /* Send response */ } +//! PrimaryProcessorResult::EndSession(reason) => { /* Handle session end */ } +//! } +//! ``` + use crate::proto::packet::HdpHeader; use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; use crate::proto::state_container::VirtualConnectionType; @@ -40,13 +136,16 @@ pub mod udp_packet; // pub mod hole_punch; -/// Allows the [HdpSession] to read results from the packet processor herein +/// Represents the result of processing a primary packet in the Citadel Protocol. +/// This enum is used to communicate the outcome of packet processing back to the +/// session handler. #[derive(PartialEq)] pub enum PrimaryProcessorResult { - /// Do nothing + /// No action needed after processing the packet Void, + /// Session should be terminated with the given reason EndSession(&'static str), - /// Returns some data to the sender + /// A response packet should be sent back to the sender ReplyToSender(BytesMut), } diff --git a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs index a884e7a91..efe8128c6 100644 --- a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs +++ b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs @@ -1,3 +1,48 @@ +//! Group Broadcast Handler for Citadel Protocol +//! +//! This module implements the group messaging and management functionality in the +//! Citadel Protocol network. It handles group creation, membership management, +//! message broadcasting, and group state synchronization. +//! +//! # Features +//! +//! - Group creation and termination +//! - Membership management (add, remove, kick) +//! - Message broadcasting +//! - Permission control +//! - Group state tracking +//! - Invitation handling +//! - Group listing +//! +//! # Important Notes +//! +//! - All group operations require authentication +//! - Group owners have special permissions +//! - Messages are securely encrypted +//! - Supports group state persistence +//! - Handles member disconnections +//! +//! # Related Components +//! +//! - `CitadelSession`: Session management +//! - `MessageGroupKey`: Group identification +//! - `GroupBroadcastPayload`: Message handling +//! - `MessageGroupOptions`: Group configuration +//! - `MemberState`: Member status tracking +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::peer::group_broadcast::GroupBroadcast; +//! use citadel_types::proto::MessageGroupOptions; +//! +//! // Create a new group broadcast message +//! let broadcast = GroupBroadcast::Create { +//! initial_invitees: vec![1, 2, 3], // CIDs of initial members +//! options: MessageGroupOptions::default(), +//! }; +//! ``` + use super::super::includes::*; use crate::error::NetworkError; use crate::functional::*; @@ -13,106 +58,178 @@ use citadel_user::serialization::SyncIO; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] +/// Enum representing different types of group broadcast messages pub enum GroupBroadcast { - // contains the set of peers that will receive an initial invitation (must be connected) + /// Create a new group with initial invitees Create { + /// CIDs of initial members initial_invitees: Vec, + /// Group configuration options options: MessageGroupOptions, }, + /// Leave a group LeaveRoom { + /// Group key key: MessageGroupKey, }, + /// Response to leaving a group LeaveRoomResponse { + /// Group key key: MessageGroupKey, + /// Success status success: bool, + /// Response message message: String, }, + /// End a group End { + /// Group key key: MessageGroupKey, }, + /// Response to ending a group EndResponse { + /// Group key key: MessageGroupKey, + /// Success status success: bool, }, + /// Disconnected from a group Disconnected { + /// Group key key: MessageGroupKey, }, - // sender cid, key, message + /// Message broadcast to a group Message { + /// Sender CID sender: u64, + /// Group key key: MessageGroupKey, + /// Message payload message: SecBuffer, }, - // not actually a "response message", but rather, just like the other response types, just what the server sends to the requesting client + /// Response to a message broadcast MessageResponse { + /// Group key key: MessageGroupKey, + /// Success status success: bool, }, + /// Add members to a group Add { + /// Group key key: MessageGroupKey, + /// CIDs of members to add invitees: Vec, }, + /// Response to adding members to a group AddResponse { + /// Group key key: MessageGroupKey, + /// List of failed invitations failed_to_invite_list: Option>, }, + /// Accept membership in a group AcceptMembership { + /// Target CID target: u64, + /// Group key key: MessageGroupKey, }, + /// Decline membership in a group DeclineMembership { + /// Target CID target: u64, + /// Group key key: MessageGroupKey, }, + /// Response to accepting membership in a group AcceptMembershipResponse { + /// Group key key: MessageGroupKey, + /// Success status success: bool, }, + /// Response to declining membership in a group DeclineMembershipResponse { + /// Group key key: MessageGroupKey, + /// Success status success: bool, }, + /// Kick members from a group Kick { + /// Group key key: MessageGroupKey, + /// CIDs of members to kick kick_list: Vec, }, + /// Response to kicking members from a group KickResponse { + /// Group key key: MessageGroupKey, + /// Success status success: bool, }, + /// List groups for a user ListGroupsFor { + /// User CID cid: u64, }, + /// Response to listing groups for a user ListResponse { + /// List of group keys groups: Vec, }, - /// When relayed to a group owner, the owner is expected to send an - /// AcceptMembership signal + /// Request to join a group RequestJoin { + /// Sender CID sender: u64, + /// Group key key: MessageGroupKey, }, + /// Invitation to join a group Invitation { + /// Sender CID sender: u64, + /// Group key key: MessageGroupKey, }, + /// Response to creating a group CreateResponse { + /// Group key (optional) key: Option, }, + /// Member state changed MemberStateChanged { + /// Group key key: MessageGroupKey, + /// New member state state: MemberState, }, + /// Group does not exist GroupNonExists { + /// Group key key: MessageGroupKey, }, + /// Request to join a group is pending RequestJoinPending { + /// Result of the request result: Result<(), String>, + /// Group key key: MessageGroupKey, }, } -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session_ref.is_server, src = header.session_cid.get(), target = header.target_cid.get())))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session_ref.is_server, src = header.session_cid.get(), target = header.target_cid.get() + ) +))] +/// Process a group broadcast message pub async fn process_group_broadcast( session_ref: &CitadelSession, header: Ref<&[u8], HdpHeader>, @@ -699,6 +816,7 @@ pub async fn process_group_broadcast( } } +/// Create a group channel fn create_group_channel( ticket: Ticket, key: MessageGroupKey, @@ -719,6 +837,7 @@ fn create_group_channel( } impl From for GroupBroadcastPayload { + /// Convert GroupBroadcast to GroupBroadcastPayload fn from(broadcast: GroupBroadcast) -> Self { match broadcast { GroupBroadcast::Message { @@ -731,6 +850,7 @@ impl From for GroupBroadcastPayload { } } +/// Forward a signal to the kernel or a group channel fn forward_signal( session: &CitadelSession, ticket: Ticket, @@ -764,8 +884,7 @@ fn forward_signal( Ok(PrimaryProcessorResult::Void) } -/// Returns None if the implicated_cid is NOT the key's cid. -/// Passing the permission gate requires that the implicated_cid is the key's owning cid +/// Permission gate for group operations fn permission_gate(implicated_cid: u64, key: MessageGroupKey) -> Option<()> { if implicated_cid != key.cid { None diff --git a/citadel_proto/src/proto/packet_processor/peer/mod.rs b/citadel_proto/src/proto/packet_processor/peer/mod.rs index 5aade0a40..a5bc8c40e 100644 --- a/citadel_proto/src/proto/packet_processor/peer/mod.rs +++ b/citadel_proto/src/proto/packet_processor/peer/mod.rs @@ -1,3 +1,31 @@ +//! Peer Packet Processing Module for Citadel Protocol +//! +//! This module provides the core functionality for handling peer-to-peer communication +//! in the Citadel Protocol network. It manages group broadcasts, peer commands, +//! server interactions, and signal handling between peers. +//! +//! # Features +//! +//! - Group broadcast management +//! - Peer command processing +//! - Server interaction handling +//! - Signal processing between peers +//! - Disconnect signal management +//! +//! # Important Notes +//! +//! - Requires established peer connections +//! - Handles peer session state +//! - Manages error propagation +//! - Supports ticket-based operations +//! +//! # Related Components +//! +//! - `CitadelSession`: Manages peer sessions +//! - `NodeResult`: Handles operation results +//! - `Ticket`: Tracks peer operations +//! - `NetworkError`: Manages error states + use crate::error::NetworkError; use crate::prelude::{ConnectFail, NodeResult, Ticket}; use crate::proto::session::CitadelSession; diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index a17242679..88527a52f 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -1,3 +1,67 @@ +//! Peer Command Packet Processor for Citadel Protocol +//! +//! This module handles the processing of peer command packets in the Citadel Protocol +//! network. It manages peer-to-peer communication, signal routing, and secure key +//! exchange between peers through the HyperLAN server. +//! +//! # Features +//! +//! - Peer command processing +//! - Signal routing and forwarding +//! - Secure key exchange +//! - Group broadcast handling +//! - Session state management +//! - Ticket tracking +//! - Error propagation +//! +//! # Important Notes +//! +//! - Requires server mediation +//! - All packets must be authenticated +//! - Handles both client and server roles +//! - Manages peer session states +//! - Supports group operations +//! - Implements error handling +//! +//! # Related Components +//! +//! - `CitadelSession`: Session management +//! - `PeerSignal`: Signal processing +//! - `HyperNodePeerLayerInner`: Peer layer +//! - `StackedRatchet`: Cryptographic operations +//! - `StateContainer`: State management +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::peer::peer_cmd_packet; +//! use citadel_proto::proto::peer::peer_layer::PeerSignal; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! async fn handle_peer_cmd( +//! session: &CitadelSession, +//! packet: HdpPacket, +//! header_version: u32, +//! ) { +//! let aux_cmd = 0; +//! let endpoint_info = None; +//! match peer_cmd_packet::process_peer_cmd( +//! session, +//! aux_cmd, +//! packet, +//! header_version, +//! endpoint_info, +//! ).await { +//! Ok(result) => { +//! // Handle successful peer command +//! } +//! Err(err) => { +//! // Handle peer command error +//! } +//! } +//! } +//! ``` + use std::sync::atomic::Ordering; use bytes::BytesMut; @@ -32,7 +96,7 @@ use crate::proto::peer::peer_layer::{ HyperNodePeerLayerInner, NodeConnectionType, PeerConnectionType, PeerResponse, PeerSignal, }; use crate::proto::remote::Ticket; -use crate::proto::session_manager::HdpSessionManager; +use crate::proto::session_manager::CitadelSessionManager; use crate::proto::state_subcontainers::peer_kem_state_container::PeerKemStateContainer; use netbeam::sync::network_endpoint::NetworkEndpoint; @@ -1018,7 +1082,6 @@ async fn process_signal_command_as_server( }; let mut peer_layer = session.hypernode_peer_layer.inner.write().await; - if let Some(ticket_new) = peer_layer.check_simultaneous_register(implicated_cid, target_cid) { @@ -1678,7 +1741,7 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( timestamp: i64, ticket: Ticket, to_primary_stream: &OutboundPrimaryStreamSender, - sess_mgr: &HdpSessionManager, + sess_mgr: &CitadelSessionManager, sess_hyper_ratchet: &StackedRatchet, security_level: SecurityLevel, peer_layer: &mut HyperNodePeerLayerInner, diff --git a/citadel_proto/src/proto/packet_processor/peer/server/mod.rs b/citadel_proto/src/proto/packet_processor/peer/server/mod.rs index 4cacae26d..2186da8cf 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/mod.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/mod.rs @@ -1,2 +1,29 @@ +//! Server-Side Peer Processing Module for Citadel Protocol +//! +//! This module handles server-side operations for peer connections in the Citadel +//! Protocol network. It manages post-connection and post-registration states for +//! peer sessions on the server side. +//! +//! # Features +//! +//! - Post-connection state management +//! - Post-registration processing +//! - Server-side peer validation +//! - Session state tracking +//! +//! # Important Notes +//! +//! - Server-side operations only +//! - Requires established connections +//! - Manages peer session states +//! - Handles registration completion +//! +//! # Related Components +//! +//! - `post_connect`: Post-connection processing +//! - `post_register`: Post-registration handling +//! - `CitadelSession`: Session management +//! - `StateContainer`: State tracking + pub mod post_connect; pub mod post_register; diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs index 9dc6d5f41..6e98fbc52 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs @@ -1,3 +1,34 @@ +//! Post-Connection Handler for Server-Side Peer Operations +//! +//! This module handles the post-connection phase of peer-to-peer communication on +//! the server side. It manages virtual connection establishment, security settings, +//! and UDP channel setup between connected peers. +//! +//! # Features +//! +//! - Virtual connection establishment +//! - Security level configuration +//! - UDP channel management +//! - Peer signal routing +//! - Session state synchronization +//! - Connection table management +//! +//! # Important Notes +//! +//! - Server-side operations only +//! - Requires established peer sessions +//! - Handles both TCP and UDP channels +//! - Manages bidirectional connections +//! - TODO: Implement disconnect cleanup +//! +//! # Related Components +//! +//! - `CitadelSession`: Session management +//! - `StateContainer`: Connection state +//! - `PeerSignal`: Signal processing +//! - `VirtualConnectionType`: Connection types +//! - `SecurityLevel`: Security settings + use crate::error::NetworkError; use crate::prelude::{PeerConnectionType, PeerResponse, PeerSignal}; use crate::proto::packet_processor::includes::VirtualConnectionType; diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs index 8d5f4fcaa..805110ea6 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs @@ -1,3 +1,33 @@ +//! Post-Registration Handler for Server-Side Peer Operations +//! +//! This module handles the post-registration phase of peer-to-peer communication on +//! the server side. It manages peer registration responses, HyperLAN P2P setup, +//! and registration state management between peers. +//! +//! # Features +//! +//! - Peer registration response handling +//! - HyperLAN P2P registration +//! - Username validation +//! - Signal routing +//! - Ticket management +//! - Response processing +//! +//! # Important Notes +//! +//! - Server-side operations only +//! - Handles registration declines +//! - Asynchronous registration +//! - TODO: Implement error routing +//! +//! # Related Components +//! +//! - `CitadelSession`: Session management +//! - `AccountManager`: Registration handling +//! - `PeerSignal`: Signal processing +//! - `PeerResponse`: Response types +//! - `SecurityLevel`: Security settings + use crate::error::NetworkError; use crate::prelude::{PeerConnectionType, PeerResponse, PeerSignal}; use crate::proto::packet_processor::peer::peer_cmd_packet::route_signal_response; diff --git a/citadel_proto/src/proto/packet_processor/peer/signal_handler_interface.rs b/citadel_proto/src/proto/packet_processor/peer/signal_handler_interface.rs index dea51a65d..465260f22 100644 --- a/citadel_proto/src/proto/packet_processor/peer/signal_handler_interface.rs +++ b/citadel_proto/src/proto/packet_processor/peer/signal_handler_interface.rs @@ -1,3 +1,30 @@ +//! Signal Handler Interface for Citadel Protocol +//! +//! This module defines the interface for handling peer signals in the Citadel Protocol +//! network. It provides a trait-based approach for managing signal flow between peers, +//! including outbound sends, server reception, and target reception. +//! +//! # Features +//! +//! - Async signal handling +//! - Outbound signal management +//! - Server signal processing +//! - Target signal reception +//! - Error propagation +//! +//! # Important Notes +//! +//! - Uses async-trait for async operations +//! - Requires implementation for specific signal types +//! - Handles network error propagation +//! - TODO: Structify PeerSignal for implementation +//! +//! # Related Components +//! +//! - `NetworkError`: Error handling +//! - `PeerSignal`: Signal type (pending structification) +//! - `async_trait`: Async trait support + use crate::error::NetworkError; use async_trait::async_trait; diff --git a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs index 85f90858a..9998fe506 100644 --- a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs @@ -1,3 +1,52 @@ +//! Preconnect packet processor for the Citadel Protocol +//! +//! This module handles the initial connection establishment and NAT traversal process +//! between peers in the Citadel Protocol network. It implements the preconnect handshake +//! which establishes secure communication channels between nodes. +//! +//! # Features +//! +//! - NAT traversal and hole punching for P2P connections +//! - Protocol version compatibility checking +//! - Session state validation and management +//! - Security level negotiation +//! - UDP and QUIC transport support +//! - Cryptographic ratchet initialization +//! +//! # Important Notes +//! +//! - The preconnect process must complete before any other protocol operations +//! - NAT traversal uses configurable STUN servers for hole punching +//! - Protocol version mismatches are currently warned but not enforced +//! - Sessions must be in provisional state to process preconnect packets +//! +//! # Related Components +//! +//! - `StateContainer`: Manages connection state during preconnect +//! - `StackedRatchet`: Provides cryptographic primitives for secure channels +//! - `UdpHolePuncher`: Handles NAT traversal operations +//! - `SessionManager`: Tracks active protocol sessions +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::preconnect_packet; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! async fn handle_preconnect(session: &CitadelSession, packet: HdpPacket) { +//! let header_drill_vers = 1; +//! match preconnect_packet::process_preconnect(session, packet, header_drill_vers).await { +//! Ok(result) => { +//! // Handle successful preconnect processing +//! } +//! Err(err) => { +//! // Handle preconnect error +//! } +//! } +//! } +//! ``` + use citadel_crypt::stacked_ratchet::StackedRatchet; use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; use citadel_wire::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; @@ -478,7 +527,7 @@ pub async fn process_preconnect( // the client gets this. The client must now begin the connect process packet_flags::cmd::aux::do_preconnect::BEGIN_CONNECT => { - log::trace!(target: "citadel", "RECV STAGE BEGIN_CONNECT PRE CONNECT PACKET"); + log::trace!(target: "citadel", "RECV STAGE BEGIN_CONNECT PRE_CONNECT PACKET"); let mut state_container = inner_mut_state!(session.state_container); let hr = return_if_none!( get_proper_hyper_ratchet(header_drill_vers, &state_container, None), diff --git a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs index 012bf702f..b6253e41d 100644 --- a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs +++ b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs @@ -1,3 +1,55 @@ +//! Primary Group Packet Processor for Citadel Protocol +//! +//! This module handles the processing of group packets in the Citadel Protocol network. +//! It manages secure group communication, including message distribution, file transfers, +//! and cryptographic operations for group sessions. +//! +//! # Features +//! +//! - Group packet validation and processing +//! - Secure message distribution +//! - File transfer management +//! - Group session cryptography +//! - Proxy packet handling +//! - UDP and TCP transport support +//! - KEM (Key Encapsulation Mechanism) operations +//! +//! # Important Notes +//! +//! - Group packets require an established session +//! - Supports both direct and proxied communication +//! - Handles TCP-only mode for group payloads +//! - Implements automatic group expiry +//! - Manages cryptographic state for group sessions +//! +//! # Related Components +//! +//! - `StateContainer`: Manages group session state +//! - `StackedRatchet`: Provides cryptographic primitives +//! - `PeerSessionCrypto`: Handles peer-to-peer encryption +//! - `VirtualConnection`: Manages group connections +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::primary_group_packet; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! fn handle_group_packet(session: &CitadelSession, packet: HdpPacket) { +//! let cmd_aux = 0; // Command auxiliary value +//! let proxy_info = None; // No proxy information +//! match primary_group_packet::process_primary_packet(session, cmd_aux, packet, proxy_info) { +//! Ok(result) => { +//! // Handle successful group packet processing +//! } +//! Err(err) => { +//! // Handle group packet error +//! } +//! } +//! } +//! ``` + use super::includes::*; use crate::constants::GROUP_EXPIRE_TIME_MS; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs index b03800251..0e4afc45f 100644 --- a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs +++ b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs @@ -1,3 +1,42 @@ +//! # Raw Primary Packet Processor +//! +//! Low-level packet processing implementation for primary data packets in the +//! Citadel Protocol, handling raw packet operations and transformations. +//! +//! ## Features +//! +//! - **Packet Operations**: +//! - Raw packet parsing +//! - Header processing +//! - Payload handling +//! - Packet validation +//! +//! - **Data Management**: +//! - Zero-copy processing +//! - Buffer management +//! - Memory safety +//! - Efficient allocation +//! +//! - **Security**: +//! - Header integrity +//! - Payload verification +//! - Length validation +//! - Type checking +//! +//! ## Important Notes +//! +//! - Implements memory-safe operations +//! - Handles packet boundaries correctly +//! - Validates packet structure +//! - Ensures data integrity +//! +//! ## Related Components +//! +//! - [`primary_group_packet`]: Group packet processing +//! - [`peer_cmd_packet`]: Peer command packets +//! - [`file_packet`]: File transfer packets +//! - [`session_manager`]: Session management + use bytes::BytesMut; use crate::proto::packet_processor::peer::peer_cmd_packet; @@ -6,7 +45,14 @@ use super::includes::*; use crate::error::NetworkError; /// For primary-port packet types. NOT for wave ports -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(implicated_cid=this_implicated_cid, is_server=session.is_server, packet_len=packet.len())))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(implicated_cid=this_implicated_cid, is_server=session.is_server, packet_len=packet.len()) +))] pub async fn process_raw_packet( this_implicated_cid: Option, session: &CitadelSession, @@ -120,7 +166,27 @@ pub(crate) enum ReceivePortType { UnorderedUnreliable, } -// returns None if the packet should finish being processed. Inlined for slightly faster TURN proxying +/// Checks if the packet should be proxied or not. +/// +/// If the packet should be proxied, it will be sent to the target CID and the function will return `None`. +/// If the packet should not be proxied, it will be returned as is. +/// +/// # Parameters +/// +/// - `this_implicated_cid`: The implicated CID of the current session. +/// - `cmd_primary`: The primary command of the packet. +/// - `cmd_aux`: The auxiliary command of the packet. +/// - `header_session_cid`: The session CID of the packet. +/// - `target_cid`: The target CID of the packet. +/// - `session`: The current session. +/// - `endpoint_cid_info`: The endpoint CID information. +/// - `recv_port_type`: The type of the receive port. +/// - `packet`: The packet to be checked. +/// +/// # Returns +/// +/// - `Some(packet)`: The packet if it should not be proxied. +/// - `None`: If the packet should be proxied. #[allow(clippy::too_many_arguments)] #[inline] pub(crate) fn check_proxy( diff --git a/citadel_proto/src/proto/packet_processor/register_packet.rs b/citadel_proto/src/proto/packet_processor/register_packet.rs index 824d42a86..2099ff612 100644 --- a/citadel_proto/src/proto/packet_processor/register_packet.rs +++ b/citadel_proto/src/proto/packet_processor/register_packet.rs @@ -1,3 +1,57 @@ +//! Registration Packet Processor for Citadel Protocol +//! +//! This module handles the registration process for new clients in the Citadel Protocol +//! network. It implements a secure multi-stage handshake that establishes client +//! identity and sets up initial cryptographic parameters. +//! +//! # Features +//! +//! - Multi-stage registration handshake +//! - Secure key exchange +//! - Passwordless registration support +//! - Session state management +//! - Cryptographic parameter negotiation +//! - Registration failure handling +//! +//! # Important Notes +//! +//! - Requires specific session states (NeedsRegister, SocketJustOpened, NeedsConnect) +//! - Supports both password-based and passwordless registration +//! - Implements post-quantum cryptography +//! - Manages registration state transitions +//! - Validates registration parameters +//! +//! # Related Components +//! +//! - `StateContainer`: Manages registration state +//! - `AccountManager`: Handles account creation +//! - `StackedRatchet`: Provides cryptographic primitives +//! - `SessionManager`: Tracks registration process +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::register_packet; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! use std::net::SocketAddr; +//! +//! async fn handle_register( +//! session: &CitadelSession, +//! packet: HdpPacket, +//! remote_addr: SocketAddr +//! ) { +//! match register_packet::process_register(session, packet, remote_addr).await { +//! Ok(result) => { +//! // Handle successful registration +//! } +//! Err(err) => { +//! // Handle registration error +//! } +//! } +//! } +//! ``` + use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{RegisterFailure, RegisterOkay}; diff --git a/citadel_proto/src/proto/packet_processor/rekey_packet.rs b/citadel_proto/src/proto/packet_processor/rekey_packet.rs index 55b9ae1d8..ac807958e 100644 --- a/citadel_proto/src/proto/packet_processor/rekey_packet.rs +++ b/citadel_proto/src/proto/packet_processor/rekey_packet.rs @@ -1,3 +1,54 @@ +//! Rekey Packet Processor for Citadel Protocol +//! +//! This module implements the key rotation mechanism in the Citadel Protocol network. +//! It manages the secure update of cryptographic keys between nodes to maintain +//! perfect forward secrecy and post-quantum security. +//! +//! # Features +//! +//! - Secure key rotation +//! - Multi-stage key exchange +//! - Post-quantum cryptography +//! - Perfect forward secrecy +//! - State synchronization +//! - Proxy support +//! +//! # Important Notes +//! +//! - Requires connected session state +//! - All packets must be authenticated +//! - Supports both C2S and P2P connections +//! - Manages ratchet state updates +//! - Handles key truncation +//! +//! # Related Components +//! +//! - `StateContainer`: Manages rekey state +//! - `StackedRatchet`: Provides cryptographic primitives +//! - `RatchetUpdateState`: Tracks key updates +//! - `SecurityLevel`: Manages encryption levels +//! +//! # Example Usage +//! +//! ```no_run +//! use citadel_proto::proto::packet_processor::rekey_packet; +//! use citadel_proto::proto::CitadelSession; +//! use citadel_proto::proto::packet::HdpPacket; +//! +//! fn handle_rekey(session: &CitadelSession, packet: HdpPacket) { +//! let header_drill_vers = 1; +//! let proxy_info = None; +//! match rekey_packet::process_rekey(session, packet, header_drill_vers, proxy_info) { +//! Ok(result) => { +//! // Handle successful rekey +//! } +//! Err(err) => { +//! // Handle rekey error +//! } +//! } +//! } +//! ``` + use super::includes::*; use crate::error::NetworkError; use crate::prelude::ReKeyReturnType; diff --git a/citadel_proto/src/proto/packet_processor/udp_packet.rs b/citadel_proto/src/proto/packet_processor/udp_packet.rs index cbe00fba3..bac62a381 100644 --- a/citadel_proto/src/proto/packet_processor/udp_packet.rs +++ b/citadel_proto/src/proto/packet_processor/udp_packet.rs @@ -1,3 +1,32 @@ +//! UDP Packet Processor for Citadel Protocol +//! +//! This module handles the processing of UDP packets in the Citadel Protocol network. +//! It provides secure, unordered data transmission over UDP while maintaining the +//! protocol's security guarantees. +//! +//! # Features +//! +//! - Secure UDP packet processing +//! - Unordered data channel management +//! - Packet validation and authentication +//! - Channel state monitoring +//! - Automatic channel cleanup +//! +//! # Important Notes +//! +//! - Requires an established session +//! - All packets must be authenticated +//! - Handles unordered data transmission +//! - Automatically closes inactive channels +//! - Validates packet headers and payloads +//! +//! # Related Components +//! +//! - `EndpointCryptoAccessor`: Provides cryptographic operations +//! - `StateContainer`: Manages UDP channel state +//! - `SecBuffer`: Handles secure data buffering +//! - `HdpPacket`: Base packet structure + use super::includes::*; use crate::error::NetworkError; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; diff --git a/citadel_proto/src/proto/peer/channel.rs b/citadel_proto/src/proto/peer/channel.rs index cdba78161..618a4aba5 100644 --- a/citadel_proto/src/proto/peer/channel.rs +++ b/citadel_proto/src/proto/peer/channel.rs @@ -1,3 +1,64 @@ +//! # Peer Channel Management +//! +//! This module implements the communication channels between peers in the Citadel Protocol. +//! It provides both TCP and UDP channel implementations with support for secure message passing +//! and WebRTC compatibility. +//! +//! ## Features +//! +//! - **Split Channel Architecture**: Separate send and receive halves for async operations +//! - **Security Level Control**: Configurable security levels for message encryption +//! - **UDP Support**: Dedicated UDP channel implementation for performance +//! - **WebRTC Compatibility**: Optional WebRTC support via feature flag +//! - **Resource Cleanup**: Automatic cleanup on channel drop +//! - **Stream Interface**: Implements Stream trait for async message reception +//! - **Virtual Connection Management**: Supports multiple virtual connections per peer +//! +//! ## Example Usage +//! +//! ```no_run +//! use citadel_proto::peer::channel::PeerChannel; +//! use citadel_types::crypto::SecurityLevel; +//! +//! // Create a new peer channel +//! let channel = PeerChannel::new( +//! server_remote, +//! target_cid, +//! vconn_type, +//! channel_id, +//! SecurityLevel::Standard, +//! is_alive, +//! receiver, +//! outbound_stream +//! ); +//! +//! // Split into send and receive halves +//! let (send_half, recv_half) = channel.split(); +//! +//! // Send a message +//! send_half.send_message(secure_packet)?; +//! +//! // Receive messages asynchronously +//! while let Some(message) = recv_half.next().await { +//! // Process received message +//! } +//! ``` +//! +//! ## Important Notes +//! +//! - Channel drops trigger automatic peer disconnection for P2P connections +//! - WebRTC support requires the "webrtc" feature flag +//! - Channels maintain their own security level settings +//! - UDP channels provide optional WebRTC compatibility layer +//! +//! ## Related Components +//! +//! - [`peer_layer`]: Manages peer connections and routing +//! - [`session`]: Handles session state and management +//! - [`state_container`]: Maintains virtual connection state +//! - [`packet_processor`]: Processes incoming and outgoing packets +//! + use crate::error::NetworkError; use crate::proto::node_request::{NodeRequest, PeerCommand}; use crate::proto::outbound_sender::{OutboundUdpSender, Sender, UnboundedReceiver}; diff --git a/citadel_proto/src/proto/peer/group_channel.rs b/citadel_proto/src/proto/peer/group_channel.rs index c9fc77bda..4c6871eda 100644 --- a/citadel_proto/src/proto/peer/group_channel.rs +++ b/citadel_proto/src/proto/peer/group_channel.rs @@ -1,3 +1,57 @@ +/*! +# Group Channel Module + +This module implements group communication channels in the Citadel Protocol, providing a secure and efficient way to manage group messaging and broadcasts. + +## Features +- **Split Channel Architecture**: Separates send and receive operations for better control +- **Group Broadcast Support**: Enables efficient message broadcasting to group members +- **Permission Management**: Implements group owner privileges and member management +- **Secure Communication**: Uses SecBuffer for encrypted message payloads +- **Stream Interface**: Implements Stream trait for asynchronous message reception + +## Core Components +- `GroupChannel`: Main channel type combining send and receive capabilities +- `GroupChannelSendHalf`: Handles message sending and group management operations +- `GroupChannelRecvHalf`: Manages message reception and implements Stream trait +- `GroupBroadcastPayload`: Represents different types of group messages + +## Example Usage +```rust +// Create a new group channel +let group_channel = GroupChannel::new( + node_remote, + tx, + key, + ticket, + implicated_cid, + recv +); + +// Send a message to the group +group_channel.send_message(message)?; + +// Invite new members +group_channel.invite(peer_cid)?; + +// Split the channel for separate send/receive handling +let (send_half, recv_half) = group_channel.split(); +``` + +## Important Notes +1. Channels can be split into send and receive halves for flexible usage +2. Group owners have special privileges for member management +3. Messages are encrypted using SecBuffer for security +4. Proper cleanup is handled through Drop implementations + +## Related Components +- `peer_layer`: Manages peer-to-peer networking +- `packet_processor`: Handles packet processing and routing +- `session`: Manages connection sessions +- `message_group`: Implements group messaging logic + +*/ + use crate::error::NetworkError; use crate::proto::outbound_sender::{Sender, UnboundedReceiver}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; diff --git a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs index abc6a1fdb..2f837e563 100644 --- a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs +++ b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs @@ -1,3 +1,52 @@ +/*! +# Hole Punch Compatibility Stream Module + +This module implements a compatibility layer for UDP hole punching in the Citadel Protocol, providing a reliable ordered stream interface for NAT traversal. + +## Features +- **Stream Abstraction**: Implements `ReliableOrderedStreamToTarget` for hole punching +- **Compatibility Layer**: Bridges between different network protocols +- **Security Integration**: Incorporates stacked ratchet encryption +- **Packet Routing**: Supports both client-server and peer-to-peer routing +- **State Management**: Maintains connection state and packet ordering + +## Core Components +- `ReliableOrderedCompatStream`: Main stream implementation for hole punching +- `ConnAddr`: Address management for connection endpoints +- `StackedRatchet`: Encryption layer for secure communication + +## Example Usage +```rust +// Create a new compatibility stream +let compat_stream = ReliableOrderedCompatStream::new( + primary_stream, + state_container, + target_cid, + stacked_ratchet, + security_level +); + +// Send data through the stream +compat_stream.send_to_peer(&data)?; + +// Receive data from the stream +let received = compat_stream.recv()?; +``` + +## Important Notes +1. Supports both client-server and peer-to-peer modes +2. Requires pre-loaded ratchets for P2P communication +3. Uses central node for packet routing in P2P mode +4. Maintains packet ordering and reliability + +## Related Components +- `peer_crypt`: Handles encryption and key exchange +- `p2p_conn_handler`: Manages peer connections +- `state_container`: Tracks connection state +- `packet_processor`: Handles packet routing + +*/ + use crate::proto::outbound_sender::{OutboundPrimaryStreamSender, UnboundedReceiver}; use crate::proto::peer::p2p_conn_handler::generic_error; use crate::proto::state_container::StateContainerInner; diff --git a/citadel_proto/src/proto/peer/message_group.rs b/citadel_proto/src/proto/peer/message_group.rs index ec3c118fc..532375261 100644 --- a/citadel_proto/src/proto/peer/message_group.rs +++ b/citadel_proto/src/proto/peer/message_group.rs @@ -1,3 +1,53 @@ +/*! +# Message Group Module + +This module implements the group messaging framework for the Citadel Protocol, providing a consent-based group communication model. + +## Features +- **Axis of Consent Model**: Implements a centralized consent model around the group initiator +- **Peer Management**: Handles both concurrent and pending peer states +- **Group Lifecycle**: Manages group creation, membership, and termination +- **Permission System**: Supports group options and peer-specific permissions +- **Short-lived Messaging Frames**: Optimized for temporary group communications + +## Core Components +- `MessageGroup`: Main structure managing group state and peer membership +- `MessageGroupPeer`: Represents individual peer state and metadata +- `MessageGroupOptions`: Configures group behavior and permissions + +## Example Usage +```rust +// Create a new message group with options +let group = MessageGroup { + concurrent_peers: HashMap::new(), + pending_peers: HashMap::new(), + options: message_group_options, +}; + +// Add a peer to the pending list +let peer = MessageGroupPeer { peer_cid: peer_id }; +group.pending_peers.insert(peer_id, peer); + +// Upgrade a peer from pending to concurrent +if let Some(peer) = group.pending_peers.remove(&peer_id) { + group.concurrent_peers.insert(peer_id, peer); +} +``` + +## Important Notes +1. Groups are centered around an "axis of consent" (the initiator) +2. Consent is not transitive - all members must connect directly to the initiator +3. Groups are short-lived and terminate when the initiator disconnects +4. Local message history persists after group termination + +## Related Components +- `peer_layer`: Manages peer-to-peer networking +- `group_channel`: Implements group communication channels +- `session`: Manages connection sessions +- `state_container`: Tracks connection state + +*/ + use citadel_types::proto::MessageGroupOptions; use std::collections::HashMap; diff --git a/citadel_proto/src/proto/peer/mod.rs b/citadel_proto/src/proto/peer/mod.rs index 8fd52b9ac..3134a9638 100644 --- a/citadel_proto/src/proto/peer/mod.rs +++ b/citadel_proto/src/proto/peer/mod.rs @@ -1,3 +1,52 @@ +/*! +# Peer Module + +This module is the root of the peer-to-peer networking implementation in the Citadel Protocol, organizing various submodules for P2P functionality. + +## Features +- **Complete P2P Stack**: Provides a full peer-to-peer networking solution +- **Modular Architecture**: Separates concerns into specialized submodules +- **Security-First Design**: Implements secure communication patterns +- **NAT Traversal**: Supports various NAT traversal techniques +- **Group Communication**: Enables secure group messaging + +## Submodules +- `peer_layer`: Core peer networking infrastructure and state management +- `channel`: Basic peer-to-peer communication channels +- `group_channel`: Group communication channels +- `peer_crypt`: Cryptographic operations and key exchange +- `message_group`: Group messaging framework +- `p2p_conn_handler`: Direct P2P connection management +- `hole_punch_compat_sink_stream`: NAT traversal compatibility layer + +## Example Usage +```rust +use citadel_proto::proto::peer::{ + channel::PeerChannel, + group_channel::GroupChannel, + peer_layer::HyperNodePeerLayer, +}; + +// Create peer components as needed +let peer_layer = HyperNodePeerLayer::new(persistence_handler); +let channel = PeerChannel::new(/* ... */); +let group = GroupChannel::new(/* ... */); +``` + +## Important Notes +1. All peer communication is encrypted by default +2. NAT traversal is handled automatically +3. Group messaging follows a consent-based model +4. Connection state is managed through the peer layer + +## Related Components +- `session`: Manages connection sessions +- `packet_processor`: Handles packet routing +- `state_container`: Tracks connection state +- `remote`: Manages remote connections + +*/ + /// For managing the state of peer-related activities pub mod peer_layer; diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index f2f8ca67c..d2932e536 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -1,3 +1,64 @@ +/*! +# P2P Connection Handler Module + +This module implements direct peer-to-peer connection handling and NAT traversal functionality for the Citadel Protocol, enabling secure direct connections between peers. + +## Features +- **Direct P2P Connections**: Manages direct peer-to-peer connections with support for both TCP and UDP +- **NAT Traversal**: Implements UDP hole punching for NAT traversal +- **Connection Management**: Handles connection setup, teardown, and error recovery +- **WebRTC Support**: Compatible with WebRTC for web-based peer connections +- **Security**: Integrates with Citadel's security infrastructure for encrypted communications + +## Core Components +- `DirectP2PRemote`: Manages direct P2P connection state and lifecycle +- `P2PInboundHandle`: Handles incoming P2P connections and related state +- `attempt_simultaneous_hole_punch`: Implements NAT traversal via UDP hole punching + +## Example Usage +```rust +// Setup a non-initiator listener +setup_listener_non_initiator( + local_bind_addr, + remote_addr, + session, + v_conn, + hole_punched_addr, + ticket, + udp_mode +)?; + +// Attempt NAT traversal +attempt_simultaneous_hole_punch( + peer_connection_type, + ticket, + session, + peer_nat_info, + implicated_cid, + kernel_tx, + channel_signal, + sync_time, + app, + encrypted_config_container, + client_config, + udp_mode +)?; +``` + +## Important Notes +1. Supports both TCP and UDP connections with automatic fallback +2. Implements symmetric NAT traversal through coordinated hole punching +3. Handles connection cleanup and resource management +4. Integrates with Citadel's session and security infrastructure + +## Related Components +- `peer_layer`: High-level peer networking abstraction +- `peer_crypt`: Handles peer-to-peer encryption +- `session`: Manages connection sessions +- `state_container`: Tracks connection state + +*/ + use citadel_io::tokio::sync::oneshot::{channel, Receiver, Sender}; use citadel_io::tokio_stream::StreamExt; diff --git a/citadel_proto/src/proto/peer/peer_crypt.rs b/citadel_proto/src/proto/peer/peer_crypt.rs index a5626d3ca..a4fbe893c 100644 --- a/citadel_proto/src/proto/peer/peer_crypt.rs +++ b/citadel_proto/src/proto/peer/peer_crypt.rs @@ -1,3 +1,56 @@ +/*! +# Peer Cryptography Module + +This module implements the cryptographic key exchange and NAT traversal functionality for peer-to-peer connections in the Citadel Protocol. + +## Features +- **Key Exchange Protocol**: Implements a multi-stage key exchange process +- **NAT Traversal Support**: Provides NAT type detection and compatibility checking +- **TLS Integration**: Supports TLS domain configuration for secure connections +- **Security Settings**: Configurable security levels and UDP modes +- **STUN/TURN Support**: Automatic TURN server fallback for incompatible NATs + +## Core Components +- `KeyExchangeProcess`: Manages the stages of peer key exchange +- `PeerNatInfo`: Handles NAT-related information and compatibility +- `TlsDomain`: Configures TLS settings for secure connections + +## Example Usage +```rust +// Stage 0: Alice initiates key exchange +let stage0 = KeyExchangeProcess::Stage0( + public_key, + security_settings, + udp_mode +); + +// Stage 1: Bob responds with encrypted data +let stage1 = KeyExchangeProcess::Stage1( + ciphertext, + Some(peer_nat_info), + file_transfer_compatible +); + +// Check NAT compatibility +let (needs_turn, addr) = peer_nat_info.generate_proper_listener_connect_addr( + &local_nat_type +); +``` + +## Important Notes +1. Key exchange follows a three-stage protocol for security +2. NAT compatibility is checked before direct connections +3. TURN servers are used as fallback for incompatible NATs +4. TLS domains ensure secure communication channels + +## Related Components +- `p2p_conn_handler`: Manages peer connections +- `hole_punch_compat_sink_stream`: Handles hole punching +- `session`: Manages connection sessions +- `state_container`: Tracks connection state + +*/ + use crate::proto::node::TlsDomain; use citadel_types::proto::SessionSecuritySettings; use citadel_types::proto::UdpMode; diff --git a/citadel_proto/src/proto/peer/peer_layer.rs b/citadel_proto/src/proto/peer/peer_layer.rs index a39f23bc8..2cb30d2db 100644 --- a/citadel_proto/src/proto/peer/peer_layer.rs +++ b/citadel_proto/src/proto/peer/peer_layer.rs @@ -1,3 +1,53 @@ +/*! +# Peer Layer Module + +This module implements the core peer-to-peer networking layer for the Citadel Protocol, providing high-level abstractions for managing peer connections, message groups, and peer signal routing. + +## Features +- **Peer Signal Management**: Handles routing and tracking of peer signals between nodes +- **Message Group Support**: Implements group messaging functionality with support for concurrent and pending peers +- **Ticket-based Connection Handling**: Uses unique tickets for tracking connection attempts and simultaneous connections +- **Timeout Management**: Provides configurable timeouts for peer operations with callback support +- **HyperLAN Integration**: Specialized support for HyperLAN client communication and state management + +## Core Components +- `HyperNodePeerLayer`: Main interface for peer operations and message group management +- `PeerSignal`: Enum representing different types of peer-to-peer communication signals +- `MessageGroup`: Handles group messaging with support for concurrent and pending peers +- `TrackedPosting`: Tracks peer signal states with timeout support + +## Example Usage +```rust +let peer_layer = HyperNodePeerLayer::new(persistence_handler); + +// Create a new message group +let group_key = peer_layer.create_new_message_group( + implicated_cid, + &initial_peers, + message_group_options +)?; + +// Add peers to the group +peer_layer.add_pending_peers_to_group(group_key, new_peers); + +// Upgrade a peer from pending to concurrent +peer_layer.upgrade_peer_in_group(group_key, peer_cid); +``` + +## Important Notes +1. Peer signals are tracked using unique tickets per session to prevent unintended expiration +2. Message groups support both concurrent (active) and pending (invited) peers +3. The module integrates with HyperLAN for advanced networking capabilities +4. Proper cleanup is handled through the `on_session_shutdown` method + +## Related Components +- `session`: Manages the overall session state +- `packet_processor`: Handles packet processing and routing +- `state_container`: Manages connection state +- `message_group`: Implements group messaging functionality + +*/ + use crate::error::NetworkError; use crate::macros::SyncContextRequirements; use crate::prelude::PreSharedKey; diff --git a/citadel_proto/src/proto/remote.rs b/citadel_proto/src/proto/remote.rs index e6350d1c7..050007cb7 100644 --- a/citadel_proto/src/proto/remote.rs +++ b/citadel_proto/src/proto/remote.rs @@ -1,3 +1,46 @@ +//! # Citadel Protocol Remote Communication +//! +//! This module implements remote communication functionality for the Citadel Protocol. +//! It provides a high-level interface for nodes to communicate with each other and +//! with the server in a secure and efficient manner. +//! +//! ## Features +//! +//! - **Asynchronous Communication**: Non-blocking request/response patterns +//! - **Ticket Management**: Unique identifiers for tracking requests and responses +//! - **Callback Support**: Subscription-based callbacks for event handling +//! - **Error Handling**: Comprehensive error handling for network operations +//! - **Account Management**: Integration with account management system +//! +//! ## Components +//! +//! - **NodeRemote**: Main interface for node communication +//! - **Remote Trait**: Core functionality definition +//! - **Ticket System**: Request tracking and correlation +//! +//! ## Security +//! +//! All communication is secured using: +//! - Post-quantum cryptography +//! - Secure ticket generation +//! - Protected channel establishment +//! +//! ## Usage Example +//! +//! ```no_run +//! use citadel_proto::remote::NodeRemote; +//! use citadel_proto::prelude::NodeRequest; +//! +//! // Create a request +//! let request = NodeRequest::new(); +//! +//! // Send request and get ticket +//! let ticket = remote.send(request)?; +//! +//! // Or use callback subscription +//! let subscription = remote.send_callback_subscription(request)?; +//! ``` + use crate::error::NetworkError; use crate::kernel::kernel_communicator::{ CallbackKey, KernelAsyncCallbackHandler, KernelStreamSubscription, @@ -23,6 +66,7 @@ pub struct NodeRemote { #[async_trait::async_trait] #[auto_impl::auto_impl(Box, &mut, &, Arc)] pub trait Remote: Clone + Send { + /// Sends a request to the server and returns a ticket for tracking the response. async fn send(&self, request: NodeRequest) -> Result { let ticket = self.get_next_ticket(); self.send_with_custom_ticket(ticket, request) @@ -30,16 +74,23 @@ pub trait Remote: Clone + Send { .map(|_| ticket) } + /// Sends a request to the server with a custom ticket. async fn send_with_custom_ticket( &self, ticket: Ticket, request: NodeRequest, ) -> Result<(), NetworkError>; + + /// Sends a request to the server and returns a subscription for callback events. async fn send_callback_subscription( &self, request: NodeRequest, ) -> Result; + + /// Returns the account manager instance. fn account_manager(&self) -> &AccountManager; + + /// Returns the next available ticket. fn get_next_ticket(&self) -> Ticket; } @@ -80,7 +131,7 @@ impl Debug for NodeRemote { } impl NodeRemote { - /// Creates a new [`NodeRemote`] + /// Creates a new [`NodeRemote`] instance. pub(crate) fn new( outbound_send_request_tx: BoundedSender<(NodeRequest, Ticket)>, callback_handler: KernelAsyncCallbackHandler, @@ -98,7 +149,7 @@ impl NodeRemote { } } - /// Especially used to keep track of a conversation (b/c a certain ticket number may be expected) + /// Sends a request to the server with a custom ticket. pub async fn send_with_custom_ticket( &self, ticket: Ticket, @@ -116,8 +167,7 @@ impl NodeRemote { }) } - /// Sends a request to the HDP server. This should always be used to communicate with the server - /// in order to obtain a ticket + /// Sends a request to the server and returns a ticket for tracking the response. pub async fn send(&self, request: NodeRequest) -> Result { let ticket = self.get_next_ticket(); self.send_with_custom_ticket(ticket, request) diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index f736788e7..0697b3102 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -1,3 +1,52 @@ +//! # Citadel Protocol Session Management +//! +//! This module implements the core session management functionality for the Citadel Protocol. +//! A session represents an active connection between two peers and handles all aspects of +//! communication, including encryption, packet processing, and state management. +//! +//! ## Session Lifecycle +//! +//! 1. **Initialization**: A session is created with [`CitadelSession::new`] +//! 2. **Authentication**: The session performs initial authentication and key exchange +//! 3. **Active State**: Handles packet processing and maintains connection state +//! 4. **Termination**: Clean shutdown with proper resource cleanup +//! +//! ## Features +//! +//! - **State Management**: Tracks session state transitions and handles reconnection +//! - **Packet Processing**: Efficient handling of protocol packets +//! - **File Transfer**: Secure file transfer with configurable security levels +//! - **UDP Support**: Optional UDP connectivity for performance-critical operations +//! - **Clean Shutdown**: Proper resource cleanup and connection termination +//! - **Security**: Post-quantum cryptographic primitives for all session data +//! - **Perfect Forward Secrecy**: Maintained through the stacked ratchet protocol +//! - **Session Key Rotation**: Automatic rotation based on configurable parameters +//! - **Memory Security**: Sensitive data is securely zeroed when dropped +//! +//! ## Example Usage +//! +//! ```no_run +//! use citadel_proto::session::{CitadelSession, SessionInitParams}; +//! +//! // Create session parameters +//! let params = SessionInitParams::new() +//! .with_node_type(NodeType::Client) +//! .with_protocol(ConnectProtocol::Tcp); +//! +//! // Initialize the session +//! let (shutdown_tx, session) = CitadelSession::new(params)?; +//! +//! // Execute the session with a network stream +//! session.execute(stream, peer_addr)?; +//! ``` +//! +//! ## Security Considerations +//! +//! - All session data is encrypted using post-quantum cryptographic primitives +//! - Perfect forward secrecy is maintained through the stacked ratchet protocol +//! - Session keys are automatically rotated based on configurable parameters +//! - Memory containing sensitive data is securely zeroed when dropped + use std::fs::File; use std::net::IpAddr; use std::sync::atomic::Ordering; @@ -15,7 +64,6 @@ use citadel_types::proto::UdpMode; use citadel_user::account_manager::AccountManager; use citadel_user::auth::proposed_credentials::ProposedCredentials; use citadel_user::client_account::ClientNetworkAccount; -use citadel_user::network_account::ConnectProtocol; use citadel_wire::hypernode_type::NodeType; use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; use netbeam::time_tracker::TimeTracker; @@ -57,7 +105,7 @@ use crate::proto::packet_processor::{self, PrimaryProcessorResult}; use crate::proto::peer::p2p_conn_handler::P2PInboundHandle; use crate::proto::peer::peer_layer::{HyperNodePeerLayer, PeerSignal}; use crate::proto::remote::{NodeRemote, Ticket}; -use crate::proto::session_manager::HdpSessionManager; +use crate::proto::session_manager::CitadelSessionManager; use crate::proto::session_queue_handler::{ QueueWorkerResult, QueueWorkerTicket, SessionQueueWorker, SessionQueueWorkerHandle, DRILL_REKEY_WORKER, FIREWALL_KEEP_ALIVE, KEEP_ALIVE_CHECKER, PROVISIONAL_CHECKER, @@ -80,6 +128,7 @@ use citadel_types::proto::SessionSecuritySettings; use citadel_types::proto::TransferType; use citadel_types::proto::VirtualObjectMetadata; use citadel_user::backend::PersistenceHandler; +use citadel_user::prelude::ConnectProtocol; use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::exports::Connection; use citadel_wire::nat_identification::NatType; @@ -87,7 +136,6 @@ use std::ops::Deref; use std::path::PathBuf; use std::pin::Pin; use std::time::{SystemTime, UNIX_EPOCH}; - //use crate::define_struct; // Defines the primary structure which wraps the inner device @@ -186,7 +234,7 @@ pub struct CitadelSessionInner { pub(super) kernel_tx: UnboundedSender, pub(super) to_primary_stream: DualLateInit>, // Setting this will determine what algorithm is used during the DO_CONNECT stage - pub(super) session_manager: HdpSessionManager, + pub(super) session_manager: CitadelSessionManager, pub(super) state: Arc>, pub(super) state_container: StateContainer, pub(super) account_manager: AccountManager, @@ -244,11 +292,11 @@ pub enum HdpSessionInitMode { pub(crate) struct SessionInitParams { pub on_drop: UnboundedSender<()>, pub local_nat_type: NatType, - pub hdp_remote: NodeRemote, + pub citadel_remote: NodeRemote, pub local_bind_addr: SocketAddr, pub local_node_type: NodeType, pub kernel_tx: UnboundedSender, - pub session_manager: HdpSessionManager, + pub session_manager: crate::proto::session_manager::CitadelSessionManager, pub account_manager: AccountManager, pub time_tracker: TimeTracker, pub remote_peer: SocketAddr, @@ -340,7 +388,7 @@ impl CitadelSession { let kernel_ticket = session_init_params.init_ticket; let remote_peer = session_init_params.remote_peer; let session_manager = session_init_params.session_manager; - let hdp_remote = session_init_params.hdp_remote; + let hdp_remote = session_init_params.citadel_remote; let session_security_settings = client_only_settings.as_ref().map(|r| r.security_settings); let udp_mode = client_only_settings .as_ref() diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index a16cb11bf..d76238b8b 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -1,3 +1,39 @@ +//! # Session Manager for Citadel Protocol +//! +//! The Session Manager is responsible for handling and maintaining stateful connections between peers +//! in the Citadel Protocol. It manages both client-server and peer-to-peer connections, handling +//! session establishment, maintenance, and termination. +//! +//! ## Features +//! +//! * Session lifecycle management (creation, maintenance, termination) +//! * Handles both provisional and established connections +//! * Manages peer-to-peer communication and group broadcasts +//! * Supports secure file transfers with configurable security levels +//! * Implements connection upgrades from provisional to full sessions +//! * Provides message group functionality for group communications +//! * Handles virtual connections and connection state transitions +//! * Supports both UDP and TCP transport protocols +//! +//! ## Important Notes +//! +//! * Session management is thread-safe and handles concurrent connections +//! * Sessions are identified by unique CIDs (Connection IDs) +//! * Provisional connections are temporary and must be upgraded to full sessions +//! * Clean shutdown procedures ensure proper resource cleanup +//! * Implements timeout mechanisms for connection management +//! +//! ## Related Components +//! +//! * `CitadelSession`: Handles individual session state and operations +//! * `HyperNodePeerLayer`: Manages peer-to-peer communications +//! * `GroupBroadcast`: Implements group messaging functionality +//! * `PeerSignal`: Handles peer-to-peer signaling +//! * `NodeRemote`: Manages remote node connections +//! * `packet_processor`: Processes various packet types +//! * `AccountManager`: Manages user authentication and credentials +//! + use std::collections::HashMap; use std::net::SocketAddr; use std::path::PathBuf; @@ -52,7 +88,7 @@ use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::exports::tokio_rustls::rustls::ClientConfig; use std::sync::Arc; -define_outer_struct_wrapper!(HdpSessionManager, HdpSessionManagerInner); +define_outer_struct_wrapper!(CitadelSessionManager, HdpSessionManagerInner); /// Used for handling stateful connections between two peer pub struct HdpSessionManagerInner { @@ -64,7 +100,7 @@ pub struct HdpSessionManagerInner { incoming_cxn_count: usize, /// Connections which have no implicated CID go herein. They are strictly expected to be /// in the state of NeedsRegister. Once they leave that state, they are eventually polled - /// by the [HdpSessionManager] and thereafter placed inside an appropriate session + /// by the [CitadelSessionManager] and thereafter placed inside an appropriate session pub provisional_connections: HashMap, CitadelSession)>, kernel_tx: UnboundedSender, time_tracker: TimeTracker, @@ -74,7 +110,7 @@ pub struct HdpSessionManagerInner { stun_servers: Option>, } -impl HdpSessionManager { +impl CitadelSessionManager { /// Creates a new [SessionManager] which handles individual connections pub fn new( local_node_type: NodeType, @@ -296,7 +332,7 @@ impl HdpSessionManager { local_nat_type, remote_peer: peer_addr, on_drop, - hdp_remote: remote, + citadel_remote: remote, local_bind_addr, local_node_type, kernel_tx, @@ -351,7 +387,7 @@ impl HdpSessionManager { ) ))] async fn execute_session_with_safe_shutdown( - session_manager: HdpSessionManager, + session_manager: CitadelSessionManager, new_session: CitadelSession, peer_addr: SocketAddr, tcp_stream: GenericNetworkStream, @@ -471,12 +507,11 @@ impl HdpSessionManager { } } - // This future should be joined up higher at the [HdpServer] layer + /// This future should be joined up higher at the [node] layer pub async fn run_peer_container( - hdp_session_manager: HdpSessionManager, + citadel_session_manager: CitadelSessionManager, ) -> Result<(), NetworkError> { - let peer_container = { inner!(hdp_session_manager).hypernode_peer_layer.clone() }; - + let peer_container = { inner!(citadel_session_manager).hypernode_peer_layer.clone() }; peer_container.create_executor().await.await } @@ -509,7 +544,7 @@ impl HdpSessionManager { let session_init_params = SessionInitParams { on_drop, local_nat_type, - hdp_remote: remote, + citadel_remote: remote, local_bind_addr, local_node_type, kernel_tx: this.kernel_tx.clone(), diff --git a/citadel_proto/src/proto/session_queue_handler.rs b/citadel_proto/src/proto/session_queue_handler.rs index 115219e1e..e817bf06d 100644 --- a/citadel_proto/src/proto/session_queue_handler.rs +++ b/citadel_proto/src/proto/session_queue_handler.rs @@ -1,3 +1,34 @@ +//! # Session Queue Handler +//! +//! This module implements a queue-based task scheduling system for Citadel Protocol sessions. +//! It manages timed operations, periodic checks, and session-specific tasks using a delay queue. +//! +//! ## Features +//! +//! - **Task Scheduling**: Manages scheduled tasks with customizable timeouts +//! - **Reserved System Processes**: Dedicated slots for critical system operations +//! - **One-shot Tasks**: Support for single-execution future tasks +//! - **State Management**: Integration with session state container +//! - **Async Stream Interface**: Implements Stream and Future traits +//! +//! ## Task Types +//! +//! - **Reserved Tasks** (indices 0-9): System-level operations like keep-alive checks +//! - **Ordinary Tasks** (indices 10+): Session-specific operations like group timeouts +//! - **One-shot Tasks**: Single-execution tasks with custom timeouts +//! +//! ## Important Notes +//! +//! - Reserved indices below 10 are preserved for system operations +//! - Task execution is managed through a DelayQueue system +//! - Supports both blocking and non-blocking task execution +//! +//! ## Related Components +//! +//! - [`SessionState`]: Session state management +//! - [`StateContainer`]: Session state storage +//! - [`NetworkError`]: Error handling +//! use crate::error::NetworkError; use crate::proto::packet_processor::includes::Duration; use crate::proto::session::SessionState; diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index c66679c17..c69b2dd7e 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -1,3 +1,74 @@ +//! # Citadel Protocol State Management +//! +//! This module implements the state management system for the Citadel Protocol. +//! It handles connection states, virtual connections, group messaging, file transfers, +//! and maintains the overall protocol state machine. +//! +//! ## State Management +//! +//! The state container manages several types of states: +//! +//! - **Connection States**: Pre-connect, connect, register, and deregister states +//! - **Virtual Connections**: Peer-to-peer and client-server connections +//! - **Group Management**: Group channels and broadcast messaging +//! - **File Transfers**: Both inbound and outbound file transfers +//! +//! ## Features +//! +//! - **Virtual Connections**: Manages peer-to-peer and client-server connections +//! - **State Machine**: Handles protocol state transitions +//! - **Group Messaging**: Supports secure group communication +//! - **File Transfer**: Manages secure file transfers with progress tracking +//! - **UDP Support**: Optional UDP connectivity for performance +//! +//! ## Implementation Details +//! +//! The state container is split into several components: +//! +//! 1. **State Container**: Main state management interface +//! 2. **Virtual Connections**: Connection management +//! 3. **Group Management**: Group messaging and channels +//! 4. **File Transfer**: File transfer state tracking +//! +//! ## Security +//! +//! - All state transitions are cryptographically verified +//! - Connection states are protected against replay attacks +//! - Group keys are securely managed +//! - File transfers are encrypted end-to-end +//! +//! ## Example Usage +//! +//! ```no_run +//! use citadel_proto::state_container::{StateContainer, VirtualConnectionType}; +//! +//! // Create a new state container +//! let container = StateContainer::create( +//! kernel_tx, +//! remote, +//! timeout, +//! state, +//! account, +//! tracker, +//! settings, +//! is_server, +//! stats, +//! udp_mode, +//! ); +//! +//! // Handle a virtual connection +//! container.insert_new_peer_virtual_connection_as_endpoint( +//! peer_addr, +//! settings, +//! ticket, +//! target_cid, +//! conn_type, +//! crypto, +//! session, +//! true, +//! )?; +//! ``` + use std::collections::{HashMap, VecDeque}; use std::fmt::{Debug, Display, Formatter}; use std::ops::RangeInclusive; diff --git a/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs index 0b6674e7d..66b04c9db 100644 --- a/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs @@ -1,3 +1,43 @@ +//! # Connect State Container +//! +//! Manages the state of active connections in the Citadel Protocol, tracking connection stages, +//! credentials, and timing information. +//! +//! ## Features +//! - Stage-based connection management with transition tracking +//! - Credential handling for authentication processes +//! - Connection timing and failure monitoring +//! - Support for different connection modes +//! - State recovery mechanisms for connection resilience +//! +//! ## Example Usage +//! ```rust +//! use citadel_proto::proto::state_subcontainers::ConnectState; +//! +//! // Create new connection state +//! let mut state = ConnectState::default(); +//! +//! // Handle successful connection +//! state.on_success(); +//! state.on_connect_packet_received(); +//! +//! // Handle connection failure +//! state.on_fail(); +//! state.on_connect_packet_received(); +//! ``` +//! +//! ## Important Notes +//! - State transitions must update both local and global session state +//! - Packet timing is tracked for timeout management +//! - Connection modes affect behavior and security settings +//! - Failure states include timing information for recovery +//! +//! ## Related Components +//! - `packet_processor`: Uses connection states for packet handling +//! - `session`: Manages overall session state +//! - `peer`: Uses connection states for peer management +//! - `remote`: Handles remote connection states + use citadel_io::tokio::time::Instant; use crate::proto::packet::packet_flags; diff --git a/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs b/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs index 35188a9de..062095b5c 100644 --- a/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs @@ -1,3 +1,35 @@ +//! # Deregistration State Container +//! +//! Manages the state of account deregistration processes in the Citadel Protocol. +//! +//! ## Features +//! - Tracks deregistration process state and progress +//! - Manages deregistration tickets for process identification +//! - Tracks timing information for deregistration operations +//! - Provides atomic state transitions for thread safety +//! +//! ## Example Usage +//! ```rust +//! use citadel_proto::proto::state_subcontainers::DeRegisterState; +//! use citadel_proto::proto::remote::Ticket; +//! +//! let mut state = DeRegisterState::default(); +//! let ticket = Ticket::default(); +//! +//! // Initialize deregistration process +//! state.on_init(timestamp, ticket); +//! ``` +//! +//! ## Important Notes +//! - State must be properly initialized before use +//! - Timing information is critical for process tracking +//! - Ticket management ensures process uniqueness +//! +//! ## Related Components +//! - `session`: Uses deregistration states for account management +//! - `remote`: Handles remote deregistration operations +//! - `validation`: Validates deregistration requests + use crate::proto::remote::Ticket; /// For keeping track of deregistration processes diff --git a/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs b/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs index 095ed7722..dd843fe0f 100644 --- a/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs @@ -1,3 +1,41 @@ +//! # Meta Expiry State Container +//! +//! Manages expiration state for high-traffic packet processing scenarios in the Citadel Protocol. +//! Prevents false expiration of active groups during high workload conditions. +//! +//! ## Features +//! - Tracks packet processing events for group activity +//! - Prevents premature group expiration under high load +//! - Supports both inbound and outbound traffic monitoring +//! - Handles file transfer expiry tracking +//! - Provides adaptive expiry timing based on workload +//! +//! ## Example Usage +//! ```rust +//! use citadel_proto::proto::state_subcontainers::MetaExpiryState; +//! +//! let mut state = MetaExpiryState::default(); +//! +//! // Update state on packet confirmation +//! state.on_event_confirmation(); +//! +//! // Check if expired +//! if state.expired() { +//! // Handle expiration +//! } +//! ``` +//! +//! ## Important Notes +//! - Critical for high-traffic workload scenarios +//! - Prevents false expiration during async processing delays +//! - Maintains group and file transfer reliability +//! - Uses constant GROUP_EXPIRE_TIME_MS for timing +//! +//! ## Related Components +//! - `group_channel`: Uses expiry state for group management +//! - `packet_processor`: Integrates with packet processing flow +//! - `file_transfer`: Uses expiry state for file operations + use crate::constants::GROUP_EXPIRE_TIME_MS; use crate::proto::packet_processor::includes::Instant; diff --git a/citadel_proto/src/proto/state_subcontainers/mod.rs b/citadel_proto/src/proto/state_subcontainers/mod.rs index 768c3360d..697e28b84 100644 --- a/citadel_proto/src/proto/state_subcontainers/mod.rs +++ b/citadel_proto/src/proto/state_subcontainers/mod.rs @@ -1,3 +1,58 @@ +/*! +# State Subcontainers Module + +This module implements specialized state containers for managing different aspects of connection lifecycle in the Citadel Protocol. + +## Features +- **Connection State Management**: Tracks various connection states and transitions +- **Key Exchange Management**: Handles cryptographic key exchange states +- **Registration Flow**: Manages user registration and authentication states +- **State Persistence**: Maintains state across connection phases +- **Timeout Handling**: Manages timeouts and expiry for different states + +## Submodules +- `connect_state_container`: Manages active connection states +- `peer_kem_state_container`: Handles peer key exchange states +- `preconnect_state_container`: Manages pre-connection setup +- `register_state_container`: Handles registration states +- `deregister_state_container`: Manages deregistration process +- `rekey_container`: Handles key rotation states +- `meta_expiry_container`: Manages state expiration + +## Example Usage +```rust +use citadel_proto::proto::state_subcontainers::{ + ConnectState, + RegisterState, + PreConnectState, +}; + +// Initialize connection state +let mut connect_state = ConnectState::default(); +connect_state.on_connect_packet_received(); + +// Handle registration +let mut register_state = RegisterState::default(); +register_state.on_register_packet_received(); + +// Manage pre-connection +let pre_connect = PreConnectState::default(); +``` + +## Important Notes +1. Each state container manages a specific phase of connection +2. State transitions should be handled atomically +3. Proper cleanup is essential for state containers +4. Timeouts are used to prevent stale states + +## Related Components +- `session`: Uses state containers for session management +- `packet_processor`: Interacts with states during packet processing +- `peer`: Uses states for peer connection management +- `remote`: Manages remote connection states + +*/ + pub mod connect_state_container; pub mod deregister_state_container; pub mod meta_expiry_container; diff --git a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs index 7a6082676..104ccd066 100644 --- a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs @@ -1,3 +1,55 @@ +/*! +# Peer Key Exchange Management State Container Module + +This module manages the state of peer-to-peer key exchange processes in the Citadel Protocol, handling cryptographic operations and security settings. + +## Features +- **Key Exchange State**: Manages key exchange constructor state +- **Security Settings**: Handles session security configuration +- **UDP Channel Management**: Controls UDP communication channels +- **Initiator Role**: Tracks local peer's role in key exchange +- **Session Security**: Manages session passwords and security + +## Core Components +- `PeerKemStateContainer`: Main structure for key exchange state +- `StackedRatchetConstructor`: Handles cryptographic key construction +- `SessionSecuritySettings`: Configures security parameters +- `UdpChannelSender`: Manages UDP channel communication + +## Example Usage +```rust +use citadel_proto::proto::state_subcontainers::PeerKemStateContainer; + +// Create new key exchange state +let state = PeerKemStateContainer::new( + security_settings, + udp_enabled, + session_password +); + +// Access security settings +let settings = state.session_security_settings; + +// Check if local peer is initiator +if state.local_is_initiator { + // Handle initiator-specific logic +} +``` + +## Important Notes +1. Key exchange state is critical for secure communication +2. UDP channels are optional based on configuration +3. Security settings determine encryption strength +4. Session passwords must be properly managed + +## Related Components +- `peer_crypt`: Handles peer encryption +- `session`: Manages session security +- `packet_processor`: Uses key exchange for packets +- `state_container`: Parent state management + +*/ + use crate::prelude::PreSharedKey; use crate::proto::state_subcontainers::preconnect_state_container::UdpChannelSender; use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; diff --git a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs index 11bc100ac..960962e6c 100644 --- a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs @@ -1,3 +1,56 @@ +/*! +# Pre-connection State Container Module + +This module manages the state of pre-connection setup in the Citadel Protocol, handling initial handshakes and cryptographic setup. + +## Features +- **Stage Tracking**: Manages pre-connection stages +- **Node Type Management**: Handles different node types +- **Cryptographic Setup**: Manages ratchet construction +- **UDP Channel Setup**: Handles UDP channel initialization +- **Ticket Management**: Tracks connection tickets + +## Core Components +- `PreConnectState`: Main structure for pre-connection state +- `UdpChannelSender`: Manages UDP channel communication +- `StackedRatchetConstructor`: Handles cryptographic setup +- `NodeType`: Configures node behavior + +## Example Usage +```rust +use citadel_proto::proto::state_subcontainers::PreConnectState; + +// Create new pre-connection state +let mut state = PreConnectState::default(); + +// Handle packet reception +state.on_packet_received(); + +// Check connection success +if state.success { + // Handle successful pre-connection +} + +// Access generated ratchet +if let Some(ratchet) = state.generated_ratchet { + // Use the ratchet for encryption +} +``` + +## Important Notes +1. Pre-connection state is critical for secure setup +2. UDP channels are managed through oneshot channels +3. Ratchet construction must complete before connection +4. Node types affect connection behavior + +## Related Components +- `connect_state_container`: Handles main connection +- `peer_kem_state_container`: Manages key exchange +- `packet_processor`: Uses pre-connection state +- `state_container`: Parent state management + +*/ + use crate::proto::packet_processor::includes::Instant; use crate::proto::peer::channel::UdpChannel; use crate::proto::remote::Ticket; diff --git a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs index 8b89112a9..b82cf4e55 100644 --- a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs @@ -1,3 +1,52 @@ +/*! +# Registration State Container Module + +This module manages the state of user registration processes in the Citadel Protocol, handling registration stages and cryptographic setup. + +## Features +- **Stage Management**: Tracks registration process stages +- **Cryptographic Setup**: Manages ratchet construction +- **Timing Control**: Monitors registration packet timing +- **Failure Handling**: Manages registration failures +- **Passwordless Support**: Handles passwordless registration + +## Core Components +- `RegisterState`: Main structure for registration state +- `StackedRatchetConstructor`: Handles cryptographic setup +- `StackedRatchet`: Provides secure communication +- `packet_flags`: Defines registration stages + +## Example Usage +```rust +use citadel_proto::proto::state_subcontainers::RegisterState; + +// Create new registration state +let mut state = RegisterState::default(); + +// Handle registration packet +state.on_register_packet_received(); + +// Handle registration failure +state.on_fail(); + +// Create state from specific stage +let stage_state = RegisterState::from(stage_number); +``` + +## Important Notes +1. Registration stages must follow proper sequence +2. Cryptographic setup is essential for security +3. Packet timing is tracked for timeout handling +4. Failed registrations clear cryptographic state + +## Related Components +- `connect_state_container`: Handles post-registration connection +- `peer_kem_state_container`: Manages key exchange +- `packet_processor`: Uses registration state +- `state_container`: Parent state management + +*/ + use citadel_io::tokio::time::Instant; use crate::proto::packet::packet_flags; diff --git a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs index 19955e9fe..154b87c3b 100644 --- a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs @@ -1,3 +1,41 @@ +//! # Rekey State Container +//! +//! Manages cryptographic key rotation and ratchet updates in the Citadel Protocol. +//! Provides security level-based key update scheduling and state management. +//! +//! ## Features +//! - Manages stacked ratchet construction for key rotation +//! - Supports peer-to-peer key updates +//! - Handles local rekey requests and notifications +//! - Provides configurable security levels +//! - Implements adaptive update frequency based on security needs +//! +//! ## Example Usage +//! ```rust +//! use citadel_proto::proto::state_subcontainers::RatchetUpdateState; +//! use citadel_proto::proto::transfer_stats::TransferStats; +//! +//! // Create new ratchet update state +//! let mut state = RatchetUpdateState::default(); +//! +//! // Calculate update frequency based on security level +//! let stats = TransferStats::default(); +//! let frequency = calculate_update_frequency(2, &stats); +//! ``` +//! +//! ## Important Notes +//! - Security levels range from 0 (low) to 4 (divine) +//! - Update frequencies are in nanoseconds +//! - P2P updates are tracked per connection +//! - Manual mode requires kernel notification +//! - Completion status is reported for local requests +//! +//! ## Related Components +//! - `stacked_ratchet`: Core cryptographic ratchet implementation +//! - `session`: Uses rekey state for session security +//! - `peer`: Manages peer-to-peer rekey operations +//! - `kernel`: Receives rekey completion notifications + use citadel_io::tokio::time::Duration; use crate::constants::{ diff --git a/citadel_proto/src/proto/transfer_stats.rs b/citadel_proto/src/proto/transfer_stats.rs index 434a3e4cb..3a9ce280d 100644 --- a/citadel_proto/src/proto/transfer_stats.rs +++ b/citadel_proto/src/proto/transfer_stats.rs @@ -1,3 +1,39 @@ +//! Transfer Statistics Tracking Module +//! +//! This module provides functionality for tracking and calculating network transfer statistics +//! including transfer rates, jitter, and total bytes transferred. +//! +//! # Features +//! - Tracks transfer rates with nanosecond precision +//! - Calculates transfer rate jitter (rate of change in transfer speed) +//! - Maintains running totals of plaintext bytes sent +//! - Thread-safe statistics accumulation +//! +//! # Usage +//! ```rust +//! use citadel_proto::proto::transfer_stats::TransferStats; +//! +//! // Create initial stats +//! let mut stats = TransferStats::new(current_timestamp_ns, bytes_sent); +//! +//! // Update stats with new measurement +//! let new_stats = TransferStats::new(new_timestamp_ns, new_bytes_sent); +//! stats += new_stats; +//! +//! // Access calculated statistics +//! println!("Transfer rate: {} bytes/sec", stats.transfer_rate); +//! println!("Total bytes sent: {}", stats.total_plaintext_bytes_sent); +//! ``` +//! +//! # Important Notes +//! - Timestamps are in nanoseconds for high precision rate calculations +//! - Transfer rates are calculated in bytes per second +//! - Uses wrapping addition for total bytes to handle potential overflows +//! +//! # Related Components +//! - Used in conjunction with packet handling and session management modules +//! - Integral part of the protocol's performance monitoring system + use std::fmt::{Display, Formatter}; use std::ops::AddAssign; diff --git a/citadel_proto/src/proto/validation.rs b/citadel_proto/src/proto/validation.rs index c66e60553..8b6cece6b 100644 --- a/citadel_proto/src/proto/validation.rs +++ b/citadel_proto/src/proto/validation.rs @@ -1,3 +1,35 @@ +//! # Citadel Protocol Packet Validation +//! +//! This module provides packet validation functionality for the Citadel Protocol. +//! It ensures packet integrity, authenticity, and proper formatting across different +//! packet types and protocol stages. +//! +//! ## Features +//! +//! - **Connection Validation**: Validates connection establishment packets +//! - **Group Packet Validation**: Validates group messaging and file transfer packets +//! - **Registration Validation**: Validates user registration process packets +//! - **AEAD Validation**: Ensures packet integrity using AEAD cryptography +//! - **Header Validation**: Validates packet headers and structure +//! - **Drill Update Validation**: Validates key rotation packets +//! - **File Transfer Validation**: Validates file transfer protocol packets +//! +//! ## Important Notes +//! +//! - All validation functions are security-critical +//! - Failed validation results in packet rejection +//! - Uses AEAD for packet integrity verification +//! - Implements zero-copy validation where possible +//! - Maintains protocol state consistency +//! +//! ## Related Components +//! +//! - [`packet`]: Core packet structure definitions +//! - [`packet_crafter`]: Packet creation functionality +//! - [`state_container`]: Protocol state management +//! - [`session`]: Session management +//! - [`crypto`]: Cryptographic operations +//! pub(crate) mod do_connect { use citadel_user::client_account::ClientNetworkAccount; @@ -186,7 +218,6 @@ pub(crate) mod do_register { } pub(crate) mod do_drill_update { - use crate::proto::packet_crafter::do_drill_update::{ Stage1UpdatePacket, TruncateAckPacket, TruncatePacket, }; @@ -220,7 +251,7 @@ pub(crate) mod pre_connect { use crate::proto::packet::HdpPacket; use crate::proto::packet_crafter::pre_connect::{PreConnectStage0, SynPacket}; use crate::proto::packet_processor::includes::packet_crafter::pre_connect::SynAckPacket; - use crate::proto::session_manager::HdpSessionManager; + use crate::proto::session_manager::CitadelSessionManager; use citadel_crypt::stacked_ratchet::constructor::{ BobToAliceTransfer, BobToAliceTransferType, StackedRatchetConstructor, }; @@ -246,7 +277,7 @@ pub(crate) mod pre_connect { pub(crate) fn validate_syn( cnac: &ClientNetworkAccount, packet: HdpPacket, - session_manager: &HdpSessionManager, + session_manager: &CitadelSessionManager, session_password: &PreSharedKey, ) -> Result { // TODO: NOTE: This can interrupt any active session's. This should be moved up after checking the connect mode diff --git a/citadel_sdk/src/backend_kv_store.rs b/citadel_sdk/src/backend_kv_store.rs index 0b37ab9b2..22c11c5cc 100644 --- a/citadel_sdk/src/backend_kv_store.rs +++ b/citadel_sdk/src/backend_kv_store.rs @@ -1,3 +1,42 @@ +//! Connection-Specific Key-Value Storage +//! +//! This module provides a trait for persistent key-value storage that is unique to each +//! connection in the Citadel Protocol. It allows applications to store and retrieve +//! arbitrary data associated with specific peer connections. +//! +//! # Features +//! - Connection-scoped storage (unique per session and peer) +//! - Basic key-value operations (get, set, remove) +//! - Bulk operations for managing multiple key-value pairs +//! - Automatic error handling and conversion +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! +//! async fn store_data(handler: &T) -> Result<(), NetworkError> { +//! // Store a value +//! handler.set("my_key", b"my_value".to_vec()).await?; +//! +//! // Retrieve the value +//! if let Some(value) = handler.get("my_key").await? { +//! println!("Retrieved value: {:?}", value); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! - Storage is tied to the connection's session and peer IDs +//! - All operations are asynchronous and may fail +//! - Values are stored as raw bytes (Vec) +//! +//! # Related Components +//! - [`TargetLockedRemote`]: Trait for accessing connection-specific information +//! - [`PersistenceHandler`]: Backend storage implementation +//! + use crate::prelude::*; use std::collections::HashMap; diff --git a/citadel_sdk/src/builder/mod.rs b/citadel_sdk/src/builder/mod.rs index c3a14613a..09986875d 100644 --- a/citadel_sdk/src/builder/mod.rs +++ b/citadel_sdk/src/builder/mod.rs @@ -1 +1,20 @@ +//! Builder Module for Citadel Protocol Configuration +//! +//! This module provides builder patterns for configuring and constructing various +//! components of the Citadel Protocol. The builders ensure type-safe and validated +//! configuration of network nodes and related components. +//! +//! # Features +//! - Type-safe configuration building +//! - Validation of component configurations +//! - Flexible node setup for different network roles +//! +//! # Components +//! - [`node_builder`]: Builder for configuring and constructing network nodes +//! +//! # Related Modules +//! - [`citadel_proto::kernel`]: Core networking kernel implementation +//! - [`citadel_proto::proto`]: Protocol implementation details +//! + pub mod node_builder; diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs index d2d8a0b80..6732387c2 100644 --- a/citadel_sdk/src/builder/node_builder.rs +++ b/citadel_sdk/src/builder/node_builder.rs @@ -1,3 +1,40 @@ +//! Node Builder API for Citadel Protocol +//! +//! This module provides a builder pattern interface for constructing and configuring Citadel network nodes. +//! The builder supports creating both peer and server nodes with customizable settings for security, +//! networking, and backend storage. +//! +//! # Features +//! - Flexible node type configuration (Peer/Server) +//! - Multiple backend storage options (In-memory, Filesystem, SQL with enterprise feature) +//! - Customizable security settings (TLS, certificates) +//! - Google services integration (optional) +//! - STUN server configuration for NAT traversal +//! - Server authentication via pre-shared keys +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use std::net::SocketAddr; +//! use std::str::FromStr; +//! +//! // Create a basic server node +//! let builder = NodeBuilder::default() +//! .with_node_type(NodeType::Server(SocketAddr::from_str("0.0.0.0:25021").unwrap())) +//! .with_backend(BackendType::InMemory); +//! ``` +//! +//! # Important Notes +//! - Server nodes require a valid bind address +//! - Default backend is filesystem-based when the "filesystem" feature is enabled +//! - TLS is enabled by default with self-signed certificates +//! - When using Google services, both services JSON and database config must be set +//! +//! # Related Components +//! - [`NetKernel`]: Core networking kernel that processes node operations +//! - [`KernelExecutor`]: Executor for running the network kernel +//! - [`BackendType`]: Storage backend configurations + use citadel_proto::prelude::*; use citadel_proto::kernel::KernelExecutorArguments; diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index 763549b17..0f9ea309c 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -1,3 +1,49 @@ +//! Remote Encrypted Virtual Filesystem (RE-VFS) +//! +//! This module provides high-level operations for interacting with the Remote Encrypted +//! Virtual Filesystem in the Citadel Protocol. RE-VFS enables secure storage and +//! retrieval of files with end-to-end encryption. +//! +//! # Features +//! - Secure file storage and retrieval +//! - Configurable security levels +//! - Automatic encryption handling +//! - Virtual path management +//! - File deletion support +//! - Take operations for atomic reads +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::fs; +//! +//! async fn store_file(remote: &impl TargetLockedRemote) -> Result<(), NetworkError> { +//! // Write a file to RE-VFS +//! fs::write(remote, "/local/file.txt", "/virtual/file.txt").await?; +//! +//! // Read the file back +//! let local_path = fs::read(remote, "/virtual/file.txt").await?; +//! +//! // Delete the file +//! fs::delete(remote, "/virtual/file.txt").await?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! - All operations are end-to-end encrypted +//! - Security levels are configurable per operation +//! - Take operations atomically read and delete +//! - Virtual paths are independent of local paths +//! +//! # Related Components +//! - [`TargetLockedRemote`]: Remote connection interface +//! - [`SecurityLevel`]: Encryption strength configuration +//! - [`ObjectSource`]: File source abstraction +//! - [`NetworkError`]: Error handling +//! + use crate::prelude::{ObjectSource, ProtocolRemoteTargetExt, TargetLockedRemote}; use citadel_proto::prelude::NetworkError; diff --git a/citadel_sdk/src/lib.rs b/citadel_sdk/src/lib.rs index 2d5aa2647..f4e136fb3 100644 --- a/citadel_sdk/src/lib.rs +++ b/citadel_sdk/src/lib.rs @@ -134,7 +134,7 @@ //! //! let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?; //! -//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |connect_success, remote| async move { +//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |connect_success, mut remote| async move { //! // handle program logic here //! let (sink, mut stream) = connect_success.channel.split(); //! while let Some(message) = stream.next().await { @@ -249,7 +249,7 @@ pub mod backend_kv_store; mod builder; /// Convenience functions for interacting with the remote encrypted virtual filesystem (RE-VFS) pub mod fs; -/// A list of prefabricated kernels designed for common use cases. If a greater degree of control is required for an application, a custom implementation of [NetKernel](crate::prelude::NetKernel) is desirable +/// The prefabs module contains pre-built kernels for common use cases. pub mod prefabs; /// Extension implementations endowed upon the [NodeRemote](crate::prelude::NodeRemote) pub mod remote_ext; diff --git a/citadel_sdk/src/macros.rs b/citadel_sdk/src/macros.rs index 530bae4d6..a2adb443a 100644 --- a/citadel_sdk/src/macros.rs +++ b/citadel_sdk/src/macros.rs @@ -1,3 +1,38 @@ +//! Procedural Macros for Citadel SDK +//! +//! This module provides macro utilities that simplify the implementation of common +//! traits and patterns in the Citadel Protocol SDK. These macros reduce boilerplate +//! code and ensure consistent implementations. +//! +//! # Features +//! - Automatic trait implementation generation +//! - Support for async/await patterns +//! - Integration with protocol communication systems +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::impl_remote; +//! +//! #[derive(Clone)] +//! struct MyRemote { +//! inner: NodeRemote, +//! } +//! +//! impl_remote!(MyRemote); +//! ``` +//! +//! # Important Notes +//! - Macros are used internally by the SDK +//! - Custom implementations should match the behavior of macro-generated code +//! - Async trait implementations require the async-trait feature +//! +//! # Related Components +//! - [`Remote`]: Core trait for network communication +//! - [`AccountManager`]: User account management +//! - [`NodeRequest`]: Network request handling +//! + #[macro_export] macro_rules! impl_remote { ($item:ty) => { diff --git a/citadel_sdk/src/prefabs/client/broadcast.rs b/citadel_sdk/src/prefabs/client/broadcast.rs index 41e6d7bd3..cd9272fef 100644 --- a/citadel_sdk/src/prefabs/client/broadcast.rs +++ b/citadel_sdk/src/prefabs/client/broadcast.rs @@ -1,3 +1,65 @@ +//! Group Broadcasting and Management +//! +//! This module provides functionality for creating and managing group-based communication +//! channels in the Citadel Protocol. It implements an owner-based trust model where one +//! peer acts as the group administrator and trust anchor. +//! +//! # Features +//! - Group creation and management +//! - Owner-based trust model +//! - Public and private group support +//! - Automatic member registration +//! - Group invitation system +//! - Dynamic peer discovery +//! - Concurrent group participation +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::client::broadcast::{BroadcastKernel, GroupInitRequestType}; +//! use uuid::Uuid; +//! +//! # fn main() -> Result<(), NetworkError> { +//! async fn create_group(local_user: UserIdentifier) -> Result<(), NetworkError> { +//! let request = GroupInitRequestType::Create { +//! local_user, +//! invite_list: vec![], +//! group_id: Uuid::new_v4(), +//! accept_registrations: true, +//! }; +//! +//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! .build()?; +//! +//! let kernel = BroadcastKernel::new( +//! settings, +//! request, +//! |group, _remote| async move { +//! println!("Group created with ID: {}", group.cid()); +//! Ok(()) +//! }, +//! ); +//! +//! Ok(()) +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - Each group must have exactly one owner +//! - Members must be registered with the owner +//! - Trust flows transitively through the owner +//! - Group IDs must be unique per owner +//! - Public groups allow automatic registration +//! +//! # Related Components +//! - [`GroupChannel`]: Group communication channel +//! - [`UserIdentifier`]: User identification +//! - [`GroupInitRequestType`]: Group initialization +//! - [`PrefabFunctions`]: Base prefab functionality +//! + use crate::prefabs::ClientServerRemote; use crate::prelude::*; use crate::test_common::wait_for_peers; diff --git a/citadel_sdk/src/prefabs/client/mod.rs b/citadel_sdk/src/prefabs/client/mod.rs index f453fce08..a0309b88a 100644 --- a/citadel_sdk/src/prefabs/client/mod.rs +++ b/citadel_sdk/src/prefabs/client/mod.rs @@ -1,3 +1,45 @@ +//! Client-Side Network Components +//! +//! This module provides pre-built client-side networking components for the Citadel Protocol. +//! It includes implementations for various connection patterns including single server +//! connections, peer-to-peer networking, and broadcast capabilities. +//! +//! # Features +//! - Connection builders with flexible configuration +//! - Multiple authentication methods (Transient, Credentials) +//! - UDP support for NAT traversal +//! - Session security customization +//! - Pre-shared key authentication +//! - Connection lifecycle management +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use std::net::SocketAddr; +//! use std::str::FromStr; +//! +//! # fn main() -> Result<(), NetworkError> { +//! // Create transient connection settings +//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! .with_udp_mode(UdpMode::Enabled) +//! .build()?; +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - Connection settings must be built before use +//! - UDP mode affects NAT traversal capabilities +//! - Pre-shared keys must match server configuration +//! - Transient connections do not persist data +//! +//! # Related Components +//! - [`broadcast`]: Group communication support +//! - [`peer_connection`]: Peer-to-peer networking +//! - [`single_connection`]: Single server connections +//! - [`PrefabFunctions`]: Base trait for prefab implementations +//! + use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; use crate::prefabs::ClientServerRemote; use crate::prelude::*; diff --git a/citadel_sdk/src/prefabs/client/peer_connection.rs b/citadel_sdk/src/prefabs/client/peer_connection.rs index 6902c08e0..8e97e0748 100644 --- a/citadel_sdk/src/prefabs/client/peer_connection.rs +++ b/citadel_sdk/src/prefabs/client/peer_connection.rs @@ -1,3 +1,65 @@ +//! Peer-to-Peer Connection Management +//! +//! This module provides functionality for establishing and managing peer-to-peer connections +//! in the Citadel Protocol. It supports both direct and NAT-traversed connections with +//! configurable security settings and file transfer capabilities. +//! +//! # Features +//! - Multiple simultaneous peer connections +//! - Configurable UDP and security settings per peer +//! - Built-in file transfer support +//! - Automatic peer registration handling +//! - Session password protection +//! - Connection state management +//! - Flexible peer identification +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::client::peer_connection::{PeerConnectionKernel, PeerConnectionSetupAggregator}; +//! +//! # fn main() -> Result<(), NetworkError> { +//! async fn connect_to_peers() -> Result<(), NetworkError> { +//! // Set up connections to multiple peers with different settings +//! let peers = PeerConnectionSetupAggregator::default() +//! .with_peer_custom("alice") +//! .with_udp_mode(UdpMode::Enabled) +//! .add() +//! .with_peer_custom("bob") +//! .with_session_security_settings(Default::default()) +//! .add(); +//! +//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! .build()?; +//! +//! let kernel = PeerConnectionKernel::new( +//! settings, +//! peers, +//! |connections, _remote| async move { +//! println!("Connected to {} peers!", connections.len()); +//! Ok(()) +//! }, +//! ); +//! +//! Ok(()) +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - Peers must be mutually registered before connecting +//! - UDP mode affects NAT traversal capabilities +//! - File transfers require proper handler setup +//! - Session passwords must match on both peers +//! +//! # Related Components +//! - [`PeerConnectionSetupAggregator`]: Peer connection configuration +//! - [`FileTransferHandleRx`]: File transfer handling +//! - [`UserIdentifier`]: Peer identification +//! - [`SessionSecuritySettings`]: Connection security +//! + use crate::prefabs::ClientServerRemote; use crate::prelude::results::PeerConnectSuccess; use crate::prelude::*; @@ -682,7 +744,6 @@ mod tests { let implicated_cid = remote.conn_type.get_implicated_cid(); let check = move |conn: PeerConnectSuccess| async move { - let peer_cid = conn.channel.get_peer_cid(); if do_deregister { conn.remote .deregister() @@ -693,7 +754,7 @@ mod tests { .inner .account_manager() .get_persistence_handler() - .hyperlan_peer_exists(implicated_cid, peer_cid) + .hyperlan_peer_exists(implicated_cid, conn.channel.get_peer_cid()) .await .unwrap()); } @@ -716,7 +777,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel)?; + let client = NodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -735,7 +796,6 @@ mod tests { #[rstest] #[case(2)] - #[case(3)] #[timeout(std::time::Duration::from_secs(90))] #[tokio::test(flavor = "multi_thread")] async fn test_peer_to_peer_file_transfer( diff --git a/citadel_sdk/src/prefabs/client/single_connection.rs b/citadel_sdk/src/prefabs/client/single_connection.rs index d2428e1d4..1256d528d 100644 --- a/citadel_sdk/src/prefabs/client/single_connection.rs +++ b/citadel_sdk/src/prefabs/client/single_connection.rs @@ -1,3 +1,55 @@ +//! Single Client-Server Connection Kernel +//! +//! This module implements a network kernel for managing a single client-to-server connection +//! in the Citadel Protocol. It provides NAT traversal, peer discovery, and secure +//! communication channels between clients and a central server. +//! +//! # Features +//! - Multiple authentication modes (Credentials, Transient) +//! - NAT traversal support with configurable UDP mode +//! - Secure session management with customizable security settings +//! - Object transfer handling for file/data exchange +//! - Pre-shared key authentication for server access +//! - Automatic connection lifecycle management +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel; +//! +//! # fn main() -> Result<(), NetworkError> { +//! async fn connect_to_server() -> Result<(), NetworkError> { +//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! .with_udp_mode(UdpMode::Enabled) +//! .build()?; +//! +//! let kernel = SingleClientServerConnectionKernel::new( +//! settings, +//! |conn, remote| async move { +//! println!("Connected to server!"); +//! Ok(()) +//! }, +//! ); +//! +//! Ok(()) +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - Only manages a single server connection at a time +//! - Connection handler must be Send + Future +//! - UDP mode affects NAT traversal capabilities +//! - Object transfer requires proper handler setup +//! +//! # Related Components +//! - [`NetKernel`]: Base trait for network kernels +//! - [`ServerConnectionSettings`]: Connection configuration +//! - [`ClientServerRemote`]: Remote connection handler +//! - [`ConnectionSuccess`]: Connection establishment data +//! + use crate::prefabs::client::peer_connection::FileTransferHandleRx; use crate::prefabs::client::ServerConnectionSettings; use crate::prefabs::ClientServerRemote; @@ -535,10 +587,9 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |channel, remote| async move { + |_channel, remote| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); wait_for_peers().await; - crate::test_common::udp_mode_assertions(udp_mode, channel.udp_channel_rx).await; const KEY: &str = "HELLO_WORLD"; const KEY2: &str = "HELLO_WORLD2"; diff --git a/citadel_sdk/src/prefabs/mod.rs b/citadel_sdk/src/prefabs/mod.rs index 8bb285190..57fca1202 100644 --- a/citadel_sdk/src/prefabs/mod.rs +++ b/citadel_sdk/src/prefabs/mod.rs @@ -1,3 +1,49 @@ +//! Pre-built Network Components +//! +//! This module provides a collection of pre-built network components for both client +//! and server implementations in the Citadel Protocol. These components offer ready-to-use +//! functionality for common networking patterns and use cases. +//! +//! # Features +//! - Client-side networking components +//! - Server-side networking components +//! - Remote connection management +//! - File transfer handling +//! - Signal and event processing +//! - Connection security management +//! - Peer discovery and listing +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::ClientServerRemote; +//! +//! async fn handle_remote(mut remote: ClientServerRemote) -> Result<(), NetworkError> { +//! // Get list of connected peers +//! let peers = remote.get_peers(None).await?; +//! +//! // Handle file transfers +//! if let Ok(transfer_handle) = remote.get_incoming_file_transfer_handle() { +//! transfer_handle.accept_all(); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! - File transfer handlers can only be obtained once +//! - Signal receivers are single-use +//! - Remote shutdown is graceful and asynchronous +//! - Connection types determine available operations +//! +//! # Related Components +//! - [`client`]: Client-side networking implementations +//! - [`server`]: Server-side networking implementations +//! - [`ClientServerRemote`]: Remote connection handler +//! - [`FileTransferHandleRx`]: File transfer processing +//! + use crate::impl_remote; use crate::prefabs::client::peer_connection::FileTransferHandleRx; use citadel_io::tokio::sync::mpsc::UnboundedReceiver; @@ -10,7 +56,8 @@ use std::sync::Arc; pub mod client; /// Kernels for servers pub mod server; -pub(crate) mod shared; +/// Shared utilities between client and server kernels +pub mod shared; use crate::prelude::user_ids::TargetLockedRemote; use crate::remote_ext::results::LocalGroupPeer; use crate::remote_ext::ProtocolRemoteExt; diff --git a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs index 02410bfc6..a8bb1bd2c 100644 --- a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs +++ b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs @@ -1,3 +1,39 @@ +//! Automatic File Transfer Acceptance +//! +//! This module provides a simple network kernel that automatically accepts and processes +//! all incoming file transfers. It's useful for server-side implementations that need +//! to handle file uploads without custom processing. +//! +//! # Features +//! - Automatic file transfer acceptance +//! - Silent processing of transfers +//! - Zero configuration required +//! - Minimal resource usage +//! - Error handling for transfers +//! +//! # Example: +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::server::accept_file_transfer_kernel::AcceptFileTransferKernel; +//! +//! # fn main() -> Result<(), NetworkError> { +//! let kernel = Box::new(AcceptFileTransferKernel::default()); +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - All file transfers are automatically accepted +//! - No customization of transfer handling +//! - Transfers are processed silently +//! - Errors are logged but not propagated +//! +//! # Related Components +//! - [`NetKernel`]: Base trait for network kernels +//! - [`ObjectTransferHandler`]: File transfer processing +//! - [`NodeResult`]: Network event handling +//! + use crate::prelude::*; #[derive(Default)] diff --git a/citadel_sdk/src/prefabs/server/client_connect_listener.rs b/citadel_sdk/src/prefabs/server/client_connect_listener.rs index 03617cefb..b7b3dfce2 100644 --- a/citadel_sdk/src/prefabs/server/client_connect_listener.rs +++ b/citadel_sdk/src/prefabs/server/client_connect_listener.rs @@ -1,3 +1,44 @@ +//! Client Connection Event Handler +//! +//! This module provides a network kernel that executes custom logic whenever a client +//! establishes a connection. It's particularly useful for implementing server-side +//! connection handling, authentication, and session initialization. +//! +//! # Features +//! - Custom connection handling +//! - Asynchronous event processing +//! - Type-safe callback execution +//! - Session security management +//! - UDP channel support +//! - Service discovery integration +//! +//! # Example: +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::server::client_connect_listener::ClientConnectListenerKernel; +//! +//! # fn main() -> Result<(), NetworkError> { +//! let kernel = Box::new(ClientConnectListenerKernel::new(|conn, remote| async move { +//! println!("Client connected!"); +//! Ok(()) +//! })); +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - Callbacks must be Send + Sync +//! - Futures must be Send + Sync +//! - Handles both TCP and UDP channels +//! - Automatic security settings handling +//! +//! # Related Components +//! - [`NetKernel`]: Base trait for network kernels +//! - [`ClientServerRemote`]: Client-server communication +//! - [`ConnectionSuccess`]: Connection event data +//! - [`NodeResult`]: Network event handling +//! + use crate::prefabs::ClientServerRemote; use crate::prelude::*; use citadel_proto::prelude::async_trait; diff --git a/citadel_sdk/src/prefabs/server/empty.rs b/citadel_sdk/src/prefabs/server/empty.rs index c81d9662a..25b7e9ec9 100644 --- a/citadel_sdk/src/prefabs/server/empty.rs +++ b/citadel_sdk/src/prefabs/server/empty.rs @@ -1,3 +1,40 @@ +//! Minimal Network Kernel +//! +//! This module provides a minimal network kernel implementation that performs no +//! additional processing on network events. It's useful for servers that need to +//! accept connections but don't require custom event handling. +//! +//! # Features +//! - Zero overhead processing +//! - Automatic event acceptance +//! - Minimal resource usage +//! - No state management +//! - Simple implementation +//! +//! # Example: +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::server::empty::EmptyKernel; +//! +//! # fn main() -> Result<(), NetworkError> { +//! let kernel = Box::new(EmptyKernel::default()); +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - No event processing +//! - No connection handling +//! - No channel interaction +//! - Suitable for basic servers +//! - Not suitable for interactive servers +//! +//! # Related Components +//! - [`NetKernel`]: Base trait for network kernels +//! - [`NodeRemote`]: Server remote interface +//! - [`NodeResult`]: Network event handling +//! + use citadel_proto::prelude::*; /// A kernel that does nothing to events in the protocol, nor does it cause any requests. A server that allows any and all connections with no special handlers would benefit from the use of this kernel. diff --git a/citadel_sdk/src/prefabs/server/internal_service.rs b/citadel_sdk/src/prefabs/server/internal_service.rs index 2f644e090..2b769d6e1 100644 --- a/citadel_sdk/src/prefabs/server/internal_service.rs +++ b/citadel_sdk/src/prefabs/server/internal_service.rs @@ -1,3 +1,68 @@ +//! Internal Service Integration +//! +//! This module provides a network kernel that enables integration of internal services, +//! such as HTTP servers, within the Citadel Protocol network. It's particularly useful +//! for implementing web services that need to communicate over secure Citadel channels. +//! +//! # Features +//! - Internal service integration +//! - HTTP server support +//! - Custom service handlers +//! - Asynchronous processing +//! - Type-safe communication +//! - Automatic channel management +//! - Service lifecycle handling +//! +//! # Example +//! ```rust +//! use std::convert::Infallible; +//! use std::net::SocketAddr; +//! use citadel_sdk::prelude::*; +//! use hyper::{Response, Body, Server, Request}; +//! use hyper::server::conn::AddrStream; +//! use hyper::service::{make_service_fn, service_fn}; +//! use citadel_sdk::prefabs::server::internal_service::InternalServiceKernel; +//! +//! // Create a kernel with an HTTP server +//! let kernel = InternalServiceKernel::new(|comm| async move { +//! +//! let make_svc = make_service_fn(|socket: &AddrStream| { +//! let remote_addr = socket.remote_addr(); +//! async move { +//! Ok::<_, Infallible>(service_fn(move |_: Request| async move { +//! Ok::<_, Infallible>( +//! Response::new(Body::from(format!("Hello, {}!", remote_addr))) +//! ) +//! })) +//! } +//! }); +//! +//! // Start the HTTP server +//! let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); +//! let server = Server::bind(&addr).serve(make_svc); +//! // Run this server indefinitely +//! if let Err(e) = server.await { +//! eprintln!("server error: {}", e); +//! } +//! +//! Ok(()) +//! }); +//! ``` +//! +//! # Important Notes +//! - Services run in isolated contexts +//! - Communication is bidirectional +//! - Supports HTTP/1.1 and HTTP/2 +//! - Automatic error handling +//! - Resource cleanup on shutdown +//! +//! # Related Components +//! - [`NetKernel`]: Base trait for network kernels +//! - [`InternalServerCommunicator`]: Service communication +//! - [`ClientConnectListenerKernel`]: Connection handling +//! - [`NodeResult`]: Network event handling +//! + use crate::prefabs::shared::internal_service::InternalServerCommunicator; use crate::prelude::*; use std::future::Future; @@ -8,7 +73,7 @@ pub struct InternalServiceKernel<'a, F, Fut> { _pd: PhantomData (&'a F, Fut)>, } -impl<'a, F, Fut> InternalServiceKernel<'a, F, Fut> +impl InternalServiceKernel<'_, F, Fut> where F: Send + Copy + Sync + FnOnce(InternalServerCommunicator) -> Fut, Fut: Send + Sync + Future>, @@ -33,7 +98,7 @@ where } #[async_trait] -impl<'a, F, Fut> NetKernel for InternalServiceKernel<'a, F, Fut> { +impl NetKernel for InternalServiceKernel<'_, F, Fut> { fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError> { self.inner_kernel.load_remote(node_remote) } diff --git a/citadel_sdk/src/prefabs/server/mod.rs b/citadel_sdk/src/prefabs/server/mod.rs index 1c126e7b5..9145b1984 100644 --- a/citadel_sdk/src/prefabs/server/mod.rs +++ b/citadel_sdk/src/prefabs/server/mod.rs @@ -1,3 +1,66 @@ +//! Server-Side Network Components +//! +//! This module provides pre-built server-side networking components for the Citadel Protocol. +//! It includes implementations for common server tasks such as file transfer handling, +//! client connection management, and internal service integration. +//! +//! # Features +//! - File transfer acceptance +//! - Client connection handling +//! - Internal service support +//! - Minimal processing kernels +//! - Event-driven architecture +//! - Automatic resource management +//! - Service integration patterns +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::server::accept_file_transfer_kernel::AcceptFileTransferKernel; +//! use citadel_sdk::prefabs::server::client_connect_listener::ClientConnectListenerKernel; +//! use citadel_sdk::prefabs::server::empty::EmptyKernel; +//! use citadel_sdk::prefabs::server::internal_service::InternalServiceKernel; +//! use citadel_io::tokio; +//! use hyper::service::service_fn; +//! use hyper::{Body, Request, Response}; +//! use std::convert::Infallible; +//! +//! # fn main() -> Result<(), NetworkError> { +//! // Create a basic server with file transfer support +//! let kernel = Box::new(AcceptFileTransferKernel::default()); +//! +//! // Create a server that listens for client connections +//! let kernel = Box::new(ClientConnectListenerKernel::new(|conn, remote| async move { +//! println!("Client connected!"); +//! Ok(()) +//! })); +//! +//! // Create a minimal server with no additional processing +//! let kernel = Box::new(EmptyKernel::default()); +//! +//! // Create a server with internal service support (e.g., HTTP server) +//! let kernel = Box::new(InternalServiceKernel::new(|_comm| async move { +//! let service = service_fn(|_req: Request| async move { +//! Ok::<_, Infallible>(Response::new(Body::empty())) +//! }); +//! Ok(()) +//! })); +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! - Kernels are composable components +//! - Each kernel serves a specific purpose +//! - Resource cleanup is automatic +//! - Event handling is asynchronous +//! +//! # Related Components +//! - [`accept_file_transfer_kernel`]: File transfer handling +//! - [`client_connect_listener`]: Client connection management +//! - [`internal_service`]: Internal service support +//! - [`empty`]: Minimal processing kernel +//! /// A kernel that accepts all inbound file transfer requests for basic file transfers /// AND RE-VFS transfers pub mod accept_file_transfer_kernel; diff --git a/citadel_sdk/src/prefabs/shared/internal_service.rs b/citadel_sdk/src/prefabs/shared/internal_service.rs index aa8b3d13a..466898600 100644 --- a/citadel_sdk/src/prefabs/shared/internal_service.rs +++ b/citadel_sdk/src/prefabs/shared/internal_service.rs @@ -1,3 +1,43 @@ +//! Internal Service Communication Layer +//! +//! This module provides the core functionality for integrating internal services +//! within the Citadel Protocol network. It enables bidirectional communication +//! between network services and the protocol layer. +//! +//! # Features +//! - Asynchronous communication channels +//! - Bidirectional message passing +//! - Automatic protocol conversion +//! - Error propagation +//! - Resource cleanup on shutdown +//! - Stream-based I/O interface +//! +//! # Example +//! ```rust,no_run +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::prefabs::shared::internal_service::InternalServerCommunicator; +//! use futures::Future; +//! +//! async fn my_service(comm: InternalServerCommunicator) -> Result<(), NetworkError> { +//! // Service implementation +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! - Services run in isolated contexts +//! - Communication is fully asynchronous +//! - Implements AsyncRead and AsyncWrite +//! - Automatic cleanup on drop +//! - Thread-safe message passing +//! +//! # Related Components +//! - [`ConnectionSuccess`]: Connection event data +//! - [`TargetLockedRemote`]: Remote target interface +//! - [`NetworkError`]: Error handling +//! - [`SecBuffer`]: Secure data handling +//! + use crate::prelude::{ConnectionSuccess, TargetLockedRemote}; use bytes::Bytes; use citadel_io::tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; diff --git a/citadel_sdk/src/prefabs/shared/mod.rs b/citadel_sdk/src/prefabs/shared/mod.rs index b4c993994..8171ce0b5 100644 --- a/citadel_sdk/src/prefabs/shared/mod.rs +++ b/citadel_sdk/src/prefabs/shared/mod.rs @@ -1 +1,23 @@ +//! Shared Network Components +//! +//! This module contains network components that are shared between client and server +//! implementations in the Citadel Protocol. These components provide common +//! functionality used across different network roles. +//! +//! # Features +//! - Internal service integration +//! - Shared utility functions +//! - Common type definitions +//! - Cross-role functionality +//! +//! # Important Notes +//! - Components are role-agnostic +//! - Thread-safe implementations +//! - Async-first design +//! +//! # Related Components +//! - [`internal_service`]: Service integration +//! - [`client`]: Client-side components +//! - [`server`]: Server-side components +//! pub mod internal_service; diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index d0dde6b76..19675f366 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -1,3 +1,60 @@ +//! Remote Protocol Extensions +//! +//! This module extends the core NodeRemote functionality with high-level operations +//! for managing connections, file transfers, and peer interactions in the Citadel +//! Protocol network. +//! +//! # Features +//! - User registration and authentication +//! - Connection management +//! - File transfer operations +//! - Virtual filesystem support +//! - Peer discovery and management +//! - Group communication +//! - Security settings configuration +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! +//! async fn example(remote: NodeRemote) -> Result<(), NetworkError> { +//! // Register a new user +//! let reg = remote.register_with_defaults( +//! "127.0.0.1:25021", +//! "John Doe", +//! "john.doe", +//! "password123" +//! ).await?; +//! +//! // Connect to a peer +//! let auth = AuthenticationRequest::credentialed("john.doe", "password123"); +//! let conn = remote.connect_with_defaults(auth).await?; +//! +//! // Send a file to a peer +//! remote.find_target("john.doe", "peer.name") +//! .await? +//! .send_file("/path/to/file.txt") +//! .await?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! - All operations are asynchronous +//! - Connections are automatically managed +//! - File transfers support chunking +//! - Virtual filesystem is encrypted +//! - Peer connections require mutual registration +//! +//! # Related Components +//! - [`NodeRemote`]: Core remote interface +//! - [`ClientServerRemote`]: Client-server communication +//! - [`PeerRemote`]: Peer-to-peer communication +//! - [`ConnectionSuccess`]: Connection management +//! - [`RegisterSuccess`]: Registration handling +//! + use crate::prefabs::ClientServerRemote; use crate::prelude::results::{PeerConnectSuccess, PeerRegisterStatus}; use crate::prelude::*; diff --git a/citadel_sdk/src/responses.rs b/citadel_sdk/src/responses.rs index 778e7ffa6..751ffa07a 100644 --- a/citadel_sdk/src/responses.rs +++ b/citadel_sdk/src/responses.rs @@ -1,11 +1,46 @@ -//! A list of helpers making the response phase simple and intuitive +//! Protocol Response Helpers //! -//! Generally, when making a response, the response must be sent outbound through -//! the [NodeRemote](crate::prelude::NodeRemote) with a custom ticket equivalent to the ticket receieved by -//! the external request in order for the peer to listen for a response. Additionally, -//! [PeerConnectionType](crate::prelude::PeerConnectionType) data must be `.reverse()`'d. Finally, some response types -//! require the manual input of usernames, and as such, this helper library enforces -//! all these requirements +//! This module provides helper functions for handling responses to various protocol +//! operations in the Citadel Protocol. It simplifies the process of sending responses +//! to peer registration, connection, and group invitation requests. +//! +//! # Features +//! - Peer registration response handling +//! - Peer connection response management +//! - Group invitation response processing +//! - Automatic ticket management +//! - Connection type reversal handling +//! - Username resolution and validation +//! +//! # Example +//! ```rust +//! use citadel_sdk::prelude::*; +//! use citadel_sdk::responses; +//! +//! async fn handle_peer_request( +//! signal: PeerSignal, +//! remote: &impl Remote +//! ) -> Result<(), NetworkError> { +//! // Accept a peer registration request +//! let ticket = responses::peer_register(signal, true, remote).await?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! - Responses must match request tickets +//! - Connection types are automatically reversed +//! - Username resolution is handled internally +//! - Group responses require server connection +//! +//! # Related Components +//! - [`Remote`]: Network communication interface +//! - [`PeerSignal`]: Peer communication events +//! - [`NodeResult`]: Network operation results +//! - [`Ticket`]: Request/response correlation +//! + use crate::prelude::*; /// Given the `input_signal` from the peer, this function sends a register response to the target peer diff --git a/citadel_sdk/src/test_common.rs b/citadel_sdk/src/test_common.rs index 0fe6c5926..4e3f38f5c 100644 --- a/citadel_sdk/src/test_common.rs +++ b/citadel_sdk/src/test_common.rs @@ -1,3 +1,43 @@ +//! Testing Utilities for Citadel Protocol +//! +//! This module provides common utilities and helpers for testing Citadel Protocol +//! components. It includes functions for setting up test servers, managing test +//! barriers, and performing common assertions. +//! +//! # Features +//! - Test server creation and configuration +//! - Synchronization barriers for multi-peer tests +//! - UDP mode testing utilities +//! - P2P connection testing helpers +//! - Local test peer management +//! +//! # Example +//! ```rust +//! use citadel_sdk::test_common::*; +//! use citadel_sdk::prelude::*; +//! +//! async fn test_server() { +//! // Create a test server with default settings +//! let (server_future, addr) = server_info(); +//! +//! // Run server and handle connections +//! server_future.await.expect("Server failed to start"); +//! } +//! ``` +//! +//! # Important Notes +//! - Most functionality requires "localhost-testing" feature +//! - Test barriers help coordinate multi-peer tests +//! - UDP assertions verify connection modes +//! - P2P assertions validate peer connections +//! +//! # Related Components +//! - [`NodeBuilder`]: Server configuration builder +//! - [`EmptyKernel`]: Basic test server kernel +//! - [`UdpMode`]: UDP connection configuration +//! - [`PeerConnectSuccess`]: Peer connection validation +//! + #![allow(missing_docs, unused_imports)] #![doc(hidden)] use crate::prefabs::server::client_connect_listener::ClientConnectListenerKernel; diff --git a/citadel_types/src/crypto/mod.rs b/citadel_types/src/crypto/mod.rs index 84e920d1b..ad0c1f9d6 100644 --- a/citadel_types/src/crypto/mod.rs +++ b/citadel_types/src/crypto/mod.rs @@ -1,3 +1,62 @@ +//! # Cryptographic Types and Utilities +//! +//! This module provides core cryptographic types and utilities for the Citadel Protocol, +//! including secure memory management, algorithm selection, and parameter configuration. +//! +//! ## Key Components +//! +//! ### Secure Memory Management +//! +//! The module provides `SecBuffer` for secure handling of sensitive data: +//! +//! ```rust +//! use citadel_types::crypto::SecBuffer; +//! +//! // Create a secure buffer +//! let mut buffer = SecBuffer::empty(); +//! +//! // Work with the buffer securely +//! { +//! let mut handle = buffer.handle(); +//! handle.extend_from_slice(b"sensitive data"); +//! } // Memory is locked when handle is dropped +//! ``` +//! +//! ### Cryptographic Parameters +//! +//! Configure cryptographic algorithms and security levels: +//! +//! ```rust +//! use citadel_types::crypto::{KemAlgorithm, EncryptionAlgorithm, SecurityLevel}; +//! +//! // Create parameters +//! let params = KemAlgorithm::Kyber +//! + EncryptionAlgorithm::ChaCha20Poly_1305; +//! +//! // Set security level +//! let level = SecurityLevel::High; +//! ``` +//! +//! ### Algorithm Selection +//! +//! Supported algorithms include: +//! +//! - KEM (Key Encapsulation Mechanism) +//! - Kyber (1024) +//! - Encryption +//! - ChaCha20-Poly1305 +//! - AES-GCM +//! - Ascon +//! - Signatures +//! - Falcon +//! +//! ## Security Considerations +//! +//! - All sensitive data should be stored in `SecBuffer` +//! - Use appropriate security levels for your use case +//! - Consider perfect secrecy mode for maximum security +//! - Properly handle algorithm selection based on requirements + use crate::utils; use crate::utils::validate_crypto_params; use bytes::{Bytes, BytesMut}; diff --git a/citadel_types/src/lib.rs b/citadel_types/src/lib.rs index 81399bb81..b7f20550d 100644 --- a/citadel_types/src/lib.rs +++ b/citadel_types/src/lib.rs @@ -1,13 +1,86 @@ +//! # Citadel Types +//! +//! Core type definitions and utilities for the Citadel Protocol. +//! This crate provides fundamental types, error definitions, and utilities +//! used throughout the Citadel Protocol ecosystem. +//! +//! ## Core Modules +//! +//! - **crypto**: Cryptographic types and utilities +//! - Secure memory buffers +//! - Cryptographic parameters +//! - Algorithm definitions +//! - Security level specifications +//! +//! - **errors**: Error types and handling +//! - Protocol-specific errors +//! - Error conversion traits +//! - Result type aliases +//! +//! - **proto**: Protocol-specific types +//! - Message definitions +//! - Protocol constants +//! - Serialization formats +//! +//! - **user**: User-related types +//! - User identifiers +//! - Authentication data +//! - Session information +//! +//! - **utils**: General utilities +//! - Validation functions +//! - Helper traits +//! - Common constants +//! +//! ## Usage +//! +//! The crate provides a prelude module for convenient imports: +//! +//! ```rust +//! use citadel_types::prelude::*; +//! +//! // Use crypto types +//! let secure_buffer = SecBuffer::new(); +//! let params = CryptoParameters::default(); +//! +//! // Use protocol types +//! let message = Message::new(); +//! +//! // Use user types +//! let user_id = UserId::new(); +//! ``` +//! +//! ## Features +//! +//! - Memory-secure types for sensitive data +//! - Comprehensive error handling +//! - Serialization support via serde +//! - Validation utilities +//! - Type-safe protocol definitions + #![allow(non_camel_case_types)] +/// Common imports for working with Citadel types. +/// +/// This module re-exports the most commonly used types from the crate's +/// modules, providing a convenient way to import multiple items at once. pub mod prelude { pub use crate::crypto::*; pub use crate::proto::*; pub use crate::user::*; } +/// Cryptographic types and utilities. pub mod crypto; + +/// Error types and handling. pub mod errors; + +/// Protocol-specific message and data types. pub mod proto; + +/// User-related types and data structures. pub mod user; + +/// General utility functions and helpers. pub mod utils; diff --git a/citadel_types/src/utils/mod.rs b/citadel_types/src/utils/mod.rs index 3a84e0c44..c4d6cedfc 100644 --- a/citadel_types/src/utils/mod.rs +++ b/citadel_types/src/utils/mod.rs @@ -65,20 +65,18 @@ pub fn const_time_compare(this: &[u8], other: &[u8]) -> bool { } pub fn validate_crypto_params(params: &CryptoParameters) -> Result<(), Error> { - if params.encryption_algorithm == EncryptionAlgorithm::Kyber - && params.kem_algorithm != KemAlgorithm::Kyber - { + let uses_kyber_kem = params.kem_algorithm == KemAlgorithm::Kyber; + if params.encryption_algorithm == EncryptionAlgorithm::Kyber && !uses_kyber_kem { return Err(Error::Generic( "Invalid crypto parameter combination. Kyber encryption must be paired with Kyber KEM", )); } if params.encryption_algorithm == EncryptionAlgorithm::Kyber - && params.kem_algorithm == KemAlgorithm::Kyber && params.sig_algorithm == SigAlgorithm::None { return Err(Error::Generic( - "A post-quantum signature scheme must be selected when using Kyber encryption + KEM", + "A post-quantum signature scheme must be selected when using Kyber encryption", )); } diff --git a/citadel_user/src/account_loader.rs b/citadel_user/src/account_loader.rs index d51c800ec..7415b9285 100644 --- a/citadel_user/src/account_loader.rs +++ b/citadel_user/src/account_loader.rs @@ -1,3 +1,52 @@ +//! Account Loading and File Management +//! +//! This module provides functionality for loading and managing serialized client network accounts (CNACs) +//! and other file-based data structures in the Citadel network. +//! +//! # Features +//! +//! * Load client network accounts from filesystem +//! * Support for both personal and impersonal accounts +//! * Generic file type loading by extension +//! * Efficient deserialization of stored data +//! * Error handling for IO and deserialization operations +//! +//! # Example +//! +//! ```rust +//! use citadel_user::account_loader; +//! use citadel_user::directory_store::DirectoryStore; +//! use citadel_crypt::stacked_ratchet::DefaultRatchet; +//! +//! # fn main() -> Result<(), Box> { +//! // Create a directory store for account management +//! let store = DirectoryStore::new("./accounts")?; +//! +//! // Load all client network accounts +//! let accounts = account_loader::load_cnac_files::(&store)?; +//! +//! // Process loaded accounts +//! for (cid, account) in accounts { +//! println!("Loaded account with CID: {}", cid); +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! # Important Notes +//! +//! * Account loading is performed non-recursively within specified directories +//! * Failed deserialization attempts are logged but do not halt the loading process +//! * Both personal and impersonal accounts are loaded and merged into a single collection +//! * File operations use buffered I/O for efficiency +//! +//! # Related Components +//! +//! * [`DirectoryStore`] - Manages filesystem paths for account storage +//! * [`ClientNetworkAccount`] - The primary account structure being loaded +//! * [`AccountError`] - Error handling for account operations +//! * [`Ratchet`] - Cryptographic operations for account security + use crate::client_account::ClientNetworkAccountInner; use crate::directory_store::*; use crate::hypernode_account::CNAC_SERIALIZED_EXTENSION; diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs index 92325d46b..04e5b6250 100644 --- a/citadel_user/src/account_manager.rs +++ b/citadel_user/src/account_manager.rs @@ -1,3 +1,98 @@ +//! # Account Manager +//! +//! The Account Manager is responsible for managing user accounts in the Citadel Protocol. +//! It provides a unified interface for account creation, storage, retrieval, and management +//! across different backend storage systems. +//! +//! ## Features +//! +//! * **Account Management** +//! - User registration and authentication +//! - Personal and impersonal account modes +//! - Account metadata management +//! - Account deletion and purging +//! +//! * **Storage Backend Support** +//! - In-memory storage +//! - File system persistence +//! - SQL database integration +//! - Redis database support +//! +//! * **Peer Management** +//! - HyperLAN peer registration +//! - P2P connection handling +//! - Peer list synchronization +//! - User information lookup +//! +//! * **Security** +//! - Argon2id password hashing +//! - Secure credential management +//! - Ratchet-based cryptography +//! +//! ## Usage Example +//! +//! ```rust +//! use citadel_user::prelude::*; +//! use citadel_crypt::stacked_ratchet::StackedRatchet; +//! +//! async fn example() -> Result<(), Box> { +//! // Initialize account manager with in-memory backend +//! let manager = AccountManager::::new( +//! BackendType::InMemory, +//! None, +//! None, +//! None +//! ).await?; +//! +//! // Register a new client account +//! let conn_info = ConnectionInfo::new( +//! SecurityLevel::Standard, +//! None, +//! None +//! ); +//! +//! let creds = ProposedCredentials::new( +//! "username".to_string(), +//! "password".to_string(), +//! None +//! ); +//! +//! let ratchet = StackedRatchet::new(1234, 0); +//! +//! let account = manager.register_impersonal_hyperlan_client_network_account( +//! conn_info, +//! creds, +//! ratchet +//! )?; +//! +//! // Retrieve peer information +//! if let Some(peers) = manager.get_hyperlan_peer_list(account.get_cid())? { +//! for peer_cid in peers { +//! println!("Connected to peer: {}", peer_cid); +//! } +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * Account manager must be initialized with appropriate backend configuration +//! * Username uniqueness is not guaranteed - use CIDs for unique identification +//! * Proper error handling is essential for backend operations +//! * Account operations are thread-safe and async-compatible +//! * Backend connections are verified during initialization +//! +//! ## Related Components +//! +//! * `ClientNetworkAccount` - Individual client account management +//! * `PersistenceHandler` - Backend storage interface +//! * `ServicesHandler` - External service integration +//! * `BackendType` - Storage backend configuration +//! * `ProposedCredentials` - Account creation parameters +//! + use crate::auth::proposed_credentials::ProposedCredentials; use crate::backend::memory::MemoryBackend; use crate::backend::{BackendType, PersistenceHandler}; diff --git a/citadel_user/src/auth/mod.rs b/citadel_user/src/auth/mod.rs index b13458fbc..b1ff2ac55 100644 --- a/citadel_user/src/auth/mod.rs +++ b/citadel_user/src/auth/mod.rs @@ -1,3 +1,36 @@ +//! Authentication Mode Management +//! +//! This module provides authentication mode handling for Citadel Network Accounts (CNACs), +//! supporting both password-based and passwordless authentication methods. +//! +//! # Features +//! +//! * **Authentication Modes** +//! - Argon2id password-based authentication +//! - Passwordless authentication +//! - Username management +//! - Full name handling +//! +//! * **Security Features** +//! - Secure password hashing +//! - Mode-specific data storage +//! - Authentication state management +//! +//! # Important Notes +//! +//! * All authentication modes require unique usernames +//! * Argon2id is used for secure password hashing +//! * Passwordless mode still maintains user identity +//! * Authentication data is serializable for storage +//! * Mode can be determined at runtime +//! +//! # Related Components +//! +//! * `proposed_credentials` - Credential validation +//! * `ArgonContainerType` - Password hashing +//! * `ClientNetworkAccount` - Uses authentication modes +//! * `AccountManager` - Manages authentication + #![allow(missing_docs, dead_code)] use citadel_crypt::argon::argon_container::ArgonContainerType; use serde::{Deserialize, Serialize}; diff --git a/citadel_user/src/auth/proposed_credentials.rs b/citadel_user/src/auth/proposed_credentials.rs index 8569930fd..46e5be9db 100644 --- a/citadel_user/src/auth/proposed_credentials.rs +++ b/citadel_user/src/auth/proposed_credentials.rs @@ -1,3 +1,43 @@ +//! Credential Proposal and Validation +//! +//! This module handles the creation, validation, and processing of user credentials +//! in the Citadel Protocol, supporting both password-based and passwordless authentication. +//! +//! # Features +//! +//! * **Credential Management** +//! - Password hashing with Argon2id +//! - Username sanitization +//! - Full name handling +//! - Passwordless mode support +//! +//! * **Security Features** +//! - Secure password transformation +//! - Random salt generation +//! - Configurable Argon2 parameters +//! - Memory-safe credential handling +//! +//! * **Validation** +//! - Server-side validation +//! - Credential comparison +//! - Username uniqueness +//! - Format sanitization +//! +//! # Important Notes +//! +//! * Passwords are pre-hashed with SHA-3 before Argon2 +//! * All strings are trimmed and sanitized +//! * Registration generates secure random secrets +//! * Credentials are zeroed after use +//! * Server validates all client credentials +//! +//! # Related Components +//! +//! * `DeclaredAuthenticationMode` - Final auth state +//! * `ServerMiscSettings` - Server validation rules +//! * `ArgonContainerType` - Password hashing +//! * `AccountManager` - Uses proposed credentials + use crate::auth::DeclaredAuthenticationMode; use crate::misc::AccountError; use crate::server_misc_settings::ServerMiscSettings; diff --git a/citadel_user/src/backend/filesystem_backend.rs b/citadel_user/src/backend/filesystem_backend.rs index fda0831a1..9a7b06d77 100644 --- a/citadel_user/src/backend/filesystem_backend.rs +++ b/citadel_user/src/backend/filesystem_backend.rs @@ -1,3 +1,32 @@ +//! # Filesystem Backend +//! +//! The filesystem backend provides persistent storage for Citadel client accounts and data using the local filesystem. +//! It implements the `BackendConnection` trait and serves as a bridge between the in-memory storage and disk-based persistence. +//! +//! ## Features +//! +//! * Persistent storage of client network accounts (CNACs) on the local filesystem +//! * Hybrid storage model combining in-memory and disk-based storage +//! * Directory structure management for organizing client data +//! * Support for peer-to-peer relationship persistence +//! * Virtual filesystem (RevFS) operations for file transfers +//! * Byte map storage for key-value data +//! +//! ## Important Notes +//! +//! * Uses atomic file operations to prevent data corruption +//! * Maintains compatibility with the memory backend for fast access +//! * Implements proper cleanup of temporary files and directories +//! * Handles both personal and impersonal client accounts +//! * Supports the RE-FVS (Reverse File Virtual System) for secure file transfers +//! +//! ## Related Components +//! +//! * `MemoryBackend`: Used as the in-memory storage layer +//! * `DirectoryStore`: Manages the filesystem directory structure +//! * `ClientNetworkAccount`: The core data structure being persisted +//! * `BackendConnection`: The trait implemented for backend storage + use crate::account_loader::load_cnac_files; use crate::backend::memory::MemoryBackend; use crate::backend::BackendConnection; @@ -19,7 +48,18 @@ use std::path::{Path, PathBuf}; use tokio::io::AsyncWriteExt; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; -/// For handling I/O with the local filesystem +/// A backend implementation that stores client data on the local filesystem while maintaining +/// an in-memory cache for fast access. This provides persistence while keeping the performance +/// benefits of in-memory storage. +/// +/// The filesystem backend organizes data in a structured directory hierarchy and implements +/// atomic file operations to ensure data integrity. It supports both personal and impersonal +/// client accounts, peer relationships, and virtual filesystem operations. +/// +/// # Type Parameters +/// +/// * `R`: The ratchet type used for encryption +/// * `Fcm`: The ratchet type used for FCM (Firebase Cloud Messaging) pub struct FilesystemBackend { memory_backend: MemoryBackend, directory_store: Option, @@ -28,6 +68,9 @@ pub struct FilesystemBackend { #[async_trait] impl BackendConnection for FilesystemBackend { + /// Establishes a connection to the filesystem backend. + /// + /// This method sets up the directory structure and loads existing client data from the filesystem. async fn connect(&mut self) -> Result<(), AccountError> { let directory_store = crate::directory_store::setup_directories(self.home_dir.clone())?; let map = load_cnac_files(&directory_store)?; @@ -38,10 +81,16 @@ impl BackendConnection for FilesystemBackend Result { Ok(true) } + /// Saves a client network account to the filesystem. + /// + /// This method serializes the client data and writes it to a file on the local filesystem. #[allow(unused_results)] async fn save_cnac(&self, cnac: &ClientNetworkAccount) -> Result<(), AccountError> { // save to filesystem, then, synchronize to memory @@ -53,6 +102,9 @@ impl BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend Result { self.memory_backend.cid_is_registered(cid).await } + /// Deletes a client network account by its CID. + /// + /// This method removes the account from the in-memory cache and deletes the corresponding file from the filesystem. async fn delete_cnac_by_cid(&self, cid: u64) -> Result<(), AccountError> { let is_personal = self .memory_backend @@ -77,6 +135,9 @@ impl BackendConnection for FilesystemBackend Result { let paths = { let mut write = self.memory_backend.clients.write(); @@ -103,6 +164,9 @@ impl BackendConnection for FilesystemBackend, @@ -112,14 +176,23 @@ impl BackendConnection for FilesystemBackend Result, AccountError> { self.memory_backend.get_username_by_cid(cid).await } + /// Retrieves the full name associated with a client CID. + /// + /// This method checks the in-memory cache for the full name. async fn get_full_name_by_cid(&self, cid: u64) -> Result, AccountError> { self.memory_backend.get_full_name_by_cid(cid).await } + /// Registers a peer-to-peer relationship between two clients. + /// + /// This method updates the in-memory cache and saves the changes to the filesystem. async fn register_p2p_as_server(&self, cid0: u64, cid1: u64) -> Result<(), AccountError> { self.memory_backend .register_p2p_as_server(cid0, cid1) @@ -141,6 +214,9 @@ impl BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend Result<(), AccountError> { self.memory_backend .deregister_p2p_as_server(cid0, cid1) @@ -174,6 +253,9 @@ impl BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend, @@ -211,6 +302,9 @@ impl BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend, @@ -271,6 +380,9 @@ impl BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend>, @@ -433,6 +560,9 @@ impl BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend FilesystemBackend { + /// Saves a client network account by its CID. + /// + /// This method retrieves the client account from the in-memory cache and saves it to the filesystem. async fn save_cnac_by_cid(&self, cid: u64) -> Result<(), AccountError> { let cnac = self .memory_backend @@ -496,6 +632,9 @@ impl FilesystemBackend { self.save_cnac(&cnac).await } + /// Generates the local save path for a client network account. + /// + /// This method constructs the file path based on the client CID and whether it is a personal or impersonal account. fn generate_cnac_local_save_path(&self, cid: u64, is_personal: bool) -> PathBuf { let dirs = self.directory_store.as_ref().unwrap(); if is_personal { @@ -517,6 +656,9 @@ impl FilesystemBackend { } impl From for FilesystemBackend { + /// Creates a new `FilesystemBackend` instance from a home directory path. + /// + /// This method initializes the backend with the provided home directory path and sets up the directory structure. fn from(home_dir: String) -> Self { Self { home_dir, @@ -526,7 +668,9 @@ impl From for FilesystemBackend { } } -// works for RE-FVS and standard file transfers +/// Retrieves the file path for a transfer type. +/// +/// This method constructs the file path based on the transfer type and client CID. async fn get_file_path( source_cid: u64, transfer_type: &TransferType, @@ -574,12 +718,18 @@ async fn get_file_path( } } +/// Retrieves the metadata path for a RevFS file. +/// +/// This method constructs the metadata path based on the file path. fn get_revfs_file_metadata_path>(path: P) -> PathBuf { let mut metadata_path = format!("{}", path.as_ref().display()); metadata_path.push_str(crate::misc::VIRTUAL_FILE_METADATA_EXT); crate::misc::prepare_virtual_path(metadata_path) } +/// Deletes a list of paths. +/// +/// This method removes the files or directories at the specified paths. async fn delete_paths, R: AsRef<[T]>>(paths: R) -> Result<(), AccountError> { let paths = paths.as_ref(); for path in paths { diff --git a/citadel_user/src/backend/memory.rs b/citadel_user/src/backend/memory.rs index 6e469314d..ef6e749a3 100644 --- a/citadel_user/src/backend/memory.rs +++ b/citadel_user/src/backend/memory.rs @@ -1,3 +1,43 @@ +//! In-Memory Backend Storage +//! +//! This module provides an in-memory implementation of the backend storage system, +//! primarily used for testing and environments without filesystem access (e.g., WASM). +//! +//! # Features +//! +//! * **Storage Management** +//! - Thread-safe client storage +//! - Peer relationship tracking +//! - Metadata management +//! - Byte map operations +//! +//! * **Memory Safety** +//! - Read-write locking +//! - Atomic operations +//! - Resource cleanup +//! - Reference management +//! +//! * **Client Operations** +//! - Account registration +//! - Peer management +//! - Data persistence +//! - Client lookup +//! +//! # Important Notes +//! +//! * Data is not persisted between program restarts +//! * All operations are thread-safe through RwLock +//! * Suitable for testing and WASM environments +//! * Memory usage scales with stored data +//! * Peer relationships are bi-directionally maintained +//! +//! # Related Components +//! +//! * `BackendConnection` - Implemented interface +//! * `ClientNetworkAccount` - Stored data type +//! * `AccountManager` - Uses backend storage +//! * `PersistenceHandler` - Manages backend lifecycle + use crate::backend::BackendConnection; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; diff --git a/citadel_user/src/backend/mod.rs b/citadel_user/src/backend/mod.rs index d37241ae0..8cda6f69b 100644 --- a/citadel_user/src/backend/mod.rs +++ b/citadel_user/src/backend/mod.rs @@ -1,3 +1,43 @@ +//! Backend Storage and Persistence Layer +//! +//! This module provides the persistence and storage infrastructure for the Citadel Protocol, +//! supporting multiple backend types including filesystem, in-memory, Redis, and SQL databases. +//! +//! # Features +//! +//! * **Multiple Backend Support** +//! - In-memory storage for ephemeral data +//! - Filesystem persistence +//! - Redis database integration +//! - SQL database support (PostgreSQL, MySQL, SQLite) +//! +//! * **Common Interface** +//! - Unified backend connection trait +//! - Consistent error handling +//! - Async operation support +//! - Transaction management +//! +//! * **Data Management** +//! - Account persistence +//! - Virtual filesystem operations +//! - Peer relationship storage +//! - Object transfer handling +//! +//! # Important Notes +//! +//! * Backend selection is feature-gated at compile time +//! * In-memory backend does not persist between restarts +//! * Database connections are managed automatically +//! * All operations are thread-safe +//! * Backends implement automatic reconnection +//! +//! # Related Components +//! +//! * `AccountManager` - Primary user of backend services +//! * `ClientNetworkAccount` - Stored account data +//! * `VirtualObjectMetadata` - File transfer metadata +//! * `PersistenceHandler` - Backend connection management + use std::collections::HashMap; use std::ops::Deref; use std::sync::Arc; diff --git a/citadel_user/src/backend/redis_backend.rs b/citadel_user/src/backend/redis_backend.rs index 0902ff981..36a08e9cb 100644 --- a/citadel_user/src/backend/redis_backend.rs +++ b/citadel_user/src/backend/redis_backend.rs @@ -1,3 +1,35 @@ +//! # Redis Backend +//! +//! The Redis backend provides distributed storage for Citadel client accounts and data using Redis. +//! It implements the `BackendConnection` trait and enables scalable, high-performance data storage +//! with support for clustering and replication. +//! +//! ## Features +//! +//! * Distributed storage using Redis +//! * Connection pooling for efficient resource management +//! * Support for Redis clustering +//! * Configurable connection options +//! * Atomic operations for data consistency +//! * Peer relationship management +//! * Byte map storage functionality +//! +//! ## Important Notes +//! +//! * Requires a running Redis server +//! * Supports both standalone and clustered Redis deployments +//! * Implements connection health checks +//! * Handles connection pooling and timeouts +//! * Provides automatic reconnection +//! +//! ## Related Components +//! +//! * `BackendConnection`: The trait implemented for backend storage +//! * `RedisConnectionManager`: Manages Redis connections +//! * `RedisConnectionOptions`: Configuration for Redis connections +//! * `ClientNetworkAccount`: The core data structure being stored +//! + use crate::backend::memory::no_backend_streaming; use crate::backend::BackendConnection; use crate::client_account::ClientNetworkAccount; @@ -16,7 +48,18 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::time::Duration; -/// Backend struct for redis +/// Backend implementation that stores client data in Redis, providing distributed storage +/// with support for clustering and replication. This backend is suitable for production +/// deployments requiring high availability and scalability. +/// +/// The Redis backend manages a connection pool for efficient resource utilization and +/// implements automatic reconnection and health checks. It supports both standalone +/// Redis servers and Redis clusters. +/// +/// # Type Parameters +/// +/// * `R`: The ratchet type used for encryption +/// * `Fcm`: The ratchet type used for FCM (Firebase Cloud Messaging) pub(crate) struct RedisBackend { url: String, conn_options: RedisConnectionOptions, @@ -26,8 +69,13 @@ pub(crate) struct RedisBackend { type RedisPool = Pool; +/// Configuration options for the Redis connection pool. These options allow fine-tuning +/// of connection management, timeouts, and health checks. +/// +/// The connection pool helps manage Redis connections efficiently by maintaining +/// a pool of reusable connections, reducing the overhead of creating new connections +/// for each operation. #[derive(Debug, Default, Clone, Eq, PartialEq)] -/// For setting custom options for the internal redis connection pool pub struct RedisConnectionOptions { /// Sets the number of connections. Default 10 pub max_open: Option, diff --git a/citadel_user/src/backend/sql_backend.rs b/citadel_user/src/backend/sql_backend.rs index 083c051e5..6b3025b89 100644 --- a/citadel_user/src/backend/sql_backend.rs +++ b/citadel_user/src/backend/sql_backend.rs @@ -1,3 +1,35 @@ +//! # SQL Backend +//! +//! The SQL backend provides relational database storage for Citadel client accounts and data. +//! It implements the `BackendConnection` trait and supports multiple SQL database variants +//! including MySQL, PostgreSQL, and SQLite. +//! +//! ## Features +//! +//! * Support for multiple SQL database variants +//! * Connection pooling for efficient resource management +//! * Configurable connection options +//! * Automatic schema creation and migration +//! * Atomic database operations +//! * Peer relationship management +//! * Byte map storage functionality +//! +//! ## Important Notes +//! +//! * Requires a running SQL database server (except for SQLite) +//! * Handles database-specific SQL syntax differences +//! * Implements connection pooling and timeouts +//! * Provides automatic schema management +//! * Supports both blob and text storage +//! +//! ## Related Components +//! +//! * `BackendConnection`: The trait implemented for backend storage +//! * `SqlConnectionOptions`: Configuration for SQL connections +//! * `SqlVariant`: Enum for supported SQL database types +//! * `ClientNetworkAccount`: The core data structure being stored +//! + use crate::backend::memory::no_backend_streaming; use crate::backend::{BackendConnection, BackendType}; use crate::client_account::ClientNetworkAccount; @@ -21,7 +53,18 @@ use std::ops::DerefMut; use std::str::FromStr; use std::time::Duration; -/// A container for handling db conns +/// Backend implementation that stores client data in SQL databases, supporting multiple +/// database variants including MySQL, PostgreSQL, and SQLite. This backend provides +/// relational storage with ACID properties and efficient querying capabilities. +/// +/// The SQL backend manages a connection pool for efficient resource utilization and +/// handles database-specific SQL syntax differences automatically. It supports both +/// blob and text storage formats for different data types. +/// +/// # Type Parameters +/// +/// * `R`: The ratchet type used for encryption +/// * `Fcm`: The ratchet type used for FCM (Firebase Cloud Messaging) pub struct SqlBackend { url: String, conn: Option, @@ -30,17 +73,28 @@ pub struct SqlBackend { _pd: PhantomData<(R, Fcm)>, } +/// The supported SQL database variants. +/// Each variant requires specific connection string formatting and SQL syntax. #[derive(Eq, PartialEq)] enum SqlVariant { + /// MySQL/MariaDB database MySQL, + /// PostgreSQL database Postgre, + /// SQLite database Sqlite, } +/// Default value for the CAR (Connection Auto-Reconnect) mode const CAR_MODE_DEFAULT: bool = false; +/// Configuration options for SQL database connections. These options allow fine-tuning +/// of connection management, pooling behavior, and timeouts. +/// +/// The connection pool helps manage database connections efficiently by maintaining +/// a pool of reusable connections, reducing the overhead of creating new connections +/// for each operation. #[derive(Default, Debug, Clone, Eq, PartialEq)] -/// Custom connection options pub struct SqlConnectionOptions { /// The maximum number of connections to keep pub max_connections: Option, @@ -426,7 +480,7 @@ impl BackendConnection for SqlBackend implicated_cid: u64, ) -> Result, AccountError> { let conn = &(self.get_conn().await?); - // cnacs(cid VARCHAR(20) NOT NULL, is_connected BOOL, is_personal BOOL, username VARCHAR({}) UNIQUE, full_name TEXT, creation_date TEXT, bin LONGTEXT, PRIMARY KEY (cid) + // cnacs(cid VARCHAR(20) NOT NULL, is_connected BOOL, is_personal BOOL, username VARCHAR({}) UNIQUE, full_name TEXT, creation_date TEXT, bin LONGTEXT, PRIMARY KEY (cid)) let query = self.format("SELECT is_personal, username, full_name, creation_date FROM cnacs WHERE cid = ? LIMIT 1"); let query: Option = gen_query!(sqlx::query(&query), self, implicated_cid) .fetch_optional(conn) @@ -454,7 +508,7 @@ impl BackendConnection for SqlBackend limit: Option, ) -> Result, AccountError> { let conn = &(self.get_conn().await?); - // cnacs(cid VARCHAR(20) NOT NULL, is_connected BOOL, is_personal BOOL, username VARCHAR({}) UNIQUE, full_name TEXT, creation_date TEXT, bin LONGTEXT, PRIMARY KEY (cid) + // cnacs(cid VARCHAR(20) NOT NULL, is_connected BOOL, is_personal BOOL, username VARCHAR({}) UNIQUE, full_name TEXT, creation_date TEXT, bin LONGTEXT, PRIMARY KEY (cid)) let query = if let Some(limit) = limit { format!( "SELECT cid, is_personal, username, full_name, creation_date FROM cnacs LIMIT {limit}", diff --git a/citadel_user/src/backend/utils/mod.rs b/citadel_user/src/backend/utils/mod.rs index 1a51184c7..5ac3eb61a 100644 --- a/citadel_user/src/backend/utils/mod.rs +++ b/citadel_user/src/backend/utils/mod.rs @@ -1,3 +1,43 @@ +//! Backend Object Transfer Utilities +//! +//! This module provides utilities for handling object transfers between peers in the +//! Citadel network, managing both sending and receiving operations asynchronously. +//! +//! # Features +//! +//! * **Transfer Management** +//! - Bidirectional file transfers +//! - Progress tracking +//! - Transfer status updates +//! - Stream-based operations +//! +//! * **Control Flow** +//! - Transfer acceptance/rejection +//! - Stream exhaustion +//! - Error handling +//! - Resource cleanup +//! +//! * **Async Support** +//! - Non-blocking operations +//! - Channel-based communication +//! - Task coordination +//! - Resource management +//! +//! # Important Notes +//! +//! * Transfer handlers must be properly closed +//! * Receivers must explicitly accept transfers +//! * Progress updates are streamed asynchronously +//! * Resource cleanup is automatic on drop +//! * Transfer orientation determines available operations +//! +//! # Related Components +//! +//! * `BackendConnection` - Uses transfer utilities +//! * `VirtualObjectMetadata` - Transfer metadata +//! * `ObjectTransferStatus` - Progress updates +//! * `AccountManager` - Initiates transfers + use futures::Stream; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; diff --git a/citadel_user/src/client_account.rs b/citadel_user/src/client_account.rs index 63f0b7527..c766fc95e 100644 --- a/citadel_user/src/client_account.rs +++ b/citadel_user/src/client_account.rs @@ -1,3 +1,92 @@ +//! # Client Network Account Management +//! +//! This module provides comprehensive client account management functionality for the Citadel Protocol. +//! It handles both personal and impersonal connection modes, secure credential management, and peer relationships +//! within HyperLAN and HyperWAN networks. +//! +//! ## Features +//! +//! * **Connection Modes** +//! - Personal mode with full authentication and encryption +//! - Impersonal mode for temporary or anonymous connections +//! +//! * **Security** +//! - Secure credential storage and validation +//! - Ratchet-based cryptographic state management +//! - Immutable critical security fields +//! +//! * **Network Management** +//! - HyperLAN and HyperWAN peer relationship handling +//! - Peer list synchronization +//! - P2P connection support +//! - Connection endpoint configuration +//! +//! * **Thread Safety** +//! - All operations are thread-safe through RwLock +//! - Concurrent access to shared resources +//! +//! ## Important Notes +//! +//! 1. Always use strong passwords and proper credential management +//! 2. Keep cryptographic state synchronized between peers +//! 3. Handle connection errors and implement proper retry logic +//! 4. Regularly clean up stale peer connections +//! 5. Monitor connection quality and implement appropriate fallbacks +//! +//! ## Related Components +//! +//! * `NetworkMode` - Defines the network operation mode +//! * `PeerConnection` - Manages individual peer connections +//! * `SecurityState` - Handles cryptographic state +//! * `EndpointConfig` - Configures connection endpoints +//! +//! Manages individual client connections within the Citadel Protocol network. Each ClientNetworkAccount +//! represents a unique connection endpoint, handling authentication, peer relationships, and cryptographic +//! state for both personal and impersonal connection modes. +//! +//! ## Features +//! +//! * **Connection Management**: +//! - Personal and impersonal connection modes +//! - HyperLAN and HyperWAN peer management +//! - Connection state tracking +//! - Network endpoint configuration +//! +//! * **Authentication**: +//! - Secure credential management +//! - Password-based authentication +//! - Passwordless authentication support +//! - Credential validation and generation +//! +//! * **Cryptographic Operations**: +//! - Ratchet-based key management +//! - Session crypto state handling +//! - Static and dynamic key management +//! - Forward secrecy support +//! +//! * **Peer Management**: +//! - HyperLAN peer registration +//! - Peer list synchronization +//! - Mutual peer relationship tracking +//! - P2P connection support +//! +//! +//! ## Important Notes +//! +//! * Each account represents a unique connection endpoint +//! * Thread-safe operations through RwLock protection +//! * Supports both personal and impersonal connection modes +//! * Automatic peer list synchronization with HyperLAN server +//! * Critical fields (cid, adjacent_nid, is_personal) are immutable +//! +//! ## Related Components +//! +//! * `auth`: Authentication and credential management +//! * `network_account`: Network node configuration +//! * `hypernode_account`: Base account functionality +//! * `citadel_crypt`: Cryptographic operations +//! + use serde::{Deserialize, Serialize}; use std::sync::Arc; diff --git a/citadel_user/src/connection_metadata.rs b/citadel_user/src/connection_metadata.rs new file mode 100644 index 000000000..174fece15 --- /dev/null +++ b/citadel_user/src/connection_metadata.rs @@ -0,0 +1,103 @@ +//! # Connection Metadata Management +//! +//! This module manages connection metadata for the Citadel Protocol, handling +//! connection information storage and protocol specifications for client-side +//! connections. +//! +//! ## Features +//! +//! * **Connection Information** +//! - Socket address storage +//! - Connection state persistence +//! - Connection display formatting +//! +//! * **Protocol Support** +//! - TCP connections +//! - TLS with optional domain +//! - QUIC with optional domain +//! +//! * **Serialization** +//! - Serde compatibility +//! - Debug formatting +//! - Display implementation +//! +//! ## Usage Example +//! +//! ```rust +//! use citadel_user::connection_metadata::{ConnectionInfo, ConnectProtocol}; +//! use std::net::{SocketAddr, IpAddr, Ipv4Addr}; +//! +//! fn manage_connections() { +//! // Create connection info +//! let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); +//! let connection = ConnectionInfo { addr }; +//! +//! // Create different protocol types +//! let tcp = ConnectProtocol::Tcp; +//! let tls = ConnectProtocol::Tls(Some("example.com".to_string())); +//! let quic = ConnectProtocol::Quic(Some("quic.example.com".to_string())); +//! +//! // Get domain information +//! assert_eq!(tcp.get_domain(), None); +//! assert_eq!(tls.get_domain(), Some("example.com".to_string())); +//! assert_eq!(quic.get_domain(), Some("quic.example.com".to_string())); +//! +//! // Display connection info +//! println!("Connection: {}", connection); +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * Connection info is serializable for persistence +//! * Protocol types support optional domain names +//! * TCP connections don't use domain information +//! * Connection display shows socket address +//! * All types implement Clone and Debug +//! +//! ## Related Components +//! +//! * `ClientNetworkAccount` - Uses connection metadata +//! * `AccountManager` - Manages connection states +//! * `PersistenceHandler` - Stores connection info +//! * `citadel_wire` - Network communication +//! + +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; +use std::net::SocketAddr; + +#[derive(Serialize, Deserialize, Debug, Clone)] +/// For saving the state of client-side connections +pub struct ConnectionInfo { + /// The address of the adjacent node + pub addr: SocketAddr, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +/// For saving the state of client-side connections +pub enum ConnectProtocol { + /// Uses the transmission control protocol + Tcp, + /// The domain + Tls(Option), + /// Quic + Quic(Option), +} + +impl ConnectProtocol { + /// Gets domain + pub fn get_domain(&self) -> Option { + match self { + Self::Tcp => None, + Self::Tls(t) => t.clone(), + Self::Quic(t) => t.clone(), + } + } +} + +impl Display for ConnectionInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "addr: {}", self.addr) + } +} diff --git a/citadel_user/src/credentials.rs b/citadel_user/src/credentials.rs index 3abd3596b..4ea2e71fa 100644 --- a/citadel_user/src/credentials.rs +++ b/citadel_user/src/credentials.rs @@ -1,17 +1,108 @@ +//! # Credential Management +//! +//! This module provides functionality for managing and validating user credentials +//! in the Citadel Protocol. It enforces consistent requirements for usernames, +//! passwords, and full names across the system. +//! +//! ## Features +//! +//! * **Credential Validation** +//! - Username format and length checks +//! - Password complexity requirements +//! - Full name format validation +//! +//! * **Configurable Requirements** +//! - Customizable length limits +//! - Default security policies +//! - Format restrictions +//! +//! * **Security Constraints** +//! - No spaces in usernames (use periods) +//! - No spaces in passwords +//! - Length boundaries for all fields +//! +//! ## Usage Example +//! +//! ```rust +//! use citadel_user::credentials::CredentialRequirements; +//! +//! fn validate_credentials() -> Result<(), Box> { +//! // Create default requirements +//! let requirements = CredentialRequirements::default(); +//! +//! // Validate credentials +//! requirements.check( +//! "john.doe", +//! Some("secure_pass123"), +//! "John Doe" +//! )?; +//! +//! // Create custom requirements +//! let custom_requirements = CredentialRequirements { +//! min_password_length: 10, +//! max_password_length: 20, +//! min_username_length: 5, +//! max_username_length: 30, +//! min_name_length: 3, +//! max_name_length: 50, +//! }; +//! +//! // Validate with custom requirements +//! custom_requirements.check( +//! "alice.smith", +//! Some("very_secure_pass"), +//! "Alice Smith" +//! )?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * Default length requirements: +//! - Username: 3-37 characters +//! - Password: 7-17 characters +//! - Full Name: 2-77 characters +//! +//! * Validation rules: +//! - Usernames must not contain spaces (use periods) +//! - Passwords must not contain spaces +//! - Full names can contain spaces +//! - All fields have minimum and maximum lengths +//! +//! * Validation only checks format, not availability +//! +//! ## Related Components +//! +//! * `AccountManager` - Uses credentials for account creation +//! * `ProposedCredentials` - Credential proposal handling +//! * `ClientNetworkAccount` - Account credential storage +//! * `AccountError` - Credential validation errors +//! + use crate::misc::AccountError; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone)] +/// Represents the requirements for user credentials. pub struct CredentialRequirements { + /// The minimum length of a password. pub min_password_length: u8, + /// The maximum length of a password. pub max_password_length: u8, + /// The minimum length of a username. pub min_username_length: u8, + /// The maximum length of a username. pub max_username_length: u8, + /// The minimum length of a full name. pub min_name_length: u8, + /// The maximum length of a full name. pub max_name_length: u8, } impl Default for CredentialRequirements { + /// Returns the default `CredentialRequirements` instance. fn default() -> Self { Self { min_password_length: MIN_PASSWORD_LENGTH, @@ -25,8 +116,18 @@ impl Default for CredentialRequirements { } impl CredentialRequirements { - /// Used to determine if the desired credentials have a valid format, length, etc. This alone DOES NOT imply whether or not the - /// credentials are available + /// Checks if the provided credentials meet the requirements. + /// + /// # Arguments + /// + /// * `username` - The username to check. + /// * `password` - The password to check (optional). + /// * `full_name` - The full name to check. + /// + /// # Returns + /// + /// * `Ok(())` if the credentials are valid. + /// * `Err(AccountError)` if the credentials are invalid. pub fn check, R: AsRef, V: AsRef>( &self, username: T, @@ -82,14 +183,20 @@ impl CredentialRequirements { } } +/// The minimum length of a password. pub const MIN_PASSWORD_LENGTH: u8 = 7; +/// The maximum length of a password. pub const MAX_PASSWORD_LENGTH: u8 = 17; +/// The minimum length of a username. pub const MIN_USERNAME_LENGTH: u8 = 3; +/// The maximum length of a username. pub const MAX_USERNAME_LENGTH: u8 = 37; +/// The minimum length of a full name. pub const MIN_NAME_LENGTH: u8 = 2; +/// The maximum length of a full name. pub const MAX_NAME_LENGTH: u8 = 77; diff --git a/citadel_user/src/directory_store.rs b/citadel_user/src/directory_store.rs index 5e95bcbc9..0b4c55dfd 100644 --- a/citadel_user/src/directory_store.rs +++ b/citadel_user/src/directory_store.rs @@ -1,3 +1,88 @@ +//! # Directory Store Management +//! +//! This module manages the filesystem structure for the Citadel Protocol, +//! handling directory creation, path management, and file organization for +//! both client and server applications. +//! +//! ## Features +//! +//! * **Directory Structure** +//! - Home directory management (.citadel) +//! - Account storage organization +//! - Server and client configuration +//! - Virtual filesystem support +//! +//! * **Path Management** +//! - Cross-platform path handling +//! - Standardized path formatting +//! - Directory hierarchy maintenance +//! - File path generation +//! +//! * **Storage Organization** +//! - Personal account storage +//! - Impersonal account storage +//! - File transfer management +//! - Configuration storage +//! +//! ## Directory Structure +//! +//! ```text +//! .citadel/ +//! ├── accounts/ +//! │ ├── personal/ # Personal account storage +//! │ └── impersonal/ # Impersonal account storage +//! ├── server/ # Server-specific files +//! ├── config/ # Configuration files +//! ├── virtual/ # Virtual encrypted filesystem +//! └── transfers/ # File transfer storage +//! ``` +//! +//! ## Usage Example +//! +//! ```rust +//! use citadel_user::directory_store::{DirectoryStore, BasePath, setup_directories}; +//! use std::path::PathBuf; +//! +//! fn manage_directories() -> Result<(), Box> { +//! // Initialize directory structure +//! let store = setup_directories(String::from("/home/user"))?; +//! +//! // Generate paths for different purposes +//! let config_path: PathBuf = store.make_path( +//! BasePath::ConfigDir, +//! "settings.conf" +//! ); +//! +//! let account_path: PathBuf = store.make_path( +//! BasePath::NacDirPersonal, +//! "user123.hca" +//! ); +//! +//! let transfer_path: PathBuf = store.make_path( +//! BasePath::FileTransferDir, +//! "download.tmp" +//! ); +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * All paths are automatically formatted for the target OS +//! * Directory structure is created on initialization +//! * File names in storage are obfuscated (50 chars) +//! * Base directory is `.citadel` in the user's home +//! * Paths are managed through the `BasePath` enum +//! +//! ## Related Components +//! +//! * `AccountManager` - Uses directory store for account storage +//! * `ClientNetworkAccount` - Stored in account directories +//! * `FilesystemBackend` - Interacts with directory structure +//! * `VirtualFilesystem` - Uses virtual directory +//! + use crate::misc::{format_path, AccountError}; use std::fs::create_dir_all as mkdir; use std::path::PathBuf; diff --git a/citadel_user/src/external_services/google_auth.rs b/citadel_user/src/external_services/google_auth.rs index 7b6679496..27a51d91b 100644 --- a/citadel_user/src/external_services/google_auth.rs +++ b/citadel_user/src/external_services/google_auth.rs @@ -1,3 +1,46 @@ +//! # Google Authentication Service +//! +//! Provides functionality for generating custom JWT tokens for Google Firebase authentication. +//! This module enables server-side authentication token generation using Google service account +//! credentials. +//! +//! ## Features +//! +//! * Load Google service account credentials from JSON file +//! * Generate custom JWT tokens for Firebase authentication +//! * RSA private key management with SHA-256 signing +//! * Configurable token expiration +//! +//! ## Usage Example +//! +//! ```rust,no_run +//! use citadel_user::external_services::google_auth::GoogleAuth; +//! +//! async fn authenticate_user(user_id: &str) -> Result> { +//! // Load service account credentials +//! let auth = GoogleAuth::load_from_google_services_file("path/to/service-account.json").await?; +//! +//! // Generate custom JWT token +//! let token = auth.sign_new_custom_jwt_auth(user_id)?; +//! +//! Ok(token.to_string()) +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * Requires a valid Google service account JSON file with private key +//! * Service account must have appropriate Firebase permissions +//! * JWT tokens are signed using RSA SHA-256 +//! * Default token expiration is 1 hour +//! +//! ## Related Components +//! +//! * `JsonWebToken`: Type representing a signed JWT token +//! * `AccountError`: Error type for authentication operations +//! * Firebase Admin SDK: External service for token verification +//! + use crate::external_services::JsonWebToken; use crate::misc::AccountError; use jwt::{PKeyWithDigest, SignWithKey}; diff --git a/citadel_user/src/external_services/mod.rs b/citadel_user/src/external_services/mod.rs index 1c6bbf472..b712d6de7 100644 --- a/citadel_user/src/external_services/mod.rs +++ b/citadel_user/src/external_services/mod.rs @@ -1,3 +1,48 @@ +//! # External Services Integration +//! +//! This module provides integration with external services, primarily focusing on +//! Google services such as Firebase Realtime Database (RTDB) and Firebase Authentication. +//! It manages service configuration, authentication, and state handling. +//! +//! ## Features +//! +//! * Google services integration (behind feature flag) +//! * Firebase Realtime Database support +//! * Custom JWT authentication +//! * Service configuration management +//! * Post-login service initialization +//! * WASM compatibility checks +//! +//! ## Usage Example +//! +//! ```rust,no_run +//! use citadel_user::external_services::{ServicesConfig, RtdbConfig}; +//! +//! // Create service configuration +//! let config = ServicesConfig { +//! google_services_json_path: Some("path/to/service-account.json".to_string()), +//! google_rtdb: Some(RtdbConfig::default()), +//! }; +//! +//! // Initialize services handler +//! let handler = config.into_services_handler()?; +//! ``` +//! +//! ## Important Notes +//! +//! * Google services require the "google-services" feature flag +//! * Some features are not available in WASM environments +//! * Service account JSON is required for server-side operations +//! * JWT tokens are managed automatically +//! +//! ## Related Components +//! +//! * `GoogleAuth`: Firebase Authentication integration +//! * `RtdbInstance`: Firebase Realtime Database client +//! * `ServicesHandler`: Main service management interface +//! * `ServicesConfig`: Service configuration container +//! + /// For services #[cfg(feature = "google-services")] pub mod google_auth; diff --git a/citadel_user/src/external_services/rtdb.rs b/citadel_user/src/external_services/rtdb.rs index 66f01b234..4b3c81ea5 100644 --- a/citadel_user/src/external_services/rtdb.rs +++ b/citadel_user/src/external_services/rtdb.rs @@ -1,3 +1,52 @@ +//! # Firebase Realtime Database Integration +//! +//! This module provides integration with Firebase Realtime Database (RTDB), enabling +//! real-time data synchronization between clients and servers. It handles authentication, +//! connection management, and data transfer operations. +//! +//! ## Features +//! +//! * Firebase RTDB client integration +//! * JWT-based authentication +//! * Automatic token expiration handling +//! * Connection refresh support +//! * Real-time data synchronization +//! * Client configuration management +//! +//! ## Usage Example +//! +//! ```rust,no_run +//! use citadel_user::external_services::rtdb::{RtdbClientConfig, RtdbInstance}; +//! +//! // Create client configuration +//! let config = RtdbClientConfig { +//! url: "https://your-db.firebaseio.com".to_string(), +//! api_key: "your-api-key".to_string(), +//! // ... other fields ... +//! }; +//! +//! // Initialize RTDB instance +//! let mut rtdb = RtdbInstance::new(&config)?; +//! +//! // Refresh connection if needed +//! rtdb.refresh()?; +//! ``` +//! +//! ## Important Notes +//! +//! * Requires valid Firebase configuration +//! * Handles token expiration automatically +//! * Supports connection refresh for long-running instances +//! * Implements efficient data synchronization +//! +//! ## Related Components +//! +//! * `FirebaseRTDB`: Underlying RTDB client +//! * `ExternalServiceChannel`: Data transfer interface +//! * `RawExternalPacket`: Data packet format +//! * `JsonWebToken`: Authentication token type +//! + use crate::external_services::service_interface::{ExternalServiceChannel, RawExternalPacket}; use crate::external_services::JsonWebToken; use crate::misc::AccountError; diff --git a/citadel_user/src/external_services/service_interface.rs b/citadel_user/src/external_services/service_interface.rs index fd8cfc884..3cb40d606 100644 --- a/citadel_user/src/external_services/service_interface.rs +++ b/citadel_user/src/external_services/service_interface.rs @@ -1,3 +1,52 @@ +//! # External Service Interface +//! +//! This module defines the core interface for external service communication in the Citadel Protocol. +//! It provides a unified way to interact with various external services through a common trait. +//! +//! ## Features +//! +//! * Common interface for external services +//! * Asynchronous data transmission +//! * Error handling with AccountError +//! * Raw packet data support +//! * Peer-to-peer communication +//! +//! ## Usage Example +//! +//! ```rust,no_run +//! use citadel_user::external_services::service_interface::{ExternalServiceChannel, RawExternalPacket}; +//! use async_trait::async_trait; +//! +//! struct MyService; +//! +//! #[async_trait] +//! impl ExternalServiceChannel for MyService { +//! async fn send( +//! &mut self, +//! data: RawExternalPacket, +//! implicated_cid: u64, +//! peer_cid: u64, +//! ) -> Result<(), AccountError> { +//! // Implement service-specific send logic +//! Ok(()) +//! } +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * Implementations must be thread-safe +//! * Services should handle reconnection +//! * Error handling is standardized +//! * Data format is raw bytes +//! +//! ## Related Components +//! +//! * `RawExternalPacket`: Data packet type +//! * `AccountError`: Error handling type +//! * `async_trait`: Async trait support +//! + use crate::misc::AccountError; use async_trait::async_trait; diff --git a/citadel_user/src/hypernode_account.rs b/citadel_user/src/hypernode_account.rs index 8850d743b..5012e8204 100644 --- a/citadel_user/src/hypernode_account.rs +++ b/citadel_user/src/hypernode_account.rs @@ -1,3 +1,77 @@ +//! # Network Account Management +//! +//! This module provides core functionality for network account operations and user identification +//! in the Citadel Protocol. It defines the extension trait for user identifiers and handles +//! account searching and peer relationships. +//! +//! ## Features +//! +//! * **User Identification** +//! - CID-based identification +//! - Username-based lookup +//! - Flexible identifier conversion +//! +//! * **Account Search** +//! - Account lookup by CID +//! - Account lookup by username +//! - Peer relationship search +//! +//! * **Peer Management** +//! - Mutual peer relationship tracking +//! - Network peer lookup +//! - Peer information retrieval +//! +//! ## Usage Example +//! +//! ```rust +//! use citadel_user::prelude::*; +//! use citadel_types::user::UserIdentifier; +//! +//! async fn example() -> Result<(), Box> { +//! // Create account manager +//! let manager = AccountManager::new( +//! BackendType::InMemory, +//! None, +//! None, +//! None +//! ).await?; +//! +//! // Search by CID +//! let user_by_id = UserIdentifier::ID(1234); +//! if let Some(account) = user_by_id.search(&manager).await? { +//! println!("Found account by CID"); +//! } +//! +//! // Search by username +//! let user_by_name = UserIdentifier::Username("alice".to_string()); +//! if let Some(account) = user_by_name.search(&manager).await? { +//! println!("Found account by username"); +//! } +//! +//! // Search for peer relationship +//! if let Some(peer) = user_by_id.search_peer(5678, &manager).await? { +//! println!("Found peer relationship"); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * CIDs are unique identifiers for network accounts +//! * Username lookups are case-insensitive +//! * Peer relationships are bidirectional +//! * Search operations are async and fallible +//! +//! ## Related Components +//! +//! * `AccountManager`: Core account management +//! * `UserIdentifier`: User identification types +//! * `ClientNetworkAccount`: Client account type +//! * `MutualPeer`: Peer relationship type +//! + use crate::account_manager::AccountManager; use crate::misc::AccountError; use crate::prelude::ClientNetworkAccount; diff --git a/citadel_user/src/lib.rs b/citadel_user/src/lib.rs index 5ff9dad98..ddbee1a7a 100644 --- a/citadel_user/src/lib.rs +++ b/citadel_user/src/lib.rs @@ -1,7 +1,79 @@ +//! # Citadel User Management System +//! +//! A comprehensive user and account management system for the Citadel Protocol, handling both +//! network nodes and client accounts within the VPN architecture. This crate provides +//! the foundational user management layer for the entire Citadel Protocol ecosystem. +//! +//! ## Features +//! +//! * **Account System**: +//! - Network Accounts: Core network identity +//! - Client Accounts: Per-connection user accounts +//! +//! * **Backend Support**: +//! - File System Storage: Persistent local storage +//! - Redis Database: High-performance caching +//! - SQL Database: Relational data storage +//! - In-Memory Storage: Fast temporary storage +//! +//! * **Authentication**: +//! - Secure Credential Management: Password and key handling +//! - Google Authentication: OAuth and service account support +//! - Custom Authentication: Extensible provider system +//! +//! * **External Services**: +//! - Google Services: Cloud service integration +//! - Firebase RTDB: Real-time data synchronization +//! - Service Interface: Common communication layer +//! +//! * **Account Management**: +//! - Account Creation: Secure account initialization +//! - Credential Updates: Safe password and key rotation +//! - State Management: Account lifecycle handling +//! - Account Recovery: Backup and restore features +//! +//! ## Architecture +//! +//! The system is built on a network-client account structure: +//! +//! ```text +//! Network Account (NAC) +//! └── Client Account (CNAC) +//! ├── Connection Metadata +//! ├── Credentials +//! └── External Services +//! ``` +//! +//! ## Security Features +//! +//! * Zero-trust architecture +//! * Post-quantum cryptography support +//! * Secure credential storage +//! * Safe account recovery +//! * Encrypted data transmission +//! +//! ## Important Notes +//! +//! * Multiple ClientAccounts can exist per node +//! * All operations are safe and secure by default +//! * File system operations are feature-gated, enabled by default +//! * External services require appropriate feature flags +//! +//! ## Related Components +//! +//! * [`citadel_crypt`]: Cryptographic operations +//! * [`citadel_wire`]: Network communication +//! * [`citadel_types`]: Common type definitions +//! * [`citadel_pqcrypto`]: Post-quantum cryptography +//! +//! ## Feature Flags +//! +//! * `filesystem`: Enable file system storage +//! * `google-services`: Enable Google service integration +//! * `redis`: Enable Redis database support +//! * `sql`: Enable SQL database support +//! #![forbid(unsafe_code)] -//! This crate is meant for containing the user-related libraries for HyperNode accounts. Both NetworkAccount and ClientAccount's are a subset of HyperNode accounts. -//! Every node/device necessarily contains a singular NetworkAccount; for each connection leading into and out of the node, a ClientAccount exists. - #![deny( trivial_numeric_casts, unused_extern_crates, @@ -15,8 +87,8 @@ /// Standard imports for this library pub mod prelude { pub use crate::client_account::*; + pub use crate::connection_metadata::*; pub use crate::hypernode_account::*; - pub use crate::network_account::*; pub use citadel_crypt::streaming_crypt_scrambler::MAX_BYTES_PER_GROUP; } @@ -34,9 +106,9 @@ pub mod hypernode_account; /// Each node must necessarily have a NetworkAccount that is invariant to any ClientAccounts. /// See the description for [client_account] below for more information. -pub mod network_account; +pub mod connection_metadata; -/// Each client within a HyperVPN has a unique ClientAccount. Multiple CAC's are possible per node. +/// Each client within a VPN has a unique ClientAccount. Multiple CAC's are possible per node. /// /// Structural design notes: In production mode, it is necessary that a [ClientNetworkAccount] be /// created by virtue of the subroutines within the [NetworkAccount]. In other words, a NAC is not @@ -45,7 +117,7 @@ pub mod network_account; /// be called a NAC. A NAC is necessary to connect and create mutually-trusted connections within /// the WAN (Wide-area network). /// -/// evoc_null(web 3.0) => void && let void alloc finite && set network evoc_null(!HyperWAN) +/// evoc_null(web 3.0) => void && let void alloc finite && set network evoc_null(!VPN) pub mod client_account; #[cfg(feature = "filesystem")] diff --git a/citadel_user/src/misc.rs b/citadel_user/src/misc.rs index bffc04557..3927aed4f 100644 --- a/citadel_user/src/misc.rs +++ b/citadel_user/src/misc.rs @@ -1,3 +1,40 @@ +//! Miscellaneous Utilities and Error Handling +//! +//! This module provides common utilities, error types, and helper functions used +//! throughout the Citadel user management system. +//! +//! # Features +//! +//! * **Error Handling** +//! - Account-specific error types +//! - Detailed error messages +//! - Error type conversion +//! +//! * **Metadata Management** +//! - Client Network Account (CNAC) metadata +//! - Timestamp formatting +//! - Account identification +//! +//! * **Path Management** +//! - Virtual path validation +//! - Cross-platform path formatting +//! - Directory structure validation +//! +//! # Important Notes +//! +//! * Error messages are designed to be user-friendly and descriptive +//! * Path validation enforces platform-specific requirements +//! * Timestamps use ISO 8601/RFC 3339 format for consistency +//! * CNAC metadata includes essential account information +//! * Virtual paths must follow specific formatting rules +//! +//! # Related Components +//! +//! * `AccountManager` - Uses error handling and metadata +//! * `DirectoryStore` - Uses path management utilities +//! * `ClientNetworkAccount` - Uses metadata structures +//! * `PersistenceHandler` - Uses error types + use chrono::Utc; use std::path::{Path, PathBuf}; diff --git a/citadel_user/src/network_account.rs b/citadel_user/src/network_account.rs deleted file mode 100644 index 4309a80b4..000000000 --- a/citadel_user/src/network_account.rs +++ /dev/null @@ -1,38 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter}; -use std::net::SocketAddr; - -#[derive(Serialize, Deserialize, Debug, Clone)] -/// For saving the state of client-side connections -pub struct ConnectionInfo { - /// The address of the adjacent node - pub addr: SocketAddr, -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -/// For saving the state of client-side connections -pub enum ConnectProtocol { - /// Uses the transmission control protocol - Tcp, - /// The domain - Tls(Option), - /// Quic - Quic(Option), -} - -impl ConnectProtocol { - /// Gets domain - pub fn get_domain(&self) -> Option { - match self { - Self::Tcp => None, - Self::Tls(t) => t.clone(), - Self::Quic(t) => t.clone(), - } - } -} - -impl Display for ConnectionInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "addr: {}", self.addr) - } -} diff --git a/citadel_user/src/serialization.rs b/citadel_user/src/serialization.rs index 8d82ad09f..e419768cb 100644 --- a/citadel_user/src/serialization.rs +++ b/citadel_user/src/serialization.rs @@ -1,3 +1,85 @@ +//! # Serialization Support +//! +//! This module provides efficient serialization and deserialization functionality +//! for Citadel Protocol types using bincode. It offers a trait-based approach +//! for consistent serialization across the system. +//! +//! ## Features +//! +//! * **Binary Serialization** +//! - Vector-based serialization +//! - Buffer-based serialization +//! - In-place deserialization +//! - Size estimation +//! +//! * **Format Support** +//! - Bincode encoding +//! - Bytes buffer integration +//! - Slice operations +//! - Reader/Writer support +//! +//! * **Performance Features** +//! - Size pre-allocation +//! - In-place operations +//! - Buffer reuse +//! - Memory efficiency +//! +//! ## Usage Example +//! +//! ```rust +//! use citadel_user::serialization::SyncIO; +//! use serde::{Serialize, Deserialize}; +//! +//! #[derive(Serialize, Deserialize)] +//! struct User { +//! id: u64, +//! name: String, +//! } +//! +//! fn handle_serialization() -> Result<(), Box> { +//! // Create test data +//! let user = User { +//! id: 1234, +//! name: "Alice".to_string(), +//! }; +//! +//! // Serialize to vector +//! let bytes = user.serialize_to_vector()?; +//! +//! // Deserialize from vector +//! let decoded: User = User::deserialize_from_vector(&bytes)?; +//! assert_eq!(decoded.id, user.id); +//! assert_eq!(decoded.name, user.name); +//! +//! // Use buffer for efficiency +//! let mut buffer = bytes::BytesMut::with_capacity(64); +//! user.serialize_into_buf(&mut buffer)?; +//! +//! // Get serialized size +//! if let Some(size) = user.serialized_size() { +//! println!("Serialized size: {} bytes", size); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Important Notes +//! +//! * Types must implement `Serialize` and `Deserialize` +//! * Buffer operations are more efficient for repeated use +//! * Size estimation helps with buffer pre-allocation +//! * In-place deserialization avoids allocations +//! * Error handling uses `AccountError` type +//! +//! ## Related Components +//! +//! * `AccountManager` - Uses serialization for persistence +//! * `ClientNetworkAccount` - Serializable account type +//! * `PersistenceHandler` - Handles serialized data storage +//! * `BackendType` - Storage backend configuration +//! + use crate::misc::AccountError; use bincode::BincodeRead; use bytes::BufMut; diff --git a/citadel_user/src/server_misc_settings.rs b/citadel_user/src/server_misc_settings.rs index 6b645e6ba..45a8afa14 100644 --- a/citadel_user/src/server_misc_settings.rs +++ b/citadel_user/src/server_misc_settings.rs @@ -1,3 +1,44 @@ +//! Server Miscellaneous Settings Management +//! +//! This module provides configuration settings for server nodes in the Citadel network, +//! focusing on authentication and credential management. +//! +//! # Features +//! +//! * Passwordless authentication control +//! * Credential requirement specifications +//! * Default settings configuration +//! +//! # Example +//! +//! ```rust +//! use citadel_user::server_misc_settings::ServerMiscSettings; +//! use citadel_user::credentials::CredentialRequirements; +//! +//! // Create custom server settings +//! let settings = ServerMiscSettings { +//! allow_passwordless: false, +//! credential_requirements: CredentialRequirements::default(), +//! }; +//! +//! // Or use default settings +//! let default_settings = ServerMiscSettings::default(); +//! assert!(default_settings.allow_passwordless); // Passwordless auth is enabled by default +//! ``` +//! +//! # Important Notes +//! +//! * Enabling passwordless authentication (`allow_passwordless`) should be done with caution +//! and only in trusted environments +//! * Credential requirements are enforced even when creating new accounts +//! * Default settings prioritize ease of use over security - modify as needed for production +//! +//! # Related Components +//! +//! * [`CredentialRequirements`] - Defines password and username requirements +//! * `AccountManager` - Uses these settings for account creation and authentication +//! * `HyperNodeAccount` - Server-side account management + use crate::credentials::CredentialRequirements; /// Miscellaneous settings for a node serving connections diff --git a/citadel_wire/src/error.rs b/citadel_wire/src/error.rs index 1733ddac0..d7013d036 100644 --- a/citadel_wire/src/error.rs +++ b/citadel_wire/src/error.rs @@ -1,3 +1,43 @@ +//! Error Types for Network Traversal and Firewall Operations +//! +//! This module defines error types specific to network traversal and firewall +//! operations in the Citadel Protocol. It handles errors from UPnP port forwarding, +//! hole punching, and other network-related operations. +//! +//! # Features +//! +//! - Custom error types for UPnP and hole punching operations +//! - Conversion traits between custom and standard IO errors +//! - Descriptive error messages for debugging +//! - Error categorization for different network scenarios +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::error::FirewallError; +//! use std::io::Error; +//! +//! // Create a UPnP error +//! let upnp_err = FirewallError::UPNP("Port mapping failed".to_string()); +//! +//! // Convert to standard IO error +//! let io_err: Error = upnp_err.into(); +//! ``` +//! +//! # Important Notes +//! +//! - `FirewallError::Skip` indicates operation should be skipped +//! - `FirewallError::NotApplicable` for unsupported operations +//! - `FirewallError::HolePunchExhausted` when all attempts fail +//! - Implements standard error traits for interoperability +//! +//! # Related Components +//! +//! - [`crate::standard::upnp_handler`] - UPnP operations +//! - [`crate::udp_traversal`] - Hole punching operations +//! - [`crate::hypernode_type`] - Node configuration +//! + use citadel_io::tokio::io::Error; use std::fmt::Formatter; diff --git a/citadel_wire/src/hypernode_type.rs b/citadel_wire/src/hypernode_type.rs index 1867900a3..2df2b2c02 100644 --- a/citadel_wire/src/hypernode_type.rs +++ b/citadel_wire/src/hypernode_type.rs @@ -1,3 +1,46 @@ +//! Node Type Configuration for Network Topology +//! +//! This module defines the network node types and their behaviors in the Citadel +//! Protocol. It supports both traditional client-server and peer-to-peer network +//! topologies. +//! +//! # Features +//! +//! - Server configuration with static IP addresses +//! - Peer configuration for residential NAT environments +//! - Automatic UPnP handling for peer nodes +//! - Fallback to NAT traversal when UPnP is unavailable +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::hypernode_type::NodeType; +//! use std::net::SocketAddr; +//! +//! // Configure a server with static IP +//! let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap(); +//! let server = NodeType::server(addr).unwrap(); +//! assert!(server.is_server()); +//! +//! // Configure a peer node (residential NAT) +//! let peer = NodeType::default(); +//! assert!(peer.is_peer()); +//! ``` +//! +//! # Important Notes +//! +//! - Server nodes must have stable, reachable IP addresses +//! - Peer nodes automatically attempt UPnP port forwarding +//! - Fallback to NAT traversal for symmetric NATs +//! - Socket addresses are validated during construction +//! +//! # Related Components +//! +//! - [`crate::udp_traversal`] - NAT traversal functionality +//! - [`crate::standard::upnp_handler`] - UPnP port forwarding +//! - [`crate::standard::socket_helpers`] - Socket utilities +//! + use std::net::{SocketAddr, ToSocketAddrs}; /// Used for determining the proper action when loading the server diff --git a/citadel_wire/src/lib.rs b/citadel_wire/src/lib.rs index 60d95247d..395364589 100644 --- a/citadel_wire/src/lib.rs +++ b/citadel_wire/src/lib.rs @@ -1,3 +1,34 @@ +//! Citadel Wire: Network Traversal and Connection Management +//! +//! This crate provides comprehensive networking utilities for establishing secure +//! peer-to-peer connections across residential NATs and firewalls. It implements +//! multiple NAT traversal strategies and secure connection protocols. +//! +//! # Features +//! +//! - UDP hole punching with multiple strategies +//! - NAT type detection and traversal analysis +//! - QUIC protocol support for secure connections +//! - TLS certificate and security management +//! - UPnP port mapping and gateway control +//! - Platform-specific socket configuration +//! - IPv4 and IPv6 support where available +//! +//! # Important Notes +//! +//! - Zero unsafe code policy enforced +//! - Async-first design with Tokio runtime +//! - Security-focused implementation +//! - Automatic fallback mechanisms +//! - Comprehensive error handling +//! +//! # Related Components +//! +//! - [`udp_traversal`] - UDP hole punching implementation +//! - [`standard`] - Core networking components +//! - [`hypernode_type`] - Node type definitions +//! - [`error`] - Error type definitions +//! //! Tools for punching holes through the firewall and network. This enables functionality across residential NATs #![forbid(unsafe_code)] #[cfg(not(target_family = "wasm"))] diff --git a/citadel_wire/src/standard/misc.rs b/citadel_wire/src/standard/misc.rs index c73b11244..415151a4b 100644 --- a/citadel_wire/src/standard/misc.rs +++ b/citadel_wire/src/standard/misc.rs @@ -1,3 +1,84 @@ +//! Certificate Format Conversion Utilities +//! +//! This module provides utilities for converting between different certificate +//! and key formats, particularly focusing on PKCS#12 to QUIC-compatible formats. +//! It handles the complexities of certificate chain management and private key +//! conversion. +//! +//! ## Example +//! +//! ```rust,no_run +//! use citadel_wire::misc; +//! use anyhow::Result; +//! use std::path::Path; +//! +//! async fn example() -> Result<()> { +//! // Read PKCS#12 file and convert to QUIC keys +//! let (certs, key) = misc::read_pkcs_12_der_to_quinn_keys( +//! "cert.p12", +//! "password" +//! )?; +//! +//! // Convert individual DER files +//! let cert_der = std::fs::read("cert.der")?; +//! let key_der = std::fs::read("key.der")?; +//! let (cert, key) = misc::cert_and_priv_key_der_to_quic_keys( +//! &cert_der, +//! &key_der +//! )?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Features +//! +//! - PKCS#12 DER file parsing +//! - Certificate chain extraction +//! - Private key conversion +//! - QUIC-compatible format generation +//! - OpenSSL to Rustls conversion +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::misc; +//! +//! fn convert_certificates() -> Result<(), anyhow::Error> { +//! // Read PKCS#12 file and convert to QUIC keys +//! let (certs, key) = misc::read_pkcs_12_der_to_quinn_keys( +//! "cert.p12", +//! "password" +//! )?; +//! +//! // Convert individual DER files +//! let cert_der = std::fs::read("cert.der")?; +//! let key_der = std::fs::read("key.der")?; +//! let (cert, key) = misc::cert_and_priv_key_der_to_quic_keys( +//! &cert_der, +//! &key_der +//! )?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - PKCS#12 passwords must be UTF-8 +//! - Certificate chains are preserved +//! - DER format is required for input +//! - Memory safety is maintained +//! - OpenSSL types are used internally +//! +//! # Related Components +//! +//! - [`crate::standard::tls`] - TLS configuration +//! - [`crate::standard::quic`] - QUIC protocol support +//! - [`crate::exports::Certificate`] - Certificate types +//! - [`crate::exports::PrivateKey`] - Key management +//! + use crate::exports::{Certificate, PrivateKey}; use openssl::pkcs12::ParsedPkcs12_2 as ParsedPkcs12; use openssl::pkey::{PKey, Private}; diff --git a/citadel_wire/src/standard/mod.rs b/citadel_wire/src/standard/mod.rs index a15b06b2c..567a24872 100644 --- a/citadel_wire/src/standard/mod.rs +++ b/citadel_wire/src/standard/mod.rs @@ -1,3 +1,58 @@ +//! Standard Network Protocol Components +//! +//! This module provides core networking components and utilities that form the +//! foundation of the Citadel Protocol's network stack. It includes implementations +//! for various standard protocols and network operations. +//! +//! # Features +//! +//! - QUIC protocol implementation +//! - TLS security and certificate management +//! - NAT traversal and identification +//! - UPnP port mapping and gateway control +//! - Socket creation and configuration +//! - Utility functions for network operations +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::standard::{ +//! nat_identification::NatType, +//! socket_helpers, +//! upnp_handler::UPnPHandler +//! }; +//! +//! async fn setup_network() -> Result<(), anyhow::Error> { +//! // Identify NAT type +//! let nat = NatType::identify(None)?; +//! +//! // Setup UPnP if available +//! if let Ok(upnp) = UPnPHandler::new(None).await { +//! println!("UPnP available: {}", upnp); +//! } +//! +//! // Create network sockets +//! let addr = "127.0.0.1:8080".parse()?; +//! let socket = socket_helpers::get_reuse_udp_socket(addr)?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Components are designed for peer-to-peer use +//! - IPv4 and IPv6 support where possible +//! - Security-first implementation approach +//! - Platform-specific behaviors handled +//! - Async-first design philosophy +//! +//! # Related Components +//! +//! - [`crate::udp_traversal`] - UDP hole punching +//! - [`crate::hypernode_type`] - Node type definitions +//! - [`crate::error`] - Error handling +//! pub mod misc; pub mod nat_identification; pub mod quic; diff --git a/citadel_wire/src/standard/nat_identification.rs b/citadel_wire/src/standard/nat_identification.rs index 80da590de..278e8208a 100644 --- a/citadel_wire/src/standard/nat_identification.rs +++ b/citadel_wire/src/standard/nat_identification.rs @@ -1,3 +1,52 @@ +//! NAT Type Identification and Traversal Analysis +//! +//! This module provides functionality to identify and analyze Network Address Translation (NAT) +//! configurations in network environments. It uses STUN servers to determine NAT behavior +//! and predict external address mappings for peer-to-peer connections. +//! +//! # Features +//! +//! - STUN-based NAT type detection +//! - Port and IP translation pattern analysis +//! - Predictive external address mapping +//! - IPv4 and IPv6 compatibility checks +//! - Multiple STUN server fallback support +//! - NAT traversal strategy determination +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::standard::nat_identification::NatType; +//! +//! async fn identify_nat() -> Result<(), anyhow::Error> { +//! // Identify NAT type using default STUN servers +//! let nat = NatType::identify(None)?; +//! +//! // Check if peer-to-peer connection is possible +//! if nat.traversal_type_required().is_direct() { +//! println!("Direct connection possible"); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - NAT detection requires active internet connection +//! - Multiple STUN requests are made for accurate detection +//! - Port prediction may fail with symmetric NATs +//! - IPv6 support depends on system configuration +//! - Detection timeout defaults to 5 seconds +//! +//! # Related Components +//! +//! - [`crate::udp_traversal`] - UDP hole punching implementation +//! - [`crate::standard::upnp_handler`] - UPnP port forwarding +//! - [`crate::standard::socket_helpers`] - Socket utility functions +//! - [`crate::error::FirewallError`] - Error handling for NAT operations +//! + use crate::error::FirewallError; use crate::socket_helpers::is_ipv6_enabled; use async_ip::IpAddressInfo; diff --git a/citadel_wire/src/standard/quic.rs b/citadel_wire/src/standard/quic.rs index b7ce4b2b0..8de28c1af 100644 --- a/citadel_wire/src/standard/quic.rs +++ b/citadel_wire/src/standard/quic.rs @@ -1,3 +1,54 @@ +//! QUIC Protocol Implementation for Secure Connections +//! +//! This module provides a high-level interface for establishing QUIC (Quick UDP Internet +//! Connections) connections. It supports both client and server roles with configurable +//! security options and NAT traversal capabilities. +//! +//! # Features +//! +//! - Client and server QUIC endpoint creation +//! - Self-signed and PKCS#12 certificate support +//! - Configurable TLS security levels +//! - NAT traversal-friendly transport configuration +//! - Bidirectional stream support +//! - Custom certificate verification options +//! - Async/await support with Tokio runtime +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::standard::quic::{QuicServer, QuicClient}; +//! use citadel_io::tokio::net::UdpSocket; +//! +//! async fn setup_quic() -> Result<(), anyhow::Error> { +//! // Create a self-signed server +//! let socket = UdpSocket::bind("127.0.0.1:0").await?; +//! let server = QuicServer::new_self_signed(socket)?; +//! +//! // Create a non-verifying client +//! let socket = UdpSocket::bind("127.0.0.1:0").await?; +//! let client = QuicClient::new_no_verify(socket)?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - QUIC requires UDP connectivity +//! - TLS 1.3 is mandatory for QUIC +//! - Certificate verification is configurable +//! - Self-signed certificates use 'localhost' domain +//! - Transport config optimized for NAT traversal +//! +//! # Related Components +//! +//! - [`crate::standard::nat_identification`] - NAT behavior analysis +//! - [`crate::standard::socket_helpers`] - UDP socket utilities +//! - [`crate::standard::tls`] - TLS configuration helpers +//! - [`crate::udp_traversal`] - UDP hole punching support +//! + use futures::Future; use quinn::{ Accept, ClientConfig, Connection, Endpoint, EndpointConfig, RecvStream, SendStream, diff --git a/citadel_wire/src/standard/socket_helpers.rs b/citadel_wire/src/standard/socket_helpers.rs index 64ac7faa7..a4e901d2a 100644 --- a/citadel_wire/src/standard/socket_helpers.rs +++ b/citadel_wire/src/standard/socket_helpers.rs @@ -1,3 +1,58 @@ +//! Socket Creation and Configuration Utilities +//! +//! This module provides a comprehensive set of utilities for creating and configuring +//! network sockets with proper settings for NAT traversal and peer-to-peer communication. +//! It handles platform-specific socket options and provides a unified interface for both +//! TCP and UDP protocols. +//! +//! # Features +//! +//! - TCP and UDP socket creation with proper options +//! - Platform-specific socket configuration handling +//! - IPv4 and IPv6 support with automatic mapping +//! - Socket reuse options for NAT traversal +//! - Connection timeout management +//! - Backlog configuration for TCP listeners +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::standard::socket_helpers; +//! use std::net::SocketAddr; +//! use std::time::Duration; +//! +//! async fn setup_sockets() -> Result<(), anyhow::Error> { +//! let addr: SocketAddr = "127.0.0.1:8080".parse()?; +//! +//! // Create UDP socket with address reuse +//! let udp = socket_helpers::get_reuse_udp_socket(addr)?; +//! +//! // Create TCP listener with default options +//! let tcp = socket_helpers::get_tcp_listener(addr)?; +//! +//! // Create TCP client with timeout +//! let stream = socket_helpers::get_tcp_stream(addr, Duration::from_secs(5))?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Socket reuse options are essential for NAT traversal +//! - Platform-specific behaviors are handled automatically +//! - IPv6 support requires system configuration +//! - TCP listeners have configurable connection backlogs +//! - Default timeouts are recommended for reliability +//! +//! # Related Components +//! +//! - [`crate::standard::nat_identification`] - NAT behavior analysis +//! - [`crate::udp_traversal`] - UDP hole punching implementation +//! - [`crate::standard::upnp_handler`] - UPnP port forwarding +//! - [`crate::error::FirewallError`] - Network error handling +//! + use citadel_io::tokio::net::{TcpListener, TcpStream, UdpSocket}; use socket2::{Domain, SockAddr, Socket, Type}; use std::net::{IpAddr, SocketAddr, SocketAddrV6}; diff --git a/citadel_wire/src/standard/tls.rs b/citadel_wire/src/standard/tls.rs index 6e7473983..42ae195d9 100644 --- a/citadel_wire/src/standard/tls.rs +++ b/citadel_wire/src/standard/tls.rs @@ -1,3 +1,55 @@ +//! TLS Configuration and Certificate Management +//! +//! This module provides TLS (Transport Layer Security) configuration utilities with +//! support for both traditional TLS and QUIC protocols. It handles certificate +//! management, validation, and secure connection establishment with flexible +//! security options. +//! +//! # Features +//! +//! - TLS and QUIC configuration interoperability +//! - Self-signed certificate generation +//! - PKCS#12 certificate support +//! - Native system certificate loading +//! - Custom certificate validation +//! - Async/await support with Tokio +//! - Rustls-based implementation +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::standard::tls; +//! +//! async fn setup_tls() -> Result<(), anyhow::Error> { +//! // Create self-signed server config +//! let server_config = tls::create_server_self_signed_config()?; +//! +//! // Load system certificates +//! let certs = tls::load_native_certs_async().await?; +//! +//! // Create secure client config +//! let client_config = tls::create_client_config(&[&certs])?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Native cert loading is expensive (~200ms) +//! - Self-signed certs use 'localhost' domain +//! - PKCS#12 passwords must be UTF-8 +//! - Supports TLS 1.2 and 1.3 +//! - Certificate chain validation is configurable +//! +//! # Related Components +//! +//! - [`crate::standard::quic`] - QUIC protocol support +//! - [`crate::exports::Certificate`] - Certificate types +//! - [`crate::exports::PrivateKey`] - Key management +//! - [`crate::standard::socket_helpers`] - Socket utilities +//! + use crate::exports::{Certificate, PrivateKey}; use crate::quic::generate_self_signed_cert; use rustls::{ClientConfig, RootCertStore}; diff --git a/citadel_wire/src/standard/upnp_handler.rs b/citadel_wire/src/standard/upnp_handler.rs index 06ac9c4d0..7e5105c83 100644 --- a/citadel_wire/src/standard/upnp_handler.rs +++ b/citadel_wire/src/standard/upnp_handler.rs @@ -1,3 +1,59 @@ +//! UPnP Port Mapping and Gateway Management +//! +//! This module provides Universal Plug and Play (UPnP) functionality for automatic +//! port forwarding and NAT traversal. It handles discovery of Internet Gateway +//! Devices and manages port mappings for both TCP and UDP protocols. +//! +//! # Features +//! +//! - Automatic gateway discovery and configuration +//! - Port mapping for TCP and UDP protocols +//! - External IP address discovery +//! - Configurable lease durations for port mappings +//! - Support for targeted and any-port forwarding +//! - Automatic local IP detection +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::standard::upnp_handler::UPnPHandler; +//! use igd::PortMappingProtocol; +//! use std::time::Duration; +//! +//! async fn setup_port_forwarding() -> Result<(), anyhow::Error> { +//! // Create UPnP handler with 5 second timeout +//! let upnp = UPnPHandler::new(Some(Duration::from_secs(5))).await?; +//! +//! // Forward external port 8080 to local port 3000 +//! upnp.open_firewall_port( +//! PortMappingProtocol::TCP, +//! Some(3600), // 1 hour lease +//! "my-service", +//! None, +//! 8080, +//! 3000 +//! ).await?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Requires UPnP-enabled router/gateway +//! - Port mappings may expire if lease duration set +//! - External ports may be already in use +//! - Gateway discovery has configurable timeout +//! - Only supports IPv4 addresses currently +//! +//! # Related Components +//! +//! - [`crate::standard::nat_identification`] - NAT behavior analysis +//! - [`crate::udp_traversal`] - Alternative NAT traversal +//! - [`crate::standard::socket_helpers`] - Socket utilities +//! - [`crate::error::FirewallError`] - Error handling +//! + use crate::error::FirewallError; use citadel_io::tokio::time::Duration; use igd::aio::Gateway; diff --git a/citadel_wire/src/udp_traversal/hole_punch_config.rs b/citadel_wire/src/udp_traversal/hole_punch_config.rs index f0abdbfb7..ae9966869 100644 --- a/citadel_wire/src/udp_traversal/hole_punch_config.rs +++ b/citadel_wire/src/udp_traversal/hole_punch_config.rs @@ -1,3 +1,84 @@ +//! Hole punch configuration module. +//! +//! This module provides the configuration structures for UDP hole punching. +//! +//! ## Example +//! +//! ```rust,no_run +//! use citadel_wire::udp_traversal::hole_punch_config::HolePunchConfig; +//! use citadel_wire::nat_identification::NatType; +//! use citadel_wire::error::FirewallError; +//! use citadel_io::tokio::net::UdpSocket; +//! use std::net::SocketAddr; +//! +//! async fn example() -> Result<(), FirewallError> { +//! let socket = UdpSocket::bind("0.0.0.0:0").await?; +//! let target_addr = "127.0.0.1:8080".parse::().unwrap(); +//! let peer_nat = NatType::identify(None).await?; +//! let config = HolePunchConfig::new(&peer_nat, &[target_addr], vec![socket]); +//! Ok(()) +//! } +//! ``` +//! +//! UDP Hole Punching Configuration +//! +//! This module provides configuration structures and utilities for UDP hole punching, +//! a NAT traversal technique that enables peer-to-peer connections between nodes +//! behind different NATs. It handles address prediction and socket preparation. +//! +//! # Features +//! +//! - NAT-aware address band configuration +//! - Port prediction for different NAT types +//! - Socket preparation for traversal +//! - IPv4 and IPv6 support +//! - Localhost testing capabilities +//! - Iterator-based address generation +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::udp_traversal::hole_punch_config::HolePunchConfig; +//! use citadel_wire::nat_identification::NatType; +//! use citadel_io::tokio::net::UdpSocket; +//! use std::net::SocketAddr; +//! +//! async fn setup_hole_punch() -> Result<(), anyhow::Error> { +//! let peer_nat = NatType::identify(None)?; +//! let peer_addr = "192.168.1.2:8080".parse()?; +//! let socket = UdpSocket::bind("0.0.0.0:0").await?; +//! +//! let config = HolePunchConfig::new( +//! &peer_nat, +//! &[peer_addr], +//! vec![socket] +//! ); +//! +//! for addrs in config { +//! // Try connecting to predicted addresses +//! println!("Trying addresses: {:?}", addrs); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Address prediction depends on NAT type +//! - Multiple sockets may be required +//! - Port ranges are NAT-behavior specific +//! - Local sockets must be pre-bound +//! - IPv6 requires system configuration +//! +//! # Related Components +//! +//! - [`crate::nat_identification`] - NAT behavior analysis +//! - [`crate::udp_traversal::udp_hole_puncher`] - Hole punching implementation +//! - [`crate::standard::socket_helpers`] - Socket utilities +//! - [`crate::standard::upnp_handler`] - Alternative NAT traversal +//! + use crate::nat_identification::NatType; use citadel_io::tokio::net::UdpSocket; use itertools::Itertools; diff --git a/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs b/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs index a1122e3cd..2de3f6b62 100644 --- a/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs +++ b/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs @@ -1,3 +1,56 @@ +//! Secure Configuration Exchange for NAT Traversal +//! +//! This module provides secure packet encryption and decryption for hole punching +//! configuration exchange. It ensures that sensitive network configuration data +//! remains confidential during the NAT traversal process, while supporting custom +//! STUN server configurations. +//! +//! # Features +//! +//! - Secure packet encryption/decryption +//! - Custom STUN server support +//! - Zero-copy packet handling +//! - Localhost testing mode +//! - Thread-safe design +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; +//! use bytes::BytesMut; +//! +//! // Create encryption functions +//! let encrypt = |data: &[u8]| BytesMut::from(data); +//! let decrypt = |data: &[u8]| Some(BytesMut::from(data)); +//! +//! // Create container with custom STUN servers +//! let stun_servers = vec!["stun.example.com:3478".to_string()]; +//! let container = HolePunchConfigContainer::new( +//! encrypt, +//! decrypt, +//! Some(stun_servers) +//! ); +//! +//! // Use container for secure packet exchange +//! let packet = container.generate_packet(b"config"); +//! let decrypted = container.decrypt_packet(&packet); +//! ``` +//! +//! # Important Notes +//! +//! - Encryption is disabled in localhost testing mode +//! - Functions must be Send + Sync for thread safety +//! - Packet encryption is zero-copy optimized +//! - STUN servers can be configured at runtime +//! - Custom encryption schemes can be injected +//! +//! # Related Components +//! +//! - [`crate::udp_traversal::linear::method3`] - NAT traversal method +//! - [`crate::standard::tls`] - TLS configuration +//! - [`crate::standard::quic`] - QUIC protocol support +//! + use bytes::BytesMut; use std::sync::Arc; diff --git a/citadel_wire/src/udp_traversal/linear/method3.rs b/citadel_wire/src/udp_traversal/linear/method3.rs index 90b50afb5..7f4d0dc06 100644 --- a/citadel_wire/src/udp_traversal/linear/method3.rs +++ b/citadel_wire/src/udp_traversal/linear/method3.rs @@ -1,3 +1,64 @@ +//! Advanced UDP Hole Punching Implementation +//! +//! This module implements Method 3 UDP hole punching as described in the paper +//! "NAT Traversal Techniques in Peer-to-Peer Applications". The technique uses +//! variable TTL values to optimize NAT traversal success rates, with a fallback +//! recovery mode for asymmetric failures. +//! +//! # Features +//! +//! - Progressive TTL packet transmission +//! - Asymmetric failure recovery +//! - Dual-stack IPv4/IPv6 support +//! - Encrypted configuration exchange +//! - Connection state tracking +//! - Mutual exclusion for UDP sockets +//! +//! # Examples +//! +//! ```rust,no_run +//! use citadel_wire::udp_traversal::linear::method3::Method3; +//! use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; +//! use citadel_wire::nat_identification::NatType; +//! use citadel_wire::error::FirewallError; +//! use citadel_io::tokio::net::UdpSocket; +//! use std::net::SocketAddr; +//! use netbeam::sync::RelativeNodeType; +//! use anyhow::Result; +//! +//! async fn example() -> Result<(), FirewallError> { +//! // Create socket and config +//! let socket = UdpSocket::bind("0.0.0.0:0").await?; +//! let target_addr = "127.0.0.1:8080".parse::().unwrap(); +//! let config = HolePunchConfigContainer::default(); +//! +//! // Create method3 instance +//! let method = Method3::new( +//! RelativeNodeType::Initiator, +//! config, +//! Default::default() +//! ); +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Requires pre-process stage completion +//! - TTL values affect traversal success +//! - Recovery mode handles asymmetric failures +//! - Thread-safe socket operations +//! - Encrypted packet exchange +//! +//! # Related Components +//! +//! - [`crate::udp_traversal::linear::encrypted_config_container`] - Config encryption +//! - [`crate::socket_helpers`] - Socket utilities +//! - [`crate::nat_identification`] - NAT analysis +//! - [`crate::standard::upnp_handler`] - UPnP support +//! + use std::collections::{HashMap, HashSet}; use std::io::ErrorKind; use std::net::SocketAddr; diff --git a/citadel_wire/src/udp_traversal/linear/mod.rs b/citadel_wire/src/udp_traversal/linear/mod.rs index 64e5bbc25..cfdaa352f 100644 --- a/citadel_wire/src/udp_traversal/linear/mod.rs +++ b/citadel_wire/src/udp_traversal/linear/mod.rs @@ -1,3 +1,63 @@ +//! Linear UDP Hole Punching Framework +//! +//! This module implements a specialized UDP hole punching framework designed for +//! client-server scenarios where clients are behind NAT. It uses a linear approach +//! with synchronized timing and multiple fallback methods to establish reliable +//! connections through firewalls. +//! +//! # Features +//! +//! - Synchronized pre-process stage +//! - Multiple traversal methods +//! - UPnP integration +//! - Recovery mode support +//! - Connection state tracking +//! - Ping-based timing control +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::udp_traversal::linear::{SingleUDPHolePuncher, NatTraversalMethod}; +//! use netbeam::sync::RelativeNodeType; +//! use std::net::SocketAddr; +//! +//! async fn establish_server_connection( +//! socket: UdpSocket, +//! server_addrs: Vec, +//! config: HolePunchConfigContainer +//! ) -> Result { +//! let mut puncher = SingleUDPHolePuncher::new( +//! RelativeNodeType::Client, +//! config, +//! socket, +//! server_addrs +//! )?; +//! +//! // Try UPnP first +//! if let Some(method) = puncher.get_next_method() { +//! puncher.try_method(method, kill_switch, post_kill_rebuild).await +//! } else { +//! Err(FirewallError::AllMethodsExhausted) +//! } +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Pre-process stage required +//! - Global time sync needed +//! - Methods tried sequentially +//! - Recovery mode for failures +//! - Client-server optimized +//! +//! # Related Components +//! +//! - [`crate::udp_traversal::linear::method3`] - Method 3 implementation +//! - [`crate::standard::upnp_handler`] - UPnP support +//! - [`crate::nat_identification`] - NAT analysis +//! - [`crate::socket_helpers`] - Socket utilities +//! + use std::net::SocketAddr; use citadel_io::tokio::net::UdpSocket; diff --git a/citadel_wire/src/udp_traversal/mod.rs b/citadel_wire/src/udp_traversal/mod.rs index cb439a581..08e7e0b11 100644 --- a/citadel_wire/src/udp_traversal/mod.rs +++ b/citadel_wire/src/udp_traversal/mod.rs @@ -1,3 +1,58 @@ +//! UDP NAT Traversal Framework +//! +//! This module provides a comprehensive framework for UDP NAT traversal, +//! implementing multiple traversal methods including UPnP, hole punching, +//! and specialized techniques. It coordinates between different traversal +//! strategies and manages connection establishment across NAT boundaries. +//! +//! # Architecture +//! +//! The framework is organized into several key components: +//! +//! - Linear traversal: Sequential hole punching attempts +//! - Multi traversal: Concurrent traversal strategies +//! - UPnP integration: Automatic port forwarding +//! - Socket management: NAT-aware socket handling +//! +//! # Features +//! +//! - Multiple NAT traversal methods +//! - Unique connection identification +//! - Method prioritization and fallback +//! - Binary serialization support +//! - Connection state tracking +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::udp_traversal::{ +//! NatTraversalMethod, +//! HolePunchID +//! }; +//! +//! // Select traversal method based on NAT type +//! let method = NatTraversalMethod::UPnP; +//! let method_byte = method.into_byte(); +//! +//! // Generate unique connection ID +//! let punch_id = HolePunchID::default(); +//! ``` +//! +//! # Important Notes +//! +//! - UPnP requires router support +//! - Method3 is a fallback strategy +//! - Connection IDs are UUID-based +//! - Methods are tried in priority order +//! - Binary encoding is network-safe +//! +//! # Related Components +//! +//! - [`crate::standard::upnp_handler`] - UPnP support +//! - [`crate::nat_identification`] - NAT analysis +//! - [`crate::standard::socket_helpers`] - Socket utilities +//! + use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use uuid::Uuid; diff --git a/citadel_wire/src/udp_traversal/multi/mod.rs b/citadel_wire/src/udp_traversal/multi/mod.rs index ad4b5cce4..a548f9f60 100644 --- a/citadel_wire/src/udp_traversal/multi/mod.rs +++ b/citadel_wire/src/udp_traversal/multi/mod.rs @@ -1,3 +1,58 @@ +//! Dual-Stack UDP Hole Punching Framework +//! +//! This module implements a concurrent UDP hole punching framework that supports +//! both IPv4 and IPv6. It manages multiple hole punching attempts across different +//! ports and protocols, coordinating between them to select the most successful +//! connection path. +//! +//! # Features +//! +//! - Concurrent IPv4/IPv6 traversal +//! - Multi-port hole punching +//! - Winner selection protocol +//! - Failure recovery handling +//! - Multiplexed connections +//! - Asynchronous coordination +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::udp_traversal::multi::DualStackUdpHolePuncher; +//! use netbeam::sync::RelativeNodeType; +//! +//! async fn establish_connection( +//! node_type: RelativeNodeType, +//! config: HolePunchConfigContainer, +//! hole_punch_config: HolePunchConfig, +//! network: NetworkEndpoint +//! ) -> Result { +//! let puncher = DualStackUdpHolePuncher::new( +//! node_type, +//! config, +//! hole_punch_config, +//! network +//! )?; +//! +//! puncher.await +//! } +//! ``` +//! +//! # Important Notes +//! +//! - IPv6 preferred for traversal +//! - Concurrent attempts coordinated +//! - Winner selection is atomic +//! - Failures trigger fallbacks +//! - Network multiplexing required +//! +//! # Related Components +//! +//! - [`crate::udp_traversal::linear::SingleUDPHolePuncher`] - Linear punching +//! - [`crate::hole_punch_config`] - Configuration +//! - [`crate::nat_identification`] - NAT analysis +//! - [`netbeam::multiplex`] - Connection multiplexing +//! + use crate::error::FirewallError; use crate::udp_traversal::hole_punch_config::HolePunchConfig; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; diff --git a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs b/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs index 3f3ad375b..6adbd605c 100644 --- a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs +++ b/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs @@ -1,3 +1,79 @@ +//! UDP Socket Address targeting module. +//! +//! This module provides functionality for targeting specific UDP socket addresses. +//! +//! ## Example +//! +//! ```rust,no_run +//! use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; +//! use std::net::SocketAddr; +//! +//! fn example() { +//! let addr = "127.0.0.1:8080".parse::().unwrap(); +//! let targetted = TargettedSocketAddr::new_invariant(addr); +//! assert_eq!(targetted.send_address, addr); +//! assert_eq!(targetted.receive_address, addr); +//! } +//! ``` +//! +//! NAT-Aware UDP Socket Management +//! +//! This module provides specialized UDP socket types that handle the complexities +//! of NAT traversal, including address translation, port mapping, and connection +//! maintenance. It manages the distinction between send and receive addresses +//! that may occur in NAT environments. +//! +//! # Features +//! +//! - NAT-aware socket address management +//! - Separate send/receive address handling +//! - Address translation detection +//! - Port mapping validation +//! - Connection state tracking +//! - Packet validation and filtering +//! - Socket cleanup utilities +//! +//! # Examples +//! +//! ```rust +//! use citadel_wire::udp_traversal::targetted_udp_socket_addr::{ +//! TargettedSocketAddr, HolePunchedUdpSocket +//! }; +//! use std::net::SocketAddr; +//! +//! async fn handle_nat_socket(socket: HolePunchedUdpSocket) -> std::io::Result<()> { +//! let mut buf = [0u8; 1024]; +//! +//! // Cleanse any stray packets +//! socket.cleanse()?; +//! +//! // Receive data with NAT-aware validation +//! let (size, addr) = socket.recv_from(&mut buf).await?; +//! println!("Received {} bytes from {}", size, addr); +//! +//! // Send response through correct NAT path +//! socket.send_to(&buf[..size], addr).await?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Send/receive addresses may differ with UPnP +//! - Packet validation ensures NAT consistency +//! - Socket cleanup required after hole punch +//! - Address translation detection is automatic +//! - Unique IDs prevent connection confusion +//! +//! # Related Components +//! +//! - [`crate::udp_traversal::udp_hole_puncher`] - Hole punching +//! - [`crate::standard::upnp_handler`] - UPnP support +//! - [`crate::nat_identification`] - NAT analysis +//! - [`crate::standard::socket_helpers`] - Socket utilities +//! + use crate::udp_traversal::HolePunchID; use citadel_io::tokio::net::UdpSocket; use serde::{Deserialize, Serialize}; diff --git a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs index 2c45fa3fd..fd9a4b50a 100644 --- a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs +++ b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs @@ -1,3 +1,60 @@ +//! UDP Hole Punching Implementation +//! +//! This module implements the core UDP hole punching algorithm for NAT traversal, +//! enabling peer-to-peer connections between nodes behind different NATs. It handles +//! the complexities of coordinated connection establishment and socket management. +//! +//! # Features +//! +//! - Asynchronous hole punching implementation +//! - Dual-stack IPv4/IPv6 support +//! - Automatic retry mechanism +//! - Configurable timeouts +//! - NAT-aware socket binding +//! - Encrypted configuration exchange +//! - Network endpoint integration +//! +//! # Examples +//! +//! ```rust,no_run +//! use citadel_wire::udp_traversal::udp_hole_puncher::UdpHolePuncher; +//! use citadel_wire::udp_traversal::hole_punch_config::HolePunchConfig; +//! use citadel_wire::nat_identification::NatType; +//! use citadel_wire::error::FirewallError; +//! use citadel_io::tokio::net::UdpSocket; +//! use std::net::SocketAddr; +//! use anyhow::Result; +//! +//! async fn example() -> Result<()> { +//! // Create socket and config +//! let socket = UdpSocket::bind("0.0.0.0:0").await?; +//! let target_addr = "127.0.0.1:8080".parse::().unwrap(); +//! let peer_nat = NatType::identify(None).await?; +//! let config = HolePunchConfig::new(&peer_nat, &[target_addr], vec![socket]); +//! +//! // Create hole puncher +//! let hole_puncher = UdpHolePuncher::new(config); +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! +//! - Requires coordinated peer configuration +//! - Default timeout includes NAT identification time +//! - Multiple retries on failure (max 3) +//! - Socket binding optimized for NAT type +//! - IPv6 support depends on peer capability +//! +//! # Related Components +//! +//! - [`crate::nat_identification`] - NAT behavior analysis +//! - [`crate::udp_traversal::hole_punch_config`] - Configuration +//! - [`crate::standard::socket_helpers`] - Socket utilities +//! - [`crate::udp_traversal::targetted_udp_socket_addr`] - Socket management +//! + use crate::nat_identification::{NatType, IDENTIFY_TIMEOUT}; use crate::udp_traversal::hole_punch_config::HolePunchConfig; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; diff --git a/context.ai.json b/context.ai.json new file mode 100644 index 000000000..aa99525c3 --- /dev/null +++ b/context.ai.json @@ -0,0 +1,2229 @@ +[ + { + "file": "./citadel_sdk/src/responses.rs", + "context": [ + "Provides helper functions for protocol responses", + "Handles peer registration responses", + "Manages peer connection responses", + "Processes group invitation responses", + "Handles automatic ticket management", + "Manages connection type reversal", + "Performs username resolution and validation", + "Requires matching request tickets", + "Integrates with Remote interface", + "Supports PeerSignal and NodeResult handling" + ] + }, + { + "file": "./citadel_sdk/src/test_common.rs", + "context": [ + "Provides testing utilities for Citadel Protocol", + "Supports test server creation and configuration", + "Implements synchronization barriers for multi-peer tests", + "Includes UDP mode testing utilities", + "Provides P2P connection testing helpers", + "Manages local test peer coordination", + "Requires localhost-testing feature for most functionality", + "Integrates with NodeBuilder for server configuration", + "Supports EmptyKernel for basic testing", + "Includes connection validation utilities" + ] + }, + { + "file": "./citadel_sdk/src/remote_ext.rs", + "context": [ + "Extends NodeRemote with high-level protocol operations", + "Provides user registration and authentication", + "Manages connections and file transfers", + "Supports encrypted virtual filesystem", + "Handles peer discovery and group communication", + "Implements security settings configuration", + "Uses asynchronous operations throughout", + "Supports chunked file transfers for efficiency", + "Requires mutual registration for peer connections", + "Integrates with client-server and P2P modes" + ] + }, + { + "file": "./citadel_proto/src/inner_arg.rs", + "context": [ + "Provides type-safe parameter reference handling", + "Implements wrapper types for mutable and immutable references", + "Uses zero-cost abstractions for performance", + "Enforces proper dereferencing behavior", + "Preserves mutability constraints", + "Integrates with packet processing and validation", + "Supports cryptographic operation safety" + ] + }, + { + "file": "./citadel_proto/src/functional.rs", + "context": [ + "Provides functional programming utilities and extensions", + "Implements monadic-style operations and conditional chaining", + "Supports method chaining with Then trait", + "Provides conditional branching with IfEq and IfTrue", + "Implements tuple mapping with PairMap", + "Uses zero-cost abstractions and lazy evaluation", + "Enhances code readability throughout the codebase" + ] + }, + { + "file": "./citadel_proto/src/constants.rs", + "context": [ + "Defines core protocol constants and configuration", + "Manages protocol version using semantic versioning", + "Specifies network parameters and MTU sizes", + "Controls timing intervals and timeouts", + "Sets buffer sizes and group limitations", + "Configures security level update frequencies", + "Defines port ranges and networking defaults" + ] + }, + { + "file": "./citadel_proto/src/auth.rs", + "context": [ + "Defines authentication request types for Citadel Protocol", + "Supports both credential-based and passwordless authentication", + "Uses SecBuffer for secure credential handling", + "Manages user identification through CID and usernames", + "Handles server connection information for authentication", + "Implements transient device-based connections" + ] + }, + { + "file": "./citadel_proto/src/proto/validation.rs", + "context": [ + "Core packet validation module for the Citadel Protocol", + "Implements security-critical validation for all packet types", + "Handles connection, registration, group, and file transfer validation", + "Uses AEAD cryptography for packet integrity verification", + "Implements zero-copy validation where possible", + "Contains submodules for different validation contexts: do_connect, group, do_register, do_drill_update, pre_connect, file, and aead", + "Maintains protocol state consistency across all validation steps" + ] + }, + { + "file": "./citadel_proto/src/proto/session.rs", + "context": [ + "Core session management implementation for Citadel Protocol", + "Handles active connections between peers with state management", + "Implements secure file transfer with configurable security levels", + "Supports UDP connectivity for performance-critical operations", + "Provides clean shutdown and resource cleanup mechanisms", + "Handles connection interruptions and session resumption", + "Uses post-quantum cryptographic primitives for session security", + "Manages packet processing and stream handling", + "Implements session initialization and parameter configuration", + "Supports both TCP and UDP transport protocols", + "Handles authentication and credential management", + "Provides virtual connection management for different transfer types" + ] + }, + { + "file": "./citadel_proto/src/proto/session_queue_handler.rs", + "context": [ + "Implements queue-based task scheduling for protocol sessions", + "Uses DelayQueue for managing timed operations", + "Handles both reserved system tasks (indices 0-9) and ordinary tasks (10+)", + "Supports one-shot tasks for single-execution operations", + "Integrates with session state management", + "Implements Stream and Future traits for async operation", + "Provides thread-safe task scheduling through atomic operations", + "Manages session shutdown and cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet.rs", + "context": [ + "Implements the core Hypernode Data Protocol (HDP) packet structure", + "Defines packet headers, commands, and buffer handling", + "Provides zero-copy header parsing for efficiency", + "Manages packet composition and decomposition", + "Supports both BytesMut and Vec buffer types", + "Handles socket address tracking and packet routing", + "Implements hierarchical command organization" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/mod.rs", + "context": [ + "Core packet processing infrastructure for the Citadel Protocol", + "Key aspects:", + "- Manages multiple packet types (connection, auth, data)", + "- Implements processing pipeline with validation", + "- Handles packet security and integrity", + "- Provides backpressure and ordering guarantees", + "- Coordinates between different packet processors" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/connect_packet.rs", + "context": [ + "Handles connection establishment in Citadel Protocol", + "Key aspects:", + "- Implements secure multi-stage handshake", + "- Manages post-quantum key exchange", + "- Handles version and capability negotiation", + "- Provides connection state management", + "- Supports transport-agnostic connections" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/disconnect_packet.rs", + "context": [ + "Implements graceful disconnection protocol", + "Uses two-stage disconnect handshake", + "Manages secure packet validation", + "Handles session state transitions", + "Coordinates with kernel for disconnect signals", + "Implements packet delivery delay for reliability", + "Tracks disconnection tickets" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/file_packet.rs", + "context": [ + "Handles secure file transfer operations", + "Manages file transfer lifecycle", + "Supports chunked transfers", + "Integrates with virtual filesystem", + "Implements transfer state tracking", + "Handles both direct and proxied transfers" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/keep_alive_packet.rs", + "context": [ + "Implements connection maintenance through keep-alive packets", + "Key aspects:", + "- Manages periodic heartbeat packets", + "- Detects connection liveness", + "- Monitors connection quality", + "- Handles connection timeouts", + "- Triggers automatic reconnection" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/deregister_packet.rs", + "context": [ + "Handles client deregistration process", + "Manages secure account removal", + "Implements resource cleanup", + "Validates session state", + "Provides ticket-based tracking", + "Handles success/failure states", + "Manages client and server cleanup", + "Maintains security during removal", + "Integrates with account management", + "Reports deregistration results" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/raw_primary_packet.rs", + "context": [ + "Low-level packet processing for primary data packets", + "Key aspects:", + "- Handles raw packet operations", + "- Manages packet headers and payloads", + "- Implements zero-copy processing", + "- Ensures packet integrity", + "- Validates packet structure" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/hole_punch.rs", + "context": [ + "Implements NAT traversal through hole punching", + "Enables direct P2P connections behind NATs", + "Handles NAT traversal packet processing", + "Provides secure packet validation", + "Manages peer connection coordination", + "Implements connection pipe management", + "Supports proxied connections", + "Manages hole puncher pipes", + "Integrates with proxy system", + "Ensures authenticated packet handling" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/register_packet.rs", + "context": [ + "Handles client registration in Citadel Protocol", + "Implements secure multi-stage handshake", + "Provides passwordless registration support", + "Manages session state transitions", + "Implements cryptographic parameter negotiation", + "Handles registration failure cases", + "Supports post-quantum cryptography", + "Validates registration parameters", + "Integrates with account management", + "Manages initial cryptographic setup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/rekey_packet.rs", + "context": [ + "Implements secure key rotation mechanism", + "Manages cryptographic key updates", + "Provides multi-stage key exchange", + "Implements post-quantum cryptography", + "Ensures perfect forward secrecy", + "Handles state synchronization", + "Supports proxy connections", + "Manages ratchet state updates", + "Handles key truncation operations", + "Integrates with security level system" + ] + }, + { + "file": "./citadel_proto/src/proto/session_manager.rs", + "context": [ + "Core session management system for Citadel Protocol", + "Handles stateful connections between peers", + "Manages both provisional and established connections", + "Implements secure peer-to-peer communication", + "Provides group broadcast functionality", + "Supports configurable security levels", + "Handles virtual connection management", + "Implements clean shutdown procedures", + "Manages connection upgrades and transitions", + "Integrates with HyperNodePeerLayer for P2P operations" + ] + }, + { + "file": "./citadel_user/src/lib.rs", + "context": [ + "Core user management system", + "Manages HyperVPN architecture", + "Provides hierarchical account structure", + "Handles authentication and security", + "Supports multiple backend storage", + "Integrates external services", + "Manages account lifecycle", + "Implements zero-trust architecture", + "Supports post-quantum cryptography", + "Provides feature-gated functionality" + ] + }, + { + "file": "./citadel_user/src/external_services/service_interface.rs", + "context": [ + "Defines external service interface", + "Provides unified service communication", + "Supports async data transmission", + "Handles raw packet data", + "Enables peer-to-peer communication", + "Standardizes error handling", + "Ensures thread safety", + "Manages service connections", + "Implements common service trait", + "Supports multiple service types" + ] + }, + { + "file": "./citadel_user/src/external_services/rtdb.rs", + "context": [ + "Provides Firebase RTDB integration", + "Manages real-time data synchronization", + "Handles authentication and tokens", + "Manages connection lifecycle", + "Implements data transfer interface", + "Supports connection refresh", + "Handles token expiration", + "Provides client configuration", + "Manages RTDB instances", + "Implements efficient data sync" + ] + }, + { + "file": "./citadel_user/src/external_services/mod.rs", + "context": [ + "Manages external service integration", + "Provides Google services support", + "Handles service configuration", + "Manages service authentication", + "Supports Firebase RTDB", + "Handles JWT management", + "Provides WASM compatibility", + "Manages service state", + "Handles post-login services", + "Supports feature flags" + ] + }, + { + "file": "./citadel_user/src/auth/proposed_credentials.rs", + "context": [ + "Manages credential proposals", + "Handles password hashing", + "Validates credentials", + "Supports passwordless auth", + "Implements username sanitization", + "Provides secure password storage", + "Manages authentication modes", + "Handles server validation", + "Implements credential comparison", + "Ensures memory safety" + ] + }, + { + "file": "./citadel_user/src/backend/sql_backend.rs", + "context": [ + "Provides SQL database storage", + "Supports multiple SQL variants", + "Implements connection pooling", + "Handles SQL syntax differences", + "Manages schema creation", + "Supports blob and text storage", + "Implements atomic operations", + "Manages peer relationships", + "Provides efficient querying", + "Enables ACID compliance" + ] + }, + { + "file": "./citadel_user/src/backend/redis_backend.rs", + "context": [ + "Provides distributed Redis storage", + "Implements connection pooling", + "Supports Redis clustering", + "Manages connection health checks", + "Handles connection timeouts", + "Provides automatic reconnection", + "Supports atomic operations", + "Manages peer relationships", + "Implements byte map storage", + "Enables high availability" + ] + }, + { + "file": "./citadel_user/src/backend/filesystem_backend.rs", + "context": [ + "Provides filesystem-based persistent storage", + "Implements hybrid storage with memory cache", + "Manages client network account persistence", + "Handles directory structure organization", + "Supports peer relationship storage", + "Implements virtual filesystem operations", + "Provides byte map storage functionality", + "Ensures atomic file operations", + "Supports personal and impersonal accounts", + "Integrates with memory backend for caching" + ] + }, + { + "file": "./citadel_user/src/backend/memory.rs", + "context": [ + "Provides in-memory backend storage", + "Implements thread-safe client storage", + "Manages peer relationships", + "Handles metadata operations", + "Supports byte map storage", + "Implements atomic operations", + "Manages resource cleanup", + "Supports WASM environments", + "Provides client registration", + "Maintains data consistency" + ] + }, + { + "file": "./citadel_user/src/backend/utils/mod.rs", + "context": [ + "Manages object transfer operations", + "Implements bidirectional transfers", + "Provides progress tracking", + "Handles transfer status updates", + "Implements stream-based transfers", + "Manages transfer control flow", + "Provides async transfer support", + "Handles resource cleanup", + "Implements transfer acceptance", + "Manages transfer orientation" + ] + }, + { + "file": "./citadel_user/src/backend/mod.rs", + "context": [ + "Defines core backend storage infrastructure", + "Manages multiple storage backend types", + "Implements unified backend interface", + "Provides async persistence operations", + "Handles database connections", + "Manages virtual filesystem operations", + "Implements thread-safe storage", + "Provides transaction management", + "Supports multiple database types", + "Handles automatic reconnection" + ] + }, + { + "file": "./citadel_user/src/auth/mod.rs", + "context": [ + "Manages authentication modes for CNACs", + "Implements Argon2id password hashing", + "Supports passwordless authentication", + "Handles username uniqueness", + "Manages full name storage", + "Provides authentication state checks", + "Implements serializable auth data", + "Handles credential validation", + "Manages authentication modes", + "Provides secure data storage" + ] + }, + { + "file": "./citadel_user/src/account_loader.rs", + "context": [ + "Manages loading of serialized client network accounts", + "Handles both personal and impersonal account types", + "Provides generic file loading capabilities", + "Implements efficient buffered I/O operations", + "Supports extensible deserialization", + "Manages filesystem-based account storage", + "Handles account loading errors gracefully", + "Integrates with directory management", + "Supports cryptographic account security", + "Provides account persistence functionality" + ] + }, + { + "file": "./citadel_user/src/server_misc_settings.rs", + "context": [ + "Manages server-side configuration settings", + "Controls passwordless authentication", + "Enforces credential requirements", + "Provides default security settings", + "Integrates with account management", + "Supports customizable authentication flows", + "Handles server-side security policies", + "Configures node authentication behavior", + "Manages credential validation rules", + "Controls server security features" + ] + }, + { + "file": "./citadel_user/src/serialization.rs", + "context": [ + "Provides binary serialization functionality", + "Implements efficient buffer operations", + "Supports in-place deserialization", + "Handles size estimation and pre-allocation", + "Uses bincode for binary encoding", + "Integrates with bytes buffer system", + "Provides trait-based serialization", + "Manages memory-efficient operations", + "Supports slice-based serialization", + "Implements error handling with AccountError" + ] + }, + { + "file": "./citadel_user/src/connection_metadata.rs", + "context": [ + "Manages connection metadata for client connections", + "Handles connection protocol specifications", + "Supports TCP, TLS, and QUIC protocols", + "Stores socket address information", + "Provides domain name handling", + "Implements serialization support", + "Manages connection state persistence", + "Supports connection info display", + "Handles protocol-specific settings", + "Maintains connection type information" + ] + }, + { + "file": "./citadel_user/src/directory_store.rs", + "context": [ + "Manages filesystem structure for Citadel Protocol", + "Handles directory creation and organization", + "Provides cross-platform path management", + "Organizes account storage directories", + "Manages configuration file locations", + "Supports virtual filesystem structure", + "Handles file transfer storage", + "Implements path formatting and generation", + "Maintains directory hierarchy", + "Ensures consistent file organization" + ] + }, + { + "file": "./citadel_user/src/credentials.rs", + "context": [ + "Manages credential validation and requirements", + "Defines username and password constraints", + "Enforces length and format restrictions", + "Provides configurable validation rules", + "Handles full name format validation", + "Implements default security policies", + "Supports custom requirement definitions", + "Ensures consistent credential formats", + "Validates credential formatting", + "Defines system-wide credential limits" + ] + }, + { + "file": "./citadel_user/src/hypernode_account.rs", + "context": [ + "Core HyperNode account functionality", + "Provides user identification extensions", + "Handles account search operations", + "Manages peer relationship lookups", + "Supports CID and username-based identification", + "Implements async account operations", + "Defines serialization file extension", + "Enables flexible account type extensions", + "Maintains bi-directional peer relationships", + "Integrates with persistence backends" + ] + }, + { + "file": "./citadel_user/src/account_manager.rs", + "context": [ + "Central user account management system", + "Handles account creation and registration", + "Supports multiple storage backends (Memory, File, SQL, Redis)", + "Manages HyperLAN peer relationships", + "Provides secure credential storage", + "Implements Argon2id password hashing", + "Supports personal and impersonal modes", + "Handles P2P connection registration", + "Provides thread-safe async operations", + "Manages external service integrations" + ] + }, + { + "file": "./citadel_user/src/client_account.rs", + "context": [ + "Manages individual client connections in Citadel Protocol", + "Handles both personal and impersonal connection modes", + "Implements secure credential management and validation", + "Provides ratchet-based cryptographic state management", + "Manages HyperLAN and HyperWAN peer relationships", + "Supports thread-safe operations through RwLock", + "Handles connection endpoint configuration", + "Implements peer list synchronization", + "Provides P2P connection support", + "Maintains immutable critical security fields" + ] + }, + { + "file": "./citadel_crypt/src/argon/autotuner.rs", + "context": [ + "Automatic parameter tuning for Argon2 password hashing", + "Implements ORY's guidelines for parameter selection", + "Uses memory-first tuning strategy for optimal security", + "Dynamically adjusts based on system capabilities", + "Supports multi-threading with CPU core detection", + "Provides configurable minimum execution time", + "Includes safeguards for memory usage", + "Implements iterative parameter optimization", + "Supports custom hash lengths and secret keys", + "Designed for release-mode performance tuning" + ] + }, + { + "file": "./citadel_crypt/src/lib.rs", + "context": [ + "Core cryptographic framework for the Citadel Protocol", + "Implements post-quantum cryptography and perfect forward secrecy", + "Provides secure memory management with zero-copy operations", + "Features entropy banking system for key derivation", + "Includes packet vectorization and port scrambling", + "Implements FCM (Forward Chain Messaging) primitives", + "Integrates Argon2 with auto-tuning capabilities", + "All operations are thread-safe and memory-efficient", + "Uses defense-in-depth with multiple security layers", + "Automatic memory zeroing for sensitive data" + ] + }, + { + "file": "./citadel_wire/src/lib.rs", + "context": [ + "Main entry point for Citadel Wire crate", + "Provides secure peer-to-peer connection capabilities", + "Implements NAT traversal and UDP hole punching", + "Enforces zero unsafe code policy", + "Uses async-first design with security focus" + ] + }, + { + "file": "./async_ip/src/lib.rs", + "context": [ + "Utility crate for asynchronous IP address resolution", + "Provides both IPv4 and IPv6 resolution with fallback mechanisms", + "Supports internal and external IP detection", + "Uses multiple services concurrently for reliability", + "Has special handling for WebAssembly environments", + "Key component for NAT traversal and network identification", + "Uses HTTP-based IP resolution services with configurable endpoints", + "Implements custom error handling for network and parsing failures" + ] + }, + { + "file": "./firebase-rtdb/src/lib.rs", + "context": [ + "Lightweight async Rust client for Firebase Realtime Database", + "Provides JWT-based authentication with automatic token renewal", + "Implements all CRUD operations with JSON serialization", + "Uses hierarchical node-based database access", + "Handles connection timeouts and TLS security", + "Provides error handling for network and database operations", + "Supports connection pooling and TCP nodelay for performance", + "Implements Firebase Security Rules compatibility", + "Uses reqwest for HTTP communication with async support", + "Manages authentication state and token expiration" + ] + }, + { + "file": "./firebase-rtdb/Cargo.toml", + "context": [ + "Package configuration for Firebase RTDB client", + "Uses workspace-level dependency management", + "Configures reqwest with rustls-tls for secure communication", + "Includes serde for JSON serialization support", + "Defines crate metadata and documentation links", + "Specifies test dependencies for integration testing" + ] + }, + { + "file": "./firebase-rtdb/tests/primary.rs", + "context": [ + "Integration test file for Firebase RTDB client", + "Currently empty, prepared for future test implementations" + ] + }, + { + "file": "./netbeam/src/lib.rs", + "context": [ + "Core library for high-performance networking with multiplexing support", + "Provides reliable ordered message delivery guarantees", + "Implements network-aware synchronization primitives", + "Uses zero unsafe code and leverages Rust's type system", + "Requires Tokio runtime for async operations", + "Supports both client and server modes with bi-directional communication", + "Includes time tracking utilities for network operations", + "Features proper error handling and type-safe APIs" + ] + }, + { + "file": "./netbeam/src/reliable_conn.rs", + "context": [ + "Core traits for reliable network connections", + "Implements ordered message delivery guarantees", + "Supports both direct and NAT-traversed connections", + "Provides connection addressing abstraction", + "Includes serialization support for messages", + "Implements network simulation for testing" + ] + }, + { + "file": "./netbeam/src/multiplex.rs", + "context": [ + "Network stream multiplexing implementation", + "Enables multiple logical connections over single physical connection", + "Provides bi-directional communication channels", + "Implements automatic stream ID generation and management", + "Ensures thread-safe subscription handling", + "Supports custom connection key types", + "Maintains message ordering within streams", + "Handles graceful stream initialization and cleanup", + "Uses pre-action and post-action hooks for lifecycle management", + "Provides both borrowed and owned subscription types" + ] + }, + { + "file": "./netbeam/src/time_tracker.rs", + "context": [ + "Provides precise timing utilities for network operations", + "Uses monotonic system time for consistency", + "Implements nanosecond precision timing", + "Handles time overflow protection", + "Used for latency measurements and timing-sensitive operations" + ] + }, + { + "file": "./citadel_types/src/lib.rs", + "context": [ + "Core type definitions for the Citadel Protocol", + "Provides fundamental data structures and utilities", + "Includes cryptographic types and parameters", + "Defines protocol-specific message types", + "Contains user-related data structures", + "Implements error handling and validation", + "Exports commonly used types through prelude" + ] + }, + { + "file": "./citadel_types/src/crypto/mod.rs", + "context": [ + "Defines cryptographic types and parameters", + "Implements secure memory management with SecBuffer", + "Provides algorithm selection and configuration", + "Supports post-quantum cryptography algorithms", + "Includes security level specifications", + "Implements secure memory locking and zeroing", + "Provides serialization for cryptographic types", + "Supports various secrecy modes for different use cases" + ] + }, + { + "file": "./citadel_logging/src/lib.rs", + "context": [ + "Structured logging facade for the Citadel Protocol", + "Built on top of the tracing ecosystem", + "Provides consistent logging setup across components", + "Supports file and line number information", + "Implements environment-based log level filtering", + "Includes panic handling with logging", + "Supports async-aware instrumentation", + "Uses span-based structured logging", + "Provides multiple log levels with target-based filtering" + ] + }, + { + "file": "./citadel_io/src/lib.rs", + "context": [ + "Cross-platform I/O utility crate for native and WebAssembly targets", + "Provides consistent interfaces for synchronization primitives", + "Implements platform-specific random number generation", + "Abstracts async runtime differences between native and WASM", + "Re-exports Tokio ecosystem with platform-specific implementations", + "Supports deadlock detection on native platforms" + ] + }, + { + "file": "./citadel_io/src/standard/locks.rs", + "context": [ + "Native platform synchronization primitives using parking_lot", + "High-performance mutex and read-write lock implementations", + "RAII-style lock guards for automatic resource management", + "More efficient than standard library synchronization types" + ] + }, + { + "file": "./citadel_io/src/wasm/locks.rs", + "context": [ + "WebAssembly-compatible synchronization primitives", + "Wraps standard library locks for WASM compatibility", + "Maintains API compatibility with native code", + "Single-threaded implementation for current WASM limitations" + ] + }, + { + "file": "./citadel_io/src/wasm/rng.rs", + "context": [ + "WebAssembly-compatible random number generation", + "Uses Web Crypto API through getrandom crate", + "Provides cryptographically secure random numbers", + "Implements RngCore and CryptoRng traits", + "Supports both fixed-size and dynamic buffer generation" + ] + }, + { + "file": "./citadel_pqcrypto/src/lib.rs", + "context": [ + "# Documentation Progress", + "## citadel_pqcrypto Crate", + "### Completed Documentation", + "- `lib.rs`: Crate-level documentation with overview, features, and examples", + "- `constructor_opts.rs`: Documentation for `ConstructorOpts` struct", + "- Enhanced `RecursiveChain` documentation with examples and security considerations", + "- `encryption.rs`: Module-level documentation", + "- `AeadModule` trait documentation", + "- `AesModule` implementation docs", + "- `ChaChaModule` implementation docs", + "- `AsconModule` implementation docs", + "- `KyberModule` implementation docs with quantum security considerations", + "- `export.rs`: Enhanced module-level documentation", + "- Key store serialization documentation", + "- Security considerations for key material", + "- Examples for serialization/deserialization", + "- `bytes_in_place.rs`: Comprehensive module documentation", + "- `InPlaceBuffer` and `EzBuffer` documentation", + "- Examples for buffer operations", + "- `wire.rs`: Module-level documentation", + "- Parameter transfer documentation", + "- Scrambling dictionary documentation", + "- `replay_attack_container.rs`: Module-level documentation", + "- Anti-replay attack mechanism documentation", + "- Examples and security considerations", + "### Security Considerations Documented", + "- Post-quantum cryptography principles", + "- Proper nonce handling", + "- Zeroization of sensitive data", + "- Constant-time operations", + "- Forward secrecy", + "- Local-user encryption for endpoint privacy", + "- Anti-replay attack protections", + "- Key material serialization safety", + "- Buffer operation safety", + "- Parameter transfer security", + "### Next Steps", + "1. Review remaining files in citadel_pqcrypto for any documentation gaps", + "2. Cross-reference documentation between related components", + "3. Ensure all security considerations are thoroughly documented", + "4. Add more real-world usage examples", + "### Code Style and Standards", + "- All documentation follows Rust documentation best practices", + "- Examples are provided for key functionality", + "- Security considerations are clearly outlined", + "- Cross-references between related components are maintained" + ] + }, + { + "file": "./citadel_pqcrypto/src/constructor_opts.rs", + "context": [ + "Provides configuration options for post-quantum cryptography (PQC) instances", + "Includes ConstructorOpts for PQC initialization and RecursiveChain for key derivation", + "Focuses on secure parameter handling and memory safety", + "Supports both initial and chained cryptographic operations" + ] + }, + { + "file": "./citadel_pqcrypto/src/bytes_in_place.rs", + "context": [ + "Implements memory-efficient and secure in-place buffer operations", + "Features window-based buffer access control for safe data manipulation", + "Supports both Vec and BytesMut buffer types", + "Emphasizes zero-copy operations and memory safety" + ] + }, + { + "file": "./citadel_pqcrypto/src/wire.rs", + "context": [ + "Implements secure wire protocol for PQC parameter transfer", + "Provides parameter transfer structures for Alice-Bob key exchange", + "Features data scrambling for additional security", + "Supports both symmetric and asymmetric encryption modes", + "Ensures memory safety and automatic parameter cleanup" + ] + }, + { + "file": "./citadel_pqcrypto/src/replay_attack_container.rs", + "context": [ + "Implements protection against replay attacks in communications", + "Uses circular buffer for efficient PID history tracking", + "Supports out-of-order packet delivery within configurable window", + "Provides thread-safe PID generation and validation", + "Features automatic state reset on re-keying" + ] + }, + { + "file": "./citadel_wire/src/hypernode_type.rs", + "context": [ + "This module defines network node types and their behaviors in the Citadel Protocol. It handles configuration for server nodes with static IPs and peer nodes in residential NAT environments, including automatic UPnP handling and NAT traversal fallback mechanisms" + ] + }, + { + "file": "./citadel_wire/src/error.rs", + "context": [ + "This module implements error types specific to network traversal and firewall operations", + "It provides custom error types for UPnP and hole punching operations, with conversion traits to standard IO errors", + "The error types help categorize and handle various network-related failure scenarios, including port mapping failures and exhausted hole punching attempts" + ] + }, + { + "file": "./citadel_wire/src/standard/nat_identification.rs", + "context": [ + "Provides NAT type identification and analysis", + "Uses STUN servers for NAT behavior detection", + "Analyzes port and IP translation patterns", + "Supports IPv4 and IPv6 compatibility checks", + "Determines optimal NAT traversal strategies" + ] + }, + { + "file": "./citadel_wire/src/standard/socket_helpers.rs", + "context": [ + "Provides socket creation and configuration utilities", + "Handles TCP and UDP socket setup", + "Implements platform-specific socket options", + "Supports IPv4 and IPv6 with automatic mapping", + "Manages socket reuse for NAT traversal" + ] + }, + { + "file": "./citadel_wire/src/standard/upnp_handler.rs", + "context": [ + "Provides UPnP port mapping and gateway management", + "Handles automatic gateway discovery and configuration", + "Manages port forwarding for TCP and UDP protocols", + "Supports configurable lease durations and targeted forwarding", + "Implements external IP and local IP detection" + ] + }, + { + "file": "./citadel_wire/src/standard/quic.rs", + "context": [ + "Implements QUIC protocol for secure connections", + "Supports client and server endpoints", + "Handles self-signed and PKCS#12 certificates", + "Provides NAT traversal-friendly transport", + "Uses Tokio for async/await support" + ] + }, + { + "file": "./citadel_wire/src/standard/tls.rs", + "context": [ + "Provides TLS configuration and certificate management", + "Supports both TLS and QUIC protocols", + "Handles self-signed and PKCS#12 certificates", + "Implements native system certificate loading", + "Uses Rustls for secure TLS implementation" + ] + }, + { + "file": "./citadel_wire/src/standard/misc.rs", + "context": [ + "Provides certificate format conversion utilities", + "Handles PKCS#12 to QUIC format conversion", + "Manages certificate chain extraction", + "Implements private key conversion", + "Ensures memory-safe certificate handling" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/hole_punch_config.rs", + "context": [ + "Provides configuration for UDP hole punching NAT traversal", + "Handles address prediction and socket preparation", + "Configures port ranges based on NAT behavior", + "Supports both IPv4 and IPv6 traversal", + "Implements iterator-based address generation" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/udp_hole_puncher.rs", + "context": [ + "Implements core UDP hole punching algorithm for NAT traversal", + "Handles asynchronous connection establishment between peers", + "Supports dual-stack IPv4/IPv6 with automatic retry", + "Manages encrypted configuration exchange", + "Provides NAT-aware socket binding optimization" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs", + "context": [ + "Manages NAT-aware UDP socket addressing", + "Handles separate send/receive addresses for NAT traversal", + "Provides packet validation and connection state tracking", + "Implements socket cleanup after hole punching", + "Supports UPnP address translation detection" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/mod.rs", + "context": [ + "Core UDP NAT traversal framework module", + "Coordinates multiple traversal methods and strategies", + "Manages UPnP and hole punching integration", + "Provides unique connection identification", + "Implements method prioritization and fallback" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs", + "context": [ + "Manages secure packet encryption for hole punching configuration", + "Handles custom STUN server configurations", + "Provides zero-copy packet handling optimizations", + "Supports localhost testing mode with disabled encryption", + "Implements thread-safe encryption function containers" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/method3.rs", + "context": [ + "Implements advanced UDP hole punching with variable TTL values", + "Handles asymmetric NAT traversal failures with recovery mode", + "Provides encrypted configuration exchange between peers", + "Supports dual-stack IPv4/IPv6 operation", + "Implements thread-safe UDP socket operations" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/mod.rs", + "context": [ + "Implements linear UDP hole punching for client-server scenarios", + "Coordinates multiple traversal methods with fallback strategy", + "Manages synchronized connection establishment timing", + "Provides recovery mode for asymmetric failures", + "Integrates UPnP support with automatic fallback" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/multi/mod.rs", + "context": [ + "Implements concurrent dual-stack UDP hole punching", + "Manages multiple traversal attempts across ports", + "Coordinates winner selection between attempts", + "Handles IPv4/IPv6 protocol selection", + "Provides multiplexed connection management" + ] + }, + { + "file": "./citadel_wire/src/standard/mod.rs", + "context": [ + "Provides core networking components for Citadel Protocol", + "Implements QUIC, TLS, and NAT traversal", + "Handles UPnP and socket configuration", + "Supports peer-to-peer networking", + "Uses async-first design philosophy" + ] + }, + { + "file": "./citadel_crypt/src/entropy_bank.rs", + "context": [ + "Implements dynamic entropy management", + "Provides secure nonce generation", + "Handles packet encryption/decryption", + "Prevents replay attacks", + "Manages transient counters", + "Integrates post-quantum operations" + ] + }, + { + "file": "./citadel_crypt/src/endpoint_crypto_container.rs", + "context": [ + "Manages peer session cryptographic state", + "Implements thread-safe ratchet updates", + "Handles version control and conflicts", + "Manages session key rotation", + "Supports atomic state transitions", + "Integrates with post-quantum cryptography" + ] + }, + { + "file": "./citadel_crypt/src/misc.rs", + "context": [ + "Provides cryptographic utility functions", + "Implements flexible error handling", + "Generates random port mappings", + "Supports security level validation", + "Implements error type conversions", + "Provides debug and display utilities" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs", + "context": [ + "Implements secure partitioned buffer management", + "Provides fixed-size memory partitioning", + "Enforces strict partition boundaries", + "Supports zero-copy partition access", + "Implements automatic buffer zeroing", + "Provides thread-safe partition operations" + ] + }, + { + "file": "./citadel_crypt/src/packet_vector.rs", + "context": [ + "Implements secure packet sequencing", + "Provides wave-based packet organization", + "Manages scrambled port assignments", + "Implements sequence hiding", + "Supports automatic memory zeroing", + "Ensures ordered packet delivery" + ] + }, + { + "file": "./citadel_crypt/src/sync_toggle.rs", + "context": [ + "Provides thread-safe toggle state management", + "Implements atomic state transitions", + "Supports one-way toggle operations", + "Enables state change detection", + "Uses sequential consistency ordering" + ] + }, + { + "file": "./citadel_crypt/src/stacked_ratchet.rs", + "context": [ + "Implements perfect forward secrecy", + "Manages independent key evolution", + "Provides post-quantum support", + "Handles message protection", + "Implements anti-replay protection", + "Ensures ordered packet delivery" + ] + }, + { + "file": "./citadel_crypt/src/scramble/crypt_splitter.rs", + "context": [ + "Implements secure packet splitting and scrambling", + "Provides wave-based packet transmission", + "Supports dynamic packet sizing based on security level", + "Implements packet reconstruction with timeout handling", + "Manages encrypted and unencrypted packet transmission", + "Integrates with post-quantum cryptography" + ] + }, + { + "file": "./citadel_crypt/src/streaming_crypt_scrambler.rs", + "context": [ + "Implements asynchronous streaming encryption for large data sources", + "Supports both file-based and in-memory data sources", + "Provides backpressure support through async/await", + "Manages efficient group-based encryption and transmission", + "Supports custom header inscription for packets", + "Implements progress tracking and cancellation" + ] + }, + { + "file": "./citadel_crypt/src/toolset.rs", + "context": [ + "Manages cryptographic ratchet versioning and synchronization", + "Implements memory-bounded storage for active ratchets", + "Provides static auxiliary ratchet for persistent encryption", + "Handles secure ratchet updates and deregistration", + "Ensures thread-safe access to cryptographic primitives", + "Manages rolling window of active encryption keys" + ] + }, + { + "file": "./citadel_crypt/src/fcm/keys.rs", + "context": [ + "Manages Firebase Cloud Messaging (FCM) credentials", + "Provides thread-safe access to API keys and client IDs", + "Implements efficient memory management through Arc", + "Supports serialization for credential persistence", + "Ensures secure handling of sensitive credential data", + "Enables type-safe FCM key construction and access" + ] + }, + { + "file": "./citadel_crypt/src/fcm/fcm_ratchet.rs", + "context": [ + "Implements size-optimized cryptographic ratchet for FCM", + "Provides post-quantum secure messaging within 4KB limit", + "Manages secure key evolution and perfect forward secrecy", + "Supports both synchronous and asynchronous operations", + "Implements message protection and validation", + "Integrates with Firebase Cloud Messaging service" + ] + }, + { + "file": "./citadel_crypt/src/argon/argon_container.rs", + "context": [ + "Implements asynchronous Argon2 password hashing", + "Provides client and server-side password handling", + "Supports configurable memory and time cost parameters", + "Implements secure memory management with SecBuffer", + "Supports associated data and secret keys", + "Uses Argon2id variant for optimal security" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/sec_packet.rs", + "context": [ + "Implements secure packet buffer handling", + "Provides three-part packet structure (header, payload, extension)", + "Enforces ordered write operations for packet construction", + "Implements zero-copy packet operations", + "Manages automatic memory cleanup", + "Supports fixed-size header optimization" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/mod.rs", + "context": [ + "Provides secure buffer management module", + "Implements memory-safe buffer operations", + "Supports zero-copy data handling", + "Manages automatic memory zeroing", + "Includes packet writing utilities", + "Integrates with streaming operations" + ] + }, + { + "file": "./citadel_user/src/misc.rs", + "context": [ + "Provides core error handling for account operations", + "Manages CNAC metadata structures", + "Implements cross-platform path validation", + "Handles timestamp formatting", + "Provides virtual path management", + "Implements error type conversion", + "Manages account identification", + "Handles platform-specific path formatting", + "Provides directory validation", + "Implements metadata comparison" + ] + }, + { + "file": "./citadel_proto/src/proto/remote.rs", + "context": [ + "Implements remote communication functionality", + "Provides high-level interface for node communication", + "Supports asynchronous request/response patterns", + "Manages ticket-based request tracking", + "Integrates with account management system", + "Implements callback-based event handling", + "Ensures secure communication channels", + "Supports post-quantum cryptography", + "Handles network errors comprehensively", + "Provides bounded message sending" + ] + }, + { + "file": "./citadel_proto/src/proto/endpoint_crypto_accessor.rs", + "context": [ + "Manages cryptographic state access for P2P and C2S channels", + "Provides safe borrowing mechanisms for crypto operations", + "Implements version-aware state management", + "Ensures thread-safe container access", + "Integrates with post-quantum cryptography", + "Handles missing or invalid crypto states", + "Supports both peer-to-peer and client-server modes", + "Uses StackedRatchet for core crypto operations" + ] + }, + { + "file": "./citadel_proto/src/proto/session.rs", + "context": [ + "Core session management for Citadel Protocol", + "Handles active connections between peers", + "Manages authentication and key exchange", + "Implements secure file transfer functionality", + "Supports UDP connectivity for performance", + "Provides clean shutdown and resource cleanup", + "Uses post-quantum cryptography for security", + "Maintains perfect forward secrecy", + "Implements automatic key rotation", + "Handles session state transitions", + "Manages packet processing and connection state" + ] + }, + { + "file": "./citadel_proto/src/proto/node_request.rs", + "context": [ + "Defines node communication request types", + "Manages node registration and connections", + "Handles secure file transfer operations", + "Implements peer-to-peer command handling", + "Supports group broadcast functionality", + "Provides session security configuration", + "Implements key rotation operations", + "Uses ticket tracking for async responses", + "Supports SHA-256 password hashing", + "Configurable security levels for operations" + ] + }, + { + "file": "./citadel_proto/src/proto/state_container.rs", + "context": [ + "Core state management system for Citadel Protocol", + "Manages connection states and transitions", + "Handles virtual connections (P2P and C2S)", + "Implements secure group messaging", + "Manages file transfers with progress tracking", + "Supports UDP connectivity for performance", + "Implements cryptographic state verification", + "Protects against replay attacks", + "Provides secure group key management", + "Implements end-to-end file encryption", + "Handles connection timeouts and cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/mod.rs", + "context": [ + "Core packet processing infrastructure for the Citadel Protocol", + "Key aspects:", + "- Manages multiple packet types (connection, auth, data)", + "- Implements processing pipeline with validation", + "- Handles packet security and integrity", + "- Provides backpressure and ordering guarantees", + "- Coordinates between different packet processors" + ] + }, + { + "file": "./citadel_proto/src/proto/node.rs", + "context": [ + "Core networking implementation for Citadel Protocol", + "Supports multiple transport protocols (TCP, TLS, QUIC)", + "Implements NAT traversal for P2P connections", + "Manages concurrent network sessions", + "Uses post-quantum cryptography", + "Supports pre-shared key authentication", + "Handles both client-server and P2P modes", + "Provides protocol negotiation", + "Manages network socket listeners", + "Implements secure session establishment", + "Handles kernel communication", + "Supports TLS certificate management" + ] + }, + { + "file": "./citadel_proto/src/proto/node_result.rs", + "context": [ + "Defines result types for node operations", + "Handles registration, connection, and event results", + "Manages transfer and group operation outcomes", + "Provides comprehensive error reporting", + "Implements ticket tracking for async operations", + "Integrates with session and channel management" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/preconnect_packet.rs", + "context": [ + "Handles initial connection establishment", + "Implements NAT traversal and hole punching", + "Manages protocol version compatibility", + "Handles session state validation", + "Implements security level negotiation", + "Supports UDP and QUIC transports", + "Initializes cryptographic ratchets", + "Manages preconnect handshake flow", + "Validates session states", + "Processes connection requests", + "Handles NAT traversal configuration", + "Manages protocol version synchronization" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/udp_packet.rs", + "context": [ + "Handles UDP packet processing", + "Manages unordered channels", + "Validates packet security", + "Monitors channel state", + "Handles cleanup tasks", + "Processes secure payloads", + "Manages data transmission", + "Validates authentication", + "Handles session state", + "Manages buffer security", + "Processes channel drops", + "Handles error states" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/mod.rs", + "context": [ + "Manages peer communications", + "Handles group broadcasts", + "Processes peer commands", + "Manages server interactions", + "Handles signal processing", + "Manages disconnect signals", + "Tracks peer sessions", + "Handles operation results", + "Manages error states", + "Supports ticket operations", + "Processes peer state", + "Coordinates peer modules" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/signal_handler_interface.rs", + "context": [ + "Defines signal handling interface", + "Manages async signal operations", + "Handles outbound signals", + "Processes server signals", + "Manages target reception", + "Implements error handling", + "Uses async traits", + "Supports signal types", + "Manages signal flow", + "Requires type implementation", + "Handles network errors", + "Pending PeerSignal structification" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/server/mod.rs", + "context": [ + "Manages server-side peer operations", + "Handles post-connection states", + "Processes post-registration", + "Validates peer sessions", + "Tracks session states", + "Manages server operations", + "Handles connection states", + "Processes registration completion", + "Coordinates peer modules", + "Manages state tracking", + "Handles peer validation", + "Manages session completion" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs", + "context": [ + "Handles post-connection phase", + "Establishes virtual connections", + "Configures security settings", + "Manages UDP channels", + "Routes peer signals", + "Synchronizes session states", + "Manages connection tables", + "Handles TCP channels", + "Processes peer responses", + "Manages security levels", + "Tracks connection states", + "Needs disconnect cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/server/post_register.rs", + "context": [ + "Handles post-registration phase", + "Processes registration responses", + "Sets up HyperLAN P2P", + "Validates usernames", + "Routes signals", + "Manages tickets", + "Processes responses", + "Handles registration declines", + "Manages async registration", + "Needs error routing", + "Manages security levels", + "Tracks registration state" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs", + "context": [ + "Handles group messaging", + "Manages group membership", + "Processes group broadcasts", + "Controls group permissions", + "Tracks group state", + "Handles invitations", + "Manages group listing", + "Processes member states", + "Handles group creation", + "Manages group termination", + "Encrypts messages", + "Handles disconnections" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs", + "context": [ + "Processes peer commands", + "Routes peer signals", + "Handles key exchange", + "Manages group broadcasts", + "Tracks session states", + "Manages tickets", + "Handles error states", + "Mediates server operations", + "Validates authentication", + "Supports client/server roles", + "Manages peer sessions", + "Implements group operations" + ] + }, + { + "file": "./citadel_proto/src/proto/transfer_stats.rs", + "context": [ + "Handles network transfer statistics tracking with nanosecond precision", + "Core component for monitoring protocol performance metrics", + "Implements AddAssign for aggregating statistics over time", + "Tracks transfer rates, jitter, and total bytes transferred", + "Uses wrapping arithmetic to handle potential numeric overflows", + "Thread-safe design for concurrent statistics updates" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/channel.rs", + "context": [ + "Implements peer-to-peer communication channels", + "Provides both TCP and UDP channel implementations", + "Supports WebRTC compatibility through feature flag", + "Uses split architecture for async send/receive operations", + "Implements automatic resource cleanup on channel drop", + "Manages security levels for message encryption", + "Handles virtual connections between peers", + "Supports ordered and reliable message delivery", + "Integrates with the peer layer for connection management", + "Provides Stream trait implementation for async operations" + ] + }, + { + "file": "./citadel_proto/src/proto/peer_layer.rs", + "context": [ + "The peer layer module serves as the core peer-to-peer networking infrastructure in the Citadel Protocol. It manages peer connections, message groups, and signal routing between nodes. Key features include:", + "Peer signal management and routing between nodes", + "Message group functionality with concurrent/pending peer support", + "Ticket-based connection tracking and management", + "Timeout handling with callback support", + "HyperLAN client communication integration" + ] + }, + { + "file": "./citadel_proto/src/proto/p2p_conn_handler.rs", + "context": [ + "The P2P connection handler module implements direct peer-to-peer connections and NAT traversal in the Citadel Protocol. Notable features include:", + "Direct P2P connection management (TCP/UDP)", + "NAT traversal via UDP hole punching", + "Connection lifecycle management", + "WebRTC compatibility", + "Integration with Citadel's security infrastructure" + ] + }, + { + "file": "./citadel_proto/src/proto/group_channel.rs", + "context": [ + "The group channel module provides secure group communication channels in the Citadel Protocol. Key aspects include:", + "Split channel architecture (separate send/receive)", + "Group broadcast capabilities", + "Permission-based member management", + "Secure message encryption", + "Asynchronous message streaming support" + ] + }, + { + "file": "./citadel_proto/src/proto/message_group.rs", + "context": [ + "The message group module implements a consent-based group messaging framework in the Citadel Protocol. Key features include:", + "Axis of consent model centered around group initiator", + "Management of concurrent and pending peer states", + "Group lifecycle and permission management", + "Short-lived messaging frames for temporary communications", + "Local message history persistence" + ] + }, + { + "file": "./citadel_proto/src/proto/peer_crypt.rs", + "context": [ + "The peer cryptography module handles secure key exchange and NAT traversal for P2P connections. Notable aspects include:", + "Multi-stage key exchange protocol", + "NAT type detection and compatibility checking", + "TLS integration for secure connections", + "Configurable security levels and UDP modes", + "STUN/TURN server fallback support" + ] + }, + { + "file": "./citadel_proto/src/proto/hole_punch_compat_sink_stream.rs", + "context": [ + "The hole punch compatibility stream module provides NAT traversal functionality. Key features include:", + "Reliable ordered stream interface for hole punching", + "Protocol compatibility layer", + "Stacked ratchet encryption integration", + "Support for both C2S and P2P routing", + "Connection state and packet ordering management" + ] + }, + { + "file": "./citadel_proto/src/proto/mod.rs", + "context": [ + "Core implementation of the Citadel Protocol", + "Manages sessions, packet processing, and security", + "Implements peer-to-peer and group communication", + "Handles connection state management", + "Provides efficient packet validation and routing", + "Integrates with cryptographic and networking layers" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/mod.rs", + "context": [ + "The state subcontainers module provides specialized state management for different aspects of the Citadel Protocol's connection lifecycle. Key features include:", + "Connection state management and transitions", + "Key exchange and cryptographic state handling", + "Registration and authentication state tracking", + "State persistence and timeout management", + "Modular state container architecture" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", + "context": [ + "Core connection state management module for Citadel Protocol", + "Implements stage-based connection tracking and transitions", + "Handles credential management during connection process", + "Provides timing control and failure monitoring", + "Supports different connection modes and state recovery", + "Integrates with packet processing and session management", + "Uses atomic operations for thread-safe state transitions" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs", + "context": [ + "The peer key exchange management container handles P2P cryptographic states. Key features include:", + "Key exchange state management", + "Session security configuration", + "UDP channel control", + "Role-based key exchange", + "Session password management" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs", + "context": [ + "The pre-connection state container manages initial connection setup. Important aspects include:", + "Pre-connection stage tracking", + "Node type management", + "Cryptographic initialization", + "UDP channel setup", + "Connection ticket handling" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/register_state_container.rs", + "context": [ + "The registration state container handles user registration processes. Key features include:", + "Registration stage management", + "Cryptographic setup handling", + "Registration timing control", + "Failure state management", + "Passwordless registration support" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs", + "context": [ + "Manages account deregistration state in Citadel Protocol", + "Tracks deregistration process progress and timing", + "Handles deregistration tickets for process identification", + "Provides atomic state transitions for thread safety", + "Integrates with session and remote operations", + "Ensures proper validation of deregistration requests" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs", + "context": [ + "Manages expiration state for high-traffic packet processing", + "Prevents false expiration of active groups under high load", + "Supports both inbound and outbound traffic monitoring", + "Handles file transfer expiry tracking", + "Provides adaptive expiry timing based on workload", + "Integrates with packet processing and group management", + "Uses constant GROUP_EXPIRE_TIME_MS for timing control" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/rekey_container.rs", + "context": [ + "Manages cryptographic key rotation and ratchet updates", + "Implements security level-based key update scheduling", + "Supports peer-to-peer key updates and notifications", + "Provides configurable security levels (0-4)", + "Implements adaptive update frequency based on security needs", + "Handles local rekey requests and kernel notifications", + "Uses stacked ratchet construction for key rotation" + ] + }, + { + "file": "./citadel_proto/src/proto/codec.rs", + "context": [ + "Basic bytes codec implementation for raw data transmission", + "Handles efficient encoding/decoding with configurable buffer capacity", + "Implements zero-copy operations for performance", + "Uses tokio_util's Encoder and Decoder traits", + "Maintains minimum buffer size with auto-resizing" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/rekey_container.rs", + "context": [ + "Manages cryptographic key rotation and ratchet updates", + "Implements security level-based key update scheduling", + "Supports peer-to-peer key updates with state tracking", + "Provides configurable security levels (0-4)", + "Handles local rekey requests and notifications", + "Implements adaptive update frequency based on security needs" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", + "context": [ + "Manages active connection states with stage-based transitions", + "Handles connection credentials and authentication", + "Tracks connection timing and failures", + "Supports different connection modes", + "Provides state recovery mechanisms", + "Integrates with session and packet processing systems" + ] + }, + { + "file": "./citadel_proto/src/proto/peer/group_channel.rs", + "context": [ + "Implements secure group communication channels", + "Uses split channel architecture for send/receive operations", + "Supports group broadcasts and member management", + "Implements permission-based group operations", + "Provides Stream trait implementation for async messaging", + "Uses SecBuffer for encrypted message payloads", + "Handles proper cleanup through Drop implementations" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/clean_shutdown.rs", + "context": [ + "The clean shutdown module provides utilities for gracefully terminating components in the Citadel Protocol. It ensures proper resource cleanup and component notification during shutdown operations.", + "Key aspects:", + "- Implements asynchronous shutdown coordination", + "- Manages resource cleanup during shutdown", + "- Provides timeout-based forced shutdown", + "- Ensures thread-safe shutdown operations", + "- Broadcasts shutdown notifications to components" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/dual_cell.rs", + "context": [ + "The dual cell module implements a flexible cell type that adapts to both single-threaded and multi-threaded contexts through compile-time feature selection.", + "Key aspects:", + "- Uses Cell in single-threaded mode", + "- Uses atomics in multi-threaded mode", + "- Provides zero-cost thread safety abstractions", + "- Ensures proper Send + Sync implementations", + "- Maintains consistent API across modes" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/dual_late_init.rs", + "context": [ + "The dual late init module provides a container for safely initializing values after construction. It ensures proper initialization semantics across different threading contexts.", + "Key aspects:", + "- Ensures safe late initialization", + "- Tracks initialization state", + "- Prevents double initialization", + "- Provides thread-safe access", + "- Zero overhead in single-threaded mode" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/dual_rwlock.rs", + "context": [ + "The dual rwlock module implements a flexible read-write lock that adapts to both single-threaded and multi-threaded contexts through compile-time features.", + "Key aspects:", + "- Uses RefCell in single-threaded mode", + "- Uses RwLock in multi-threaded mode", + "- Supports multiple readers", + "- Provides exclusive writer access", + "- Prevents deadlocks", + "- Zero-cost thread safety abstractions" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/lock_holder.rs", + "context": [ + "The lock holder module provides safe lock management across asynchronous boundaries in the Citadel Protocol. It ensures proper lock handling and cleanup.", + "Key aspects:", + "- Manages locks in async contexts", + "- Prevents deadlocks", + "- Implements RAII-style cleanup", + "- Tracks lock states", + "- Ensures thread safety", + "- Prevents resource leaks" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/mod.rs", + "context": [ + "The misc module provides utility types and functions used throughout the Citadel Protocol implementation.", + "Key aspects:", + "- Provides thread-safe data structures", + "- Implements network utilities", + "- Manages protocol resources", + "- Supports async operations", + "- Defines protocol types", + "- Ensures zero-cost abstractions" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/net.rs", + "context": [ + "The network utilities module provides core networking functionality for the Citadel Protocol, handling connections and protocol operations.", + "Key aspects:", + "- Manages network sockets and connections", + "- Handles address resolution", + "- Implements protocol negotiation", + "- Supports multiple transport protocols", + "- Provides error handling and recovery", + "- Handles both IPv4 and IPv6" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/ordered_channel.rs", + "context": [ + "The ordered channel module implements a messaging channel that guarantees message ordering for protocol operations.", + "Key aspects:", + "- Ensures strict message ordering", + "- Implements asynchronous operations", + "- Provides backpressure mechanisms", + "- Supports multiple producers", + "- Maintains thread safety", + "- Handles channel state and cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/panic_future.rs", + "context": [ + "The panic future module provides a wrapper for safely handling panics in asynchronous code, preventing process crashes.", + "Key aspects:", + "- Catches and handles panics", + "- Converts panics to errors", + "- Wraps futures safely", + "- Ensures proper unwinding", + "- Maintains thread safety", + "- Preserves error types" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/session_security_settings.rs", + "context": [ + "The session security settings module defines configuration options for securing protocol sessions.", + "Key aspects:", + "- Configures encryption parameters", + "- Manages authentication settings", + "- Controls key management", + "- Sets security levels", + "- Handles protocol versions", + "- Provides secure defaults" + ] + }, + { + "file": "./netbeam/src/sync/subscription.rs", + "context": [ + "Provides bidirectional subscription-based streaming", + "Key aspects:", + "- Implements reliable ordered messaging", + "- Manages stream subscriptions", + "- Supports connection multiplexing", + "- Handles connection lifecycle", + "- Ensures thread-safety and ordering" + ] + }, + { + "file": "./netbeam/src/sync/sync_start.rs", + "context": [ + "Implements network operation synchronization primitives", + "Key aspects:", + "- Coordinates operation start between nodes", + "- Supports type-safe payload exchange", + "- Handles network latency compensation", + "- Integrates with async/await", + "- Provides timing coordination" + ] + }, + { + "file": "./netbeam/src/sync/network_endpoint.rs", + "context": [ + "Provides network endpoint abstraction with address management", + "Key aspects:", + "- Manages socket addresses", + "- Handles connection registration", + "- Tracks connection roles", + "- Integrates with network applications", + "- Provides address resolution" + ] + }, + { + "file": "./netbeam/src/sync/network_application.rs", + "context": [ + "Core network application implementation with synchronization primitives", + "Key aspects:", + "- Provides network synchronization primitives", + "- Implements network operations (select, join)", + "- Manages communication channels", + "- Handles connection multiplexing", + "- Ensures operation coordination" + ] + }, + { + "file": "./netbeam/src/sync/callback_channel.rs", + "context": [ + "Specialized channel implementation for async request-response patterns", + "Key aspects:", + "- Asynchronous message passing with optional callbacks", + "- Built on Tokio MPSC channels", + "- Supports fire-and-forget operations", + "- Thread-safe and cloneable", + "- Implements Stream trait for receiver" + ] + }, + { + "file": "./netbeam/src/sync/tracked_callback_channel.rs", + "context": [ + "Enhanced callback channel with request-response tracking", + "Key aspects:", + "- Request tracking with unique IDs", + "- Response correlation with requests", + "- Thread-safe tracking with atomics", + "- Memory-efficient response tracking", + "- Support for response timeouts" + ] + }, + { + "file": "./netbeam/src/sync/mod.rs", + "context": [ + "Core synchronization module for Netbeam framework", + "Key aspects:", + "- Network endpoint and application management", + "- Bidirectional channels with callbacks", + "- Request-response tracking", + "- Symmetric conversation tracking", + "- Reliable ordered streaming", + "- Thread-safe operations" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_join.rs", + "context": [ + "Network-aware join operation for future synchronization", + "Key aspects:", + "- Synchronizes futures across network endpoints", + "- Returns when both endpoints complete", + "- Early error termination", + "- Type-safe generic values", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_try_join.rs", + "context": [ + "Network-aware try-join operation for fallible future synchronization", + "Key aspects:", + "- Synchronizes fallible futures across endpoints", + "- Returns when both endpoints complete", + "- Early error termination", + "- Type-safe generic value and error types", + "- State synchronization between nodes", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_select.rs", + "context": [ + "Network-aware select operation for racing futures", + "Key aspects:", + "- Races futures between network endpoints", + "- First endpoint to complete wins", + "- Built-in conflict resolution", + "- Type-safe generic result type", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_select_ok.rs", + "context": [ + "Network-aware select operation for racing fallible futures", + "Key aspects:", + "- Races fallible futures between endpoints", + "- First successful endpoint wins", + "- Built-in conflict resolution", + "- Error handling with Result type", + "- State synchronization between nodes", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/primitives/net_mutex.rs", + "context": [ + "Distributed mutex implementation for network synchronization", + "Key aspects:", + "- Distributed mutual exclusion", + "- Network-aware locking", + "- Automatic lock release", + "- State synchronization", + "- Deadlock prevention", + "- Background state management" + ] + }, + { + "file": "./netbeam/src/sync/primitives/mod.rs", + "context": [ + "Provides network-aware synchronization primitives", + "Defines NetObject trait for network-compatible types", + "Houses distributed mutex and rwlock implementations", + "Ensures thread-safety and serializability across network boundaries", + "Integrates with Serde for object serialization" + ] + }, + { + "file": "./citadel_sdk/src/builder/mod.rs", + "context": [ + "Provides builder patterns for configuring Citadel Protocol components", + "Ensures type-safe configuration building", + "Validates component configurations", + "Supports flexible node setup for different network roles", + "Integrates with core networking kernel", + "Manages protocol implementation details" + ] + }, + { + "file": "./citadel_sdk/src/builder/node_builder.rs", + "context": [ + "Implements builder pattern for Citadel network nodes", + "Supports both peer and server node configuration", + "Provides multiple backend storage options", + "Configures security settings (TLS, certificates)", + "Integrates with Google services", + "Supports STUN server configuration for NAT traversal", + "Manages server authentication via pre-shared keys", + "Handles kernel executor settings", + "Configures password hashing settings", + "Supports database configuration for enterprise features" + ] + }, + { + "file": "./citadel_sdk/src/backend_kv_store.rs", + "context": [ + "Implements connection-scoped persistent key-value storage", + "Provides async operations for storing and retrieving arbitrary data", + "Uses session and peer IDs for storage isolation", + "Integrates with the persistence handler backend", + "Supports bulk operations for managing multiple key-value pairs", + "Implements automatic error handling and conversion", + "Designed for application-level data persistence" + ] + }, + { + "file": "./citadel_sdk/src/macros.rs", + "context": [ + "Provides procedural macros for SDK trait implementations", + "Reduces boilerplate in network communication code", + "Supports async/await patterns in trait implementations", + "Ensures consistent Remote trait implementations", + "Integrates with account management and request handling", + "Used internally by the SDK for code generation", + "Requires async-trait feature for async implementations" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/mod.rs", + "context": [ + "Root module for client-side networking components", + "Provides connection builders and configuration", + "Supports multiple authentication methods", + "Manages UDP and NAT traversal settings", + "Implements session security and PSK handling", + "Organizes broadcast, peer, and server connections", + "Defines base traits for prefab implementations" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/single_connection.rs", + "context": [ + "Implements single client-to-server connection management", + "Supports multiple authentication modes", + "Provides NAT traversal with UDP support", + "Handles secure session management", + "Supports object transfer handling", + "Manages connection lifecycle" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/peer_connection.rs", + "context": [ + "Manages peer-to-peer connections", + "Supports multiple simultaneous peers", + "Implements file transfer capabilities", + "Handles NAT traversal settings", + "Provides session security", + "Manages peer identification" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/broadcast.rs", + "context": [ + "Implements group-based communication", + "Uses owner-based trust model", + "Supports public and private groups", + "Handles member registration", + "Manages group invitations", + "Enables concurrent participation" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/mod.rs", + "context": [ + "Root module for pre-built network components", + "Organizes client and server implementations", + "Provides remote connection management", + "Handles file transfer functionality", + "Manages signal and event processing", + "Implements connection security", + "Supports peer discovery and listing" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs", + "context": [ + "Provides automatic handling of file transfers on the server side", + "Implements automatic acceptance and silent processing", + "Uses minimal resources with no configuration needed", + "Integrates with NetKernel for event handling", + "Handles error conditions gracefully", + "Supports both basic and RE-VFS transfers" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/client_connect_listener.rs", + "context": [ + "Executes custom logic on client connection events", + "Implements async event processing and security management", + "Supports both TCP and UDP channels", + "Handles session security settings", + "Manages connection success events", + "Provides type-safe callback execution" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/empty.rs", + "context": [ + "Provides minimal no-op network kernel implementation", + "Implements zero overhead event acceptance", + "Suitable for basic connection acceptance", + "Uses minimal system resources", + "Provides clean shutdown handling", + "Not designed for interactive servers" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/internal_service.rs", + "context": [ + "Enables integration of internal services like HTTP servers", + "Supports HTTP/1.1 and HTTP/2 protocols", + "Implements bidirectional communication", + "Provides service lifecycle management", + "Handles automatic resource cleanup", + "Supports custom service handlers" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/mod.rs", + "context": [ + "Organizes server-side network components", + "Provides pre-built server implementations", + "Supports file transfer and client connections", + "Enables internal service integration", + "Implements event-driven architecture", + "Manages component lifecycle" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/shared/internal_service.rs", + "context": [ + "Provides core functionality for internal service integration", + "Implements bidirectional communication channels", + "Uses async I/O for efficient communication", + "Handles protocol conversion automatically", + "Manages resource cleanup and shutdown", + "Supports thread-safe message passing" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/shared/mod.rs", + "context": [ + "Contains shared network components for client and server", + "Provides role-agnostic functionality", + "Implements thread-safe shared utilities", + "Uses async-first design patterns", + "Manages cross-role functionality", + "Organizes internal service integration" + ] + } +] diff --git a/firebase-rtdb/Cargo.toml b/firebase-rtdb/Cargo.toml index b3e7b2921..c454ba58c 100644 --- a/firebase-rtdb/Cargo.toml +++ b/firebase-rtdb/Cargo.toml @@ -21,3 +21,4 @@ serde = { workspace = true, features = ["derive"] } [dev-dependencies] citadel_io = { workspace = true } citadel_logging = { workspace = true } +serde_json = { workspace = true } diff --git a/firebase-rtdb/src/lib.rs b/firebase-rtdb/src/lib.rs index 5943dc8f3..aff7a33aa 100644 --- a/firebase-rtdb/src/lib.rs +++ b/firebase-rtdb/src/lib.rs @@ -1,3 +1,92 @@ +//! # Firebase Realtime Database Client +//! +//! A lightweight, async Rust client for interacting with Firebase Realtime Database. +//! This crate provides a safe and ergonomic interface for performing CRUD operations +//! on Firebase RTDB with JWT authentication support. +//! +//! ## Features +//! +//! - JWT-based authentication with automatic token renewal +//! - Support for all CRUD operations (GET, PUT, POST, PATCH, DELETE) +//! - Hierarchical node-based access to database paths +//! - Connection timeout handling and error management +//! - Serialization/deserialization support via serde +//! - TLS encryption for all requests +//! - Automatic token refresh before expiration +//! +//! ## Example +//! +//! ```rust +//! use firebase_rtdb::{FirebaseRTDB, Node, RtdbError}; +//! use serde_json::json; +//! +//! async fn example() -> Result<(), RtdbError> { +//! // Initialize with JWT +//! let mut db = FirebaseRTDB::new_from_jwt( +//! "https://your-project.firebaseio.com", +//! "your-jwt-token", +//! "your-api-key" +//! ).await?; +//! +//! // Access and modify data +//! let mut root = db.root().await?; +//! let mut users = root.child("users"); +//! +//! // Read data +//! let user_data = users.child("user1").get().await?; +//! +//! // Write data +//! users.child("user2") +//! .put(json!({ +//! "name": "John Doe", +//! "age": 30, +//! "email": "john@example.com" +//! })).await?; +//! +//! // Update specific fields +//! users.child("user2") +//! .patch(json!({ +//! "age": 31, +//! "last_login": "2023-01-01" +//! })).await?; +//! +//! // Create new entry with generated key +//! let new_user = users +//! .post(json!({ +//! "name": "Jane Doe", +//! "age": 25 +//! })).await?; +//! +//! // Delete data +//! users.child("old_user").delete().await?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Security Considerations +//! +//! - JWT tokens are automatically renewed before expiration +//! - All HTTP requests use TLS encryption +//! - Connection timeouts are enforced to prevent hanging +//! - API keys and tokens should be kept secure and not hardcoded +//! - Supports Firebase Security Rules for access control +//! +//! ## Error Handling +//! +//! The crate uses a custom `RtdbError` type that wraps various error conditions: +//! - Network errors (connection timeouts, DNS failures) +//! - Authentication errors (invalid tokens, expired credentials) +//! - Database errors (permission denied, invalid paths) +//! - Serialization/deserialization errors +//! +//! ## Performance +//! +//! - Uses connection pooling via reqwest +//! - TCP nodelay enabled for reduced latency +//! - Efficient token renewal with expiration buffering +//! - Reuses HTTP clients for better performance + #![allow(non_snake_case)] use reqwest::header::CONTENT_TYPE; @@ -7,6 +96,19 @@ use std::error::Error; use std::str::FromStr; use std::time::{Duration, Instant}; +/// Firebase Realtime Database client with JWT authentication support. +/// +/// This struct maintains the authentication state and provides access to database operations. +/// It automatically handles token renewal and maintains connection settings. +/// +/// # Fields +/// +/// * `base_url` - The base URL of your Firebase project +/// * `client` - HTTP client with connection pooling and TLS support +/// * `auth` - Current authentication state including tokens +/// * `expire_time` - Token expiration timestamp +/// * `api_key` - Firebase project API key +/// * `jwt` - Original JWT token for renewals #[derive(Clone, Debug)] pub struct FirebaseRTDB { pub base_url: String, @@ -17,8 +119,19 @@ pub struct FirebaseRTDB { pub jwt: String, } +/// Default buffer time before token expiration to trigger renewal pub const DEFAULT_EXPIRE_BUFFER_SECS: Duration = Duration::from_secs(5); +/// Authentication response from Firebase containing tokens and expiration. +/// +/// This struct represents the response from Firebase authentication endpoints +/// and contains the necessary tokens for database access. +/// +/// # Fields +/// +/// * `idToken` - The token used for database operations +/// * `refreshToken` - Token used to obtain new credentials +/// * `expiresIn` - Token lifetime in seconds #[derive(Deserialize, Serialize, Debug, Clone)] pub struct AuthResponsePayload { pub idToken: String, @@ -26,12 +139,41 @@ pub struct AuthResponsePayload { pub expiresIn: String, } +/// Represents a node in the Firebase Realtime Database. +/// +/// Nodes are used to traverse the database hierarchy and perform operations at specific paths. +/// Each node maintains its full path and can create child nodes or perform CRUD operations. +/// +/// The path is built incrementally using the builder pattern, allowing for fluent API usage. +/// +/// # Example +/// +/// ```rust +/// use firebase_rtdb::{FirebaseRTDB, RtdbError}; +/// +/// async fn example() -> Result<(), RtdbError> { +/// let mut db = FirebaseRTDB::new_from_jwt("https://your-project.firebaseio.com", "your-jwt-token", "your-api-key").await?; +/// let mut root = db.root().await?; +/// let users = root +/// .child("users") +/// .child("user123") +/// .child("profile"); +/// Ok(()) +/// } +/// ``` pub struct Node<'a> { string_builder: String, client: &'a Client, token: &'a String, } +/// Custom error type for Firebase RTDB operations. +/// +/// Encapsulates various error conditions that can occur during database operations, +/// including network errors, authentication failures, and invalid data. +/// +/// All errors are converted to a string representation for simplified error handling +/// while maintaining the original error context. #[derive(Debug)] pub struct RtdbError { pub inner: String, @@ -45,12 +187,24 @@ impl From for RtdbError { } } +/// Default connection timeout for HTTP requests const CONNECT_TIMEOUT: Duration = Duration::from_secs(5); impl FirebaseRTDB { - /// `project_url`: e.g., https://PROJECT_ID.firebaseio.com/ + /// Creates a new Firebase RTDB client using a JWT token. + /// + /// This method will contact the authorization server to obtain necessary credentials. + /// The JWT token is used for initial authentication and subsequent token renewals. + /// + /// # Arguments /// - /// This will contact the authorization server in order to get the proper values + /// * `project_url` - The base URL of your Firebase project (e.g., "https://project-id.firebaseio.com") + /// * `jwt` - A valid JWT token for authentication + /// * `api_key` - Your Firebase project's API key + /// + /// # Returns + /// + /// Returns a Result containing the initialized FirebaseRTDB client or an error pub async fn new_from_jwt, R: Into, V: AsRef>( project_url: T, jwt: R, @@ -99,7 +253,18 @@ impl FirebaseRTDB { }) } - /// Use this if authentication already occurred, and the token is still valid + /// Creates a new Firebase RTDB client using an existing valid token. + /// + /// Use this method when you already have valid authentication credentials and want to + /// avoid an initial token refresh. + /// + /// # Arguments + /// + /// * `project_url` - The base URL of your Firebase project + /// * `api_key` - Your Firebase project's API key + /// * `jwt` - A valid JWT token + /// * `auth` - Existing authentication payload + /// * `expire_time` - Token expiration time pub fn new_from_token, R: Into, V: Into>( project_url: T, api_key: R, @@ -119,7 +284,10 @@ impl FirebaseRTDB { }) } - /// Unconditionally renews the token. Make sure to update internal client config afterwards as data could have changed + /// Renews the authentication token. + /// + /// This method should be called when the current token is about to expire or has expired. + /// It will update the internal authentication state with new credentials. pub async fn renew_token(&mut self) -> Result<(), RtdbError> { #[derive(Serialize)] struct RenewPayload { @@ -176,11 +344,15 @@ impl FirebaseRTDB { Ok(()) } - /// Returns true if the token expired. will need to be refreshed before use again + /// Checks if the current authentication token has expired. + /// + /// Returns true if the token has expired and needs to be renewed before making + /// further database requests. pub fn token_expired(&self) -> bool { Instant::now() + DEFAULT_EXPIRE_BUFFER_SECS > self.expire_time } + /// Creates a new reqwest Client with appropriate timeout settings. fn build_client() -> Result { Ok(Client::builder() .use_rustls_tls() @@ -189,7 +361,10 @@ impl FirebaseRTDB { .build()?) } - /// Updates the token if required + /// Returns a Node representing the root of the database. + /// + /// This method will automatically renew the token if it has expired. + /// Use this as the starting point for accessing database paths. pub async fn root(&mut self) -> Result, RtdbError> { if self.token_expired() { self.renew_token().await? @@ -204,6 +379,15 @@ impl FirebaseRTDB { } impl Node<'_> { + /// Creates a new child node at the specified path. + /// + /// # Arguments + /// + /// * `child` - The name of the child node to create + /// + /// # Returns + /// + /// Returns a mutable reference to the new child node pub fn child>(&mut self, child: T) -> &mut Self { self.string_builder += child.as_ref(); self.string_builder += "/"; @@ -211,6 +395,10 @@ impl Node<'_> { self } + /// Finalizes the node path with a last segment. + /// + /// Similar to child() but returns an immutable reference, useful for + /// immediately performing an operation. pub fn final_node>(&mut self, node: T) -> &Self { self.string_builder += node.as_ref(); self.string_builder += ".json"; @@ -218,6 +406,9 @@ impl Node<'_> { self } + /// Retrieves data at the current node path. + /// + /// Performs a GET request to fetch the current value at this database location. pub async fn get(&self) -> Result { let resp = self .client @@ -227,6 +418,10 @@ impl Node<'_> { Self::handle_response(resp).await } + /// Writes data to the current node path. + /// + /// Performs a PUT request to set the value at this database location. + /// The input must be serializable to JSON. pub async fn put(&self, input: T) -> Result { let resp = self .client @@ -237,6 +432,10 @@ impl Node<'_> { Self::handle_response(resp).await } + /// Creates a new child with a unique key. + /// + /// Performs a POST request to create a new child with a Firebase-generated key + /// and the provided data. pub async fn post(&self, input: T) -> Result { let resp = self .client @@ -247,6 +446,10 @@ impl Node<'_> { Self::handle_response(resp).await } + /// Updates specific fields at the current path. + /// + /// Performs a PATCH request to update only the specified fields while leaving + /// others unchanged. pub async fn patch(&self, input: T) -> Result { let resp = self .client @@ -257,6 +460,9 @@ impl Node<'_> { Self::handle_response(resp).await } + /// Removes data at the current path. + /// + /// Performs a DELETE request to remove all data at this location. pub async fn delete(&self) -> Result { let resp = self .client @@ -266,6 +472,7 @@ impl Node<'_> { Self::handle_response(resp).await } + /// Processes the HTTP response and extracts the result or error. async fn handle_response(resp: Response) -> Result { if resp.status().as_u16() == 200 { Ok(resp.text().await?) diff --git a/netbeam/src/lib.rs b/netbeam/src/lib.rs index cb1e87139..eb61fca51 100644 --- a/netbeam/src/lib.rs +++ b/netbeam/src/lib.rs @@ -1,13 +1,105 @@ +//! # Netbeam +//! +//! A high-performance networking library providing multiplexing, reliable connections, +//! and synchronization primitives for building robust networked applications. +//! +//! ## Features +//! +//! - **Multiplexing**: Create multiple logical streams over a single connection +//! - **Reliable Connections**: Guaranteed ordered delivery of messages +//! - **Synchronization**: Thread-safe primitives for network applications +//! - **Time Tracking**: Precise timing utilities for network operations +//! - **Zero Unsafe Code**: Completely safe Rust implementation +//! +//! ## Core Components +//! +//! - `multiplex`: Multiplexed connection handling and stream management +//! - `reliable_conn`: Traits and implementations for reliable ordered connections +//! - `sync`: Synchronization primitives and network application utilities +//! - `time_tracker`: Precise timing utilities for network operations +//! +//! ## Example +//! +//! ```rust,no_run +//! use anyhow::Result; +//! use netbeam::multiplex::MultiplexedConn; +//! use netbeam::sync::SymmetricConvID; +//! use netbeam::reliable_conn::ReliableOrderedStreamToTarget; +//! use netbeam::sync::subscription::Subscribable; +//! use netbeam::sync::RelativeNodeType; +//! +//! async fn example() -> Result<()> { +//! // This is just a placeholder - replace with your actual connection +//! let conn = get_connection().await?; +//! +//! // Create a multiplexed connection +//! let muxed_conn = MultiplexedConn::::new(RelativeNodeType::Initiator, conn); +//! +//! // Create a new stream with a unique ID +//! let stream_id = 1.into(); +//! let mut stream = muxed_conn.subscribe(stream_id); +//! +//! // Send data +//! stream.send_to_peer(b"Hello").await?; +//! +//! // Receive response +//! let response = stream.recv().await?; +//! println!("Received: {:?}", response); +//! +//! Ok(()) +//! } +//! # async fn get_connection() -> Result { Ok(citadel_io::tokio::net::TcpStream::connect("127.0.0.1:8080").await?) } +//! ``` +//! +//! ## Synchronization Example +//! +//! ```rust,no_run +//! use anyhow::Result; +//! use netbeam::sync::primitives::net_mutex::NetMutex; +//! use netbeam::sync::subscription::Subscribable; +//! +//! async fn sync_example(connection: &S) -> Result<()> { +//! // Create a network-aware mutex +//! let mutex = NetMutex::create(connection, Some(0)).await?; +//! +//! // Acquire the lock +//! let mut guard = mutex.lock().await?; +//! +//! // Modify the protected data +//! *guard += 1; +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Design Philosophy +//! +//! Netbeam is built with the following principles: +//! +//! 1. **Safety**: Zero unsafe code, leveraging Rust's type system +//! 2. **Performance**: Efficient multiplexing and minimal overhead +//! 3. **Reliability**: Guaranteed message ordering and delivery +//! 4. **Flexibility**: Extensible traits for custom implementations +//! +//! ## Usage Notes +//! +//! - All network operations are async and require a Tokio runtime +//! - Proper error handling is essential as network operations can fail +//! - Stream IDs should be coordinated between endpoints +//! - Consider using the synchronization primitives for shared state #![forbid(unsafe_code)] use std::future::Future; use std::pin::Pin; +pub mod multiplex; pub mod reliable_conn; pub mod sync; pub mod time_tracker; -pub mod multiplex; - +/// A scoped future result type used internally by the crate. +/// +/// This type alias represents a pinned, boxed future that returns a Result +/// and can be sent across thread boundaries. pub(crate) type ScopedFutureResult<'a, T> = Pin> + Send + 'a>>; diff --git a/netbeam/src/multiplex.rs b/netbeam/src/multiplex.rs index 6c5e99375..5684a1880 100644 --- a/netbeam/src/multiplex.rs +++ b/netbeam/src/multiplex.rs @@ -1,3 +1,37 @@ +//! # Network Stream Multiplexing +//! +//! Provides a robust multiplexing system for network streams, allowing multiple logical connections +//! to share a single underlying network connection. This module implements connection multiplexing +//! with automatic stream management and bi-directional communication support. +//! +//! ## Features +//! +//! * Dynamic stream multiplexing over a single connection +//! * Automatic stream ID generation and management +//! * Bi-directional communication channels +//! * Thread-safe subscription management +//! * Pre-action and post-action hooks for stream lifecycle +//! * Support for custom connection key types +//! * Reliable ordered message delivery +//! * Automatic cleanup of dropped streams +//! +//! ## Important Notes +//! +//! * Streams are automatically cleaned up when dropped +//! * All operations are thread-safe and async-aware +//! * Messages within each multiplexed stream maintain order +//! * Custom key types must implement MultiplexedConnKey trait +//! * Pre-action signals ensure proper stream initialization +//! * Post-action signals handle graceful stream closure +//! +//! ## Related Components +//! +//! * `reliable_conn`: Underlying reliable stream implementation +//! * `sync::subscription`: Stream subscription management +//! * `sync::network_application`: Network action handling +//! * `sync::RelativeNodeType`: Node type identification +//! + use crate::reliable_conn::{ReliableOrderedStreamToTarget, ReliableOrderedStreamToTargetExt}; use crate::sync::network_application::{PostActionChannel, PreActionChannel, INITIAL_CAPACITY}; use crate::sync::subscription::{ @@ -18,6 +52,7 @@ use std::ops::Deref; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; +/// A trait representing a multiplexed connection key. pub trait MultiplexedConnKey: Debug + Eq + Hash + Copy + Send + Sync + Serialize + DeserializeOwned + IDGen { @@ -27,10 +62,15 @@ impl { + /// The type of container used to generate IDs. type Container: Send + Sync; + /// Generates a new container for ID generation. fn generate_container() -> Self::Container; + /// Generates the next ID in the sequence. fn generate_next(container: &Self::Container) -> Self; + /// Gets the proposed next ID in the sequence. fn get_proposed_next(container: &Self::Container) -> Key; } @@ -50,22 +90,34 @@ impl IDGen for SymmetricConvID { } } +/// A multiplexed connection. pub struct MultiplexedConn { inner: Arc>, } +/// The inner implementation of a multiplexed connection. pub struct MultiplexedConnInner { + /// The underlying reliable connection. pub(crate) conn: Arc, + /// A map of subscribers. subscribers: RwLock>, + /// A channel for pre-action signals. pre_open_container: PreActionChannel, + /// A channel for post-action signals. post_close_container: PostActionChannel, + /// The ID generator. id_gen: K::Container, + /// The current latest subscribed ID. current_latest_subscribed: K::Container, + /// The node type. node_type: RelativeNodeType, } +/// A memory sender. pub struct MemorySender { + /// The sender. tx: UnboundedSender>, + /// The pre-reserved receiver. pre_reserved_rx: Option>>, } @@ -85,16 +137,22 @@ impl Deref for MultiplexedConn { } } +/// A multiplexed packet. #[derive(Serialize, Deserialize)] #[serde(bound = "")] pub(crate) enum MultiplexedPacket { + /// An application layer packet. ApplicationLayer { id: K, payload: Vec }, + /// A post-drop packet. PostDrop { id: K }, + /// A pre-create packet. PreCreate { id: K }, + /// A greeter packet. Greeter, } impl MultiplexedConn { + /// Creates a new multiplexed connection. pub fn new( node_type: RelativeNodeType, conn: T, @@ -142,9 +200,13 @@ impl Clone for MultiplexedConn { } } +/// A multiplexed subscription. pub struct MultiplexedSubscription<'a, K: MultiplexedConnKey = SymmetricConvID> { + /// A reference to the multiplexed connection. ptr: &'a MultiplexedConn, + /// The receiver. receiver: Option>>>, + /// The ID. id: K, } @@ -185,9 +247,13 @@ impl From> } } +/// An owned multiplexed subscription. pub struct OwnedMultiplexedSubscription { + /// The multiplexed connection. ptr: MultiplexedConn, + /// The receiver. receiver: Mutex>>, + /// The ID. id: K, } diff --git a/netbeam/src/reliable_conn.rs b/netbeam/src/reliable_conn.rs index 2b25b1168..4094c5698 100644 --- a/netbeam/src/reliable_conn.rs +++ b/netbeam/src/reliable_conn.rs @@ -1,3 +1,47 @@ +//! # Reliable Connection Module +//! +//! This module provides traits and implementations for reliable, ordered network connections. +//! It is designed to support both direct client-server connections and peer-to-peer connections +//! through NAT traversal. +//! +//! ## Key Components +//! +//! - `ReliableOrderedStreamToTarget`: Core trait for reliable message delivery +//! - `ConnAddr`: Trait for connection addressing +//! - `ReliableOrderedConnectionToTarget`: Combined trait for reliable addressed connections +//! - `StreamWrapper`: Implementation for AsyncRead + AsyncWrite streams +//! - `NetworkConnSimulator`: Network condition simulator for testing +//! +//! ## Features +//! +//! - Guaranteed message ordering +//! - Support for NAT traversal +//! - Optional encryption layer +//! - Automatic serialization/deserialization +//! - Network simulation for testing +//! +//! ## Example +//! +//! ```rust,no_run +//! use netbeam::reliable_conn::{ReliableOrderedStreamToTarget, StreamWrapper}; +//! use anyhow::Result; +//! use citadel_io::tokio::net::TcpStream; +//! +//! async fn example() -> Result<()> { +//! // This is just an example - replace with your actual connection +//! let stream = TcpStream::connect("127.0.0.1:8080").await?; +//! let mut reliable_stream = StreamWrapper::from(stream); +//! +//! // Send data with guaranteed ordering +//! reliable_stream.send_to_peer(b"Hello").await?; +//! +//! // Receive response +//! let response = reliable_stream.recv().await?; +//! println!("Received: {:?}", response); +//! Ok(()) +//! } +//! ``` + use async_trait::async_trait; use bytes::{Bytes, BytesMut}; use citadel_io::tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; @@ -8,21 +52,52 @@ use std::net::SocketAddr; use std::sync::Arc; #[async_trait] -/// This represents a direct client to server or client->server->peer connection (usually just TCP) for establishing the hole-punching process +/// Core trait for reliable, ordered message delivery between network endpoints. +/// +/// This trait represents a connection that guarantees message ordering and delivery, +/// typically implemented over TCP or similar reliable protocols. It can be used for +/// both direct connections and NAT-traversed peer-to-peer connections. pub trait ReliableOrderedStreamToTarget: Send + Sync { - /// Accepts plaintext from the NAT traversal driver. Encryption can be optionally applied + /// Sends plaintext data to the peer. + /// + /// This method accepts raw bytes and handles reliable delivery to the target. + /// Implementations may optionally apply encryption or other transformations. + /// + /// # Arguments + /// + /// * `input` - The data to send async fn send_to_peer(&self, input: &[u8]) -> std::io::Result<()>; - /// returns the plaintext + + /// Receives plaintext data from the peer. + /// + /// Returns the next message in the ordered sequence from the peer. + /// Any encryption or transformation is handled by the implementation. async fn recv(&self) -> std::io::Result; } +/// Trait for accessing connection addresses. +/// +/// This trait provides methods to get both local and peer socket addresses, +/// which is essential for NAT traversal and connection management. pub trait ConnAddr { - /// Returns the bind addr. Used for establishing a local UDP socket + /// Returns the local bind address. + /// + /// This is typically used for establishing local UDP sockets and + /// identifying the local endpoint. fn local_addr(&self) -> std::io::Result; - /// Returns the peer addr. If relaying is used to get the packet to the peer, then the peer addr should be used, not the relay addr + + /// Returns the peer's address. + /// + /// For direct connections, this is the peer's socket address. + /// For relayed connections, this should be the ultimate peer address, + /// not the relay server address. fn peer_addr(&self) -> std::io::Result; } +/// Combined trait for reliable, ordered, addressed connections. +/// +/// This trait combines `ConnAddr` and `ReliableOrderedStreamToTarget` to provide +/// a complete interface for reliable network connections with addressing capabilities. pub trait ReliableOrderedConnectionToTarget: ConnAddr + ReliableOrderedStreamToTarget {} impl ReliableOrderedConnectionToTarget for T {} @@ -154,6 +229,12 @@ impl ReliableOrderedStreamToTarget for } pub mod simulator { + //! Network condition simulation module. + //! + //! This module provides tools for simulating various network conditions + //! such as latency and packet loss, which is useful for testing network + //! applications under different scenarios. + use crate::reliable_conn::{ ConnAddr, ReliableOrderedConnectionToTarget, ReliableOrderedStreamToTarget, }; @@ -164,6 +245,11 @@ pub mod simulator { use std::net::SocketAddr; use std::sync::Arc; + /// Simulates network conditions for testing. + /// + /// This struct wraps a reliable connection and adds simulated network + /// conditions such as latency, making it useful for testing how + /// applications behave under various network scenarios. pub struct NetworkConnSimulator { inner: Arc, fwd: UnboundedSender>, diff --git a/netbeam/src/sync/callback_channel.rs b/netbeam/src/sync/callback_channel.rs index a776c2720..e28f45543 100644 --- a/netbeam/src/sync/callback_channel.rs +++ b/netbeam/src/sync/callback_channel.rs @@ -1,17 +1,65 @@ +/*! + * # Callback Channel + * + * A specialized channel implementation that enables asynchronous request-response patterns + * with optional callback support. + * + * ## Features + * - Asynchronous message passing with response callbacks + * - Support for fire-and-forget operations (no callback required) + * - Built on top of Tokio's MPSC channels + * - Implements Stream trait for receiver side + * - Thread-safe and Clone-able design + * + * ## Usage Example + * ```rust + * use netbeam::sync::callback_channel::CallbackChannel; + * + * async fn example() { + * // Create a new channel with buffer size 10 + * let (channel, receiver) = CallbackChannel::::new(10); + * + * // Send with callback + * if let Ok(result) = channel.send("hello".to_string()).await { + * println!("Got response: {}", result); + * } + * + * // Send without callback + * let _ = channel.send_no_callback("world".to_string()).await; + * } + * ``` + * + * ## Related Components + * - `tracked_callback_channel.rs`: Enhanced version with tracking capabilities + * - `bi_channel.rs`: Bidirectional channel implementation + * + * ## Important Notes + * - Channel operations are fallible and return Result types + * - Callbacks are optional via send_no_callback + * - Implements Stream trait for easy integration with async streams + */ + use citadel_io::tokio::sync::mpsc::{Receiver, Sender}; use futures::Stream; use std::fmt::{Debug, Formatter}; use std::pin::Pin; use std::task::{Context, Poll}; +/// A specialized channel implementation that enables asynchronous request-response patterns +/// with optional callback support. #[derive(Clone)] pub struct CallbackChannel { + /// Inner implementation details of the callback channel. inner: CallbackChannelInner, } +/// Enum representing possible errors that can occur during callback channel operations. pub enum CallbackError { + /// Error occurred while sending a message. SendError(T), + /// Error occurred while receiving a message. RecvError, + /// Internal error occurred during channel operations. InternalError(&'static str), } @@ -33,14 +81,20 @@ impl Debug for CallbackError { } } +/// Inner implementation details of the callback channel. #[derive(Clone)] struct CallbackChannelInner { + /// Sender for the callback channel. to_channel: Sender>, } +/// Type alias for the payload of the callback channel. pub type CallbackChannelPayload = (T, Option>); impl CallbackChannel { + /// Creates a new callback channel with the specified buffer size. + /// + /// Returns a tuple containing the sender and receiver of the channel. pub fn new(buffer: usize) -> (Self, CallbackReceiver) { let (to_channel, from_channel) = citadel_io::tokio::sync::mpsc::channel(buffer); ( @@ -53,6 +107,9 @@ impl CallbackChannel { ) } + /// Sends a message through the channel with an optional callback. + /// + /// Returns a result containing the response from the receiver, or an error if the send operation fails. pub async fn send(&self, payload: T) -> Result> { let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); self.inner @@ -63,6 +120,9 @@ impl CallbackChannel { rx.await.map_err(|_| CallbackError::RecvError) } + /// Sends a message through the channel without a callback. + /// + /// Returns a result indicating whether the send operation was successful, or an error if it fails. pub async fn send_no_callback(&self, payload: T) -> Result<(), CallbackError> { self.inner .to_channel @@ -72,13 +132,16 @@ impl CallbackChannel { } } +/// Receiver for the callback channel. pub struct CallbackReceiver { + /// Inner implementation details of the receiver. inner: Receiver>, } impl Stream for CallbackReceiver { type Item = CallbackChannelPayload; + /// Polls the receiver for the next message. fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.inner).poll_recv(cx) } diff --git a/netbeam/src/sync/channel/bi_channel.rs b/netbeam/src/sync/channel/bi_channel.rs index 2bf1e1969..dfeefa692 100644 --- a/netbeam/src/sync/channel/bi_channel.rs +++ b/netbeam/src/sync/channel/bi_channel.rs @@ -1,3 +1,54 @@ +/*! + * # Bidirectional Channel + * + * A bidirectional channel implementation that enables two-way communication between + * network endpoints. This serves as the base abstraction for other channel types + * in the netbeam framework. + * + * ## Features + * - Two-way communication with send and receive halves + * - Support for reliable ordered streaming + * - Graceful channel shutdown with halt verification + * - Async/await support with Future and Stream implementations + * - Thread-safe operation + * + * ## Usage Example + * ```rust + * use netbeam::sync::channel::bi_channel::Channel; + * use netbeam::sync::subscription::Subscribable; + * use anyhow::Result; + * + * async fn example(connection: &S) -> Result<()> { + * // Create a new channel + * let channel = Channel::create(connection).await?; + * + * // Split into send and receive halves + * let (sender, mut receiver) = channel.split(); + * + * // Send data (replace with your data type) + * sender.send_item("hello".to_string()).await?; + * + * // Receive data + * if let Some(data) = receiver.recv().await { + * if let Ok(msg) = data { + * println!("Received: {:?}", msg); + * } + * } + * Ok(()) + * } + * ``` + * + * ## Related Components + * - `callback_channel.rs`: Channel with callback support + * - `tracked_callback_channel.rs`: Channel with request tracking + * + * ## Important Notes + * - Channels implement graceful shutdown + * - Send and receive halves can be used independently + * - Supports reliable ordered streaming + * - Thread-safe with atomic operations + */ + use crate::reliable_conn::ReliableOrderedStreamToTargetExt; use crate::sync::primitives::NetObject; use crate::sync::subscription::Subscribable; @@ -12,39 +63,54 @@ use std::sync::Arc; use std::task::{Context, Poll}; use sync_wrapper::SyncWrapper; +/// Inner channel type for a given subscribable type `S`. pub(crate) type InnerChannel = ::SubscriptionType; +/// Enum representing different types of packets that can be sent over the channel. #[derive(Serialize, Deserialize, Debug)] enum ChannelPacket { + /// A packet containing a value of type `T`. Packet(T), + /// A halt signal indicating that no more packets will be sent. Halt, + /// A verified halt signal indicating that the halt signal has been received. HaltVerified, } -/// Allows two-way communication. The base abstraction for other types of channels +/// A bidirectional channel that enables two-way communication between network endpoints. pub struct Channel { + /// The receive half of the channel. recv: ChannelRecvHalf, + /// The send half of the channel. send: ChannelSendHalf, } +/// The receive half of a bidirectional channel. pub struct ChannelRecvHalf { + /// The receiver stream for the channel. receiver: ChannelRecvHalfReceiver, + /// A flag indicating whether the receive half has been halted. recv_halt: Arc, + /// The inner channel for the receive half. tx: Option>>, } +/// Type alias for a receiver stream. type ChannelRecvHalfReceiver = SyncWrapper, anyhow::Error>> + Send>>>; -//impl Unpin for ChannelRecvHalf {} - +/// The send half of a bidirectional channel. pub struct ChannelSendHalf { + /// A flag indicating whether the receive half has been halted. recv_halt: Arc, + /// The inner channel for the send half. tx: Option>>, + /// A phantom data marker for the type `T`. _pd: PhantomData, } impl ChannelSendHalf { + /// Sends an item over the channel. pub async fn send_item(&self, t: T) -> Result<(), anyhow::Error> { if self.recv_halt.load(Ordering::Relaxed) { Err(anyhow::Error::msg( @@ -58,17 +124,20 @@ impl ChannelSendHalf { } } + /// Gets a reference to the inner channel. fn get_chan(&self) -> &InnerChannel { self.tx.as_ref().unwrap() } } impl ChannelRecvHalf { + /// Receives an item from the channel. pub async fn recv(&mut self) -> Option> { let packet = Pin::new(&mut self.receiver).get_pin_mut().next().await?; Some(self.process_packet(packet)) } + /// Processes a packet received from the channel. fn process_packet( &mut self, packet: Result, anyhow::Error>, @@ -85,6 +154,7 @@ impl ChannelRecvHalf { } impl Channel { + /// Creates a new channel. pub fn create(conn: &S) -> ChannelLoader { ChannelLoader { inner: Box::pin( @@ -94,14 +164,17 @@ impl Channel { } } + /// Sends an item over the channel. pub async fn send_item(&self, t: T) -> Result<(), anyhow::Error> { self.send.send_item(t).await } + /// Receives an item from the channel. pub async fn recv(&mut self) -> Option> { self.recv.recv().await } + /// Creates a new internal channel. fn new_internal(chan: InnerChannel) -> Self { let chan = Arc::new(chan); let chan_stream = chan.clone(); @@ -135,6 +208,7 @@ impl Channel { } } + /// Splits the channel into send and receive halves. pub fn split(self) -> (ChannelSendHalf, ChannelRecvHalf) { (self.send, self.recv) } @@ -207,6 +281,7 @@ impl Drop for ChannelRecvHalf { } } +/// A loader for a channel. pub struct ChannelLoader<'a, T: NetObject, S: Subscribable + 'static> { inner: ScopedFutureResult<'a, Channel>, } diff --git a/netbeam/src/sync/mod.rs b/netbeam/src/sync/mod.rs index bf42ef50e..1226ac408 100644 --- a/netbeam/src/sync/mod.rs +++ b/netbeam/src/sync/mod.rs @@ -1,3 +1,35 @@ +/*! + * # Netbeam Synchronization Module + * + * Core synchronization primitives and networking components for the Netbeam framework. + * This module provides the foundation for building reliable, bidirectional network + * communication channels with advanced synchronization capabilities. + * + * ## Features + * - Network endpoint management and addressing + * - Bidirectional channels with callback support + * - Request-response tracking and correlation + * - Network application synchronization + * - Symmetric conversation tracking + * - Reliable ordered streaming + * + * ## Module Structure + * - `operations/`: Network operation implementations (select, join) + * - `primitives/`: Core synchronization primitives (mutex, rwlock) + * - `channel/`: Channel implementations for network communication + * - `subscription/`: Subscription and event handling + * + * ## Important Notes + * - All network operations are async/await compatible + * - Implements reliable ordered streaming by default + * - Thread-safe with atomic operations + * - Supports symmetric conversations across nodes + * + * ## Related Components + * - `reliable_conn/`: Reliable connection handling + * - `proto/`: Protocol implementation details + */ + use serde::{Deserialize, Serialize}; pub mod operations; diff --git a/netbeam/src/sync/network_application.rs b/netbeam/src/sync/network_application.rs index 726e9a97c..0b0ab03b5 100644 --- a/netbeam/src/sync/network_application.rs +++ b/netbeam/src/sync/network_application.rs @@ -1,3 +1,42 @@ +//! # Network Application Core +//! +//! Core implementation of network applications in netbeam, providing high-level +//! abstractions for synchronized network operations and communication primitives. +//! +//! ## Features +//! +//! - **Synchronization Primitives**: +//! - Mutex implementation +//! - RwLock implementation +//! - Channel creation +//! - Operation coordination +//! +//! - **Network Operations**: +//! - Select operations +//! - Join operations +//! - Try-join operations +//! - Synchronized execution +//! +//! - **Communication Channels**: +//! - Bidirectional channels +//! - Action channels +//! - Pre/post synchronization +//! - Connection multiplexing +//! +//! ## Important Notes +//! +//! - Implements async/await patterns +//! - Ensures operation synchronization +//! - Handles connection multiplexing +//! - Manages node coordination +//! +//! ## Related Components +//! +//! - [`MultiplexedConn`]: Connection multiplexing +//! - [`NetMutex`]: Network mutex implementation +//! - [`NetRwLock`]: Network read-write lock +//! - [`bi_channel`]: Bidirectional channels + use std::collections::HashMap; use std::future::Future; use std::pin::Pin; @@ -321,7 +360,7 @@ impl<'a> PostActionSync<'a> { } } -impl<'a> Future for PostActionSync<'a> { +impl Future for PostActionSync<'_> { type Output = Result<(), anyhow::Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/netbeam/src/sync/network_endpoint.rs b/netbeam/src/sync/network_endpoint.rs index 5b14d9407..49f357379 100644 --- a/netbeam/src/sync/network_endpoint.rs +++ b/netbeam/src/sync/network_endpoint.rs @@ -1,3 +1,42 @@ +//! # Network Endpoint Implementation +//! +//! Provides a network endpoint abstraction that combines network application +//! functionality with socket address management for peer-to-peer communication. +//! +//! ## Features +//! +//! - **Address Management**: +//! - Local address handling +//! - Peer address handling +//! - Address resolution +//! - Socket management +//! +//! - **Network Integration**: +//! - Application binding +//! - Connection registration +//! - Node type awareness +//! - Connection tracking +//! +//! - **Connection Management**: +//! - Connection establishment +//! - Address resolution +//! - Connection state +//! - Role determination +//! +//! ## Important Notes +//! +//! - Implements ConnAddr trait +//! - Manages socket addresses +//! - Tracks connection roles +//! - Handles address resolution +//! +//! ## Related Components +//! +//! - [`NetworkApplication`]: Core application functionality +//! - [`ConnAddr`]: Address management interface +//! - [`Subscribable`]: Subscription management +//! - [`RelativeNodeType`]: Node role management + use crate::reliable_conn::{ConnAddr, ReliableOrderedConnectionToTarget}; use crate::sync::network_application::NetworkApplication; use crate::sync::subscription::Subscribable; diff --git a/netbeam/src/sync/operations/net_join.rs b/netbeam/src/sync/operations/net_join.rs index 728123a50..73686204e 100644 --- a/netbeam/src/sync/operations/net_join.rs +++ b/netbeam/src/sync/operations/net_join.rs @@ -1,3 +1,45 @@ +/*! + * # Network Join Operation + * + * Implements a network-aware join operation that synchronizes futures across two network + * endpoints. Similar to `futures::join`, but operates over a network connection. + * + * ## Features + * - Synchronizes futures between two network endpoints + * - Returns when both endpoints complete + * - Type-safe with generic value types + * - Network-aware relative node types + * + * ## Usage Example + * ```rust + * use netbeam::sync::operations::net_join::NetJoin; + * use netbeam::sync::RelativeNodeType; + * use netbeam::sync::subscription::Subscribable; + * use anyhow::Result; + * + * async fn example(connection: &S) -> Result<()> { + * // Create a join operation + * let join = NetJoin::new( + * connection, + * RelativeNodeType::Initiator, + * async { 42 } + * ); + * + * // Wait for both endpoints + * let result = join.await?; + * Ok(()) + * } + * ``` + * + * ## Important Notes + * - Both endpoints must complete + * - Uses multiplexed connections + * + * ## Related Components + * - `net_try_join.rs`: Try-join variant that handles errors + * - `net_select.rs`: Select operation for multiple futures + */ + use crate::multiplex::MultiplexedConnKey; use crate::reliable_conn::ReliableOrderedStreamToTarget; use crate::sync::operations::net_try_join::NetTryJoin; diff --git a/netbeam/src/sync/operations/net_select.rs b/netbeam/src/sync/operations/net_select.rs index 734dd0ed3..7c233cef9 100644 --- a/netbeam/src/sync/operations/net_select.rs +++ b/netbeam/src/sync/operations/net_select.rs @@ -1,3 +1,48 @@ +/*! + * # Network Select Operation + * + * Implements a network-aware select operation that races futures across two network + * endpoints. Similar to `futures::select`, but operates over a network connection + * with built-in conflict resolution. + * + * ## Features + * - Races futures between two network endpoints + * - First endpoint to complete wins + * - Built-in conflict resolution + * - Type-safe with generic result type + * - Network-aware relative node types + * + * ## Usage Example + * ```rust + * use netbeam::sync::operations::net_select::NetSelect; + * use netbeam::sync::RelativeNodeType; + * use netbeam::sync::subscription::Subscribable; + * use anyhow::Result; + * + * async fn example(connection: &S) -> Result<()> { + * // Create a select operation + * let select = NetSelect::new( + * connection, + * RelativeNodeType::Initiator, + * async { Ok::<_, anyhow::Error>(42) } + * ); + * + * // Wait for first endpoint to complete + * let result = select.await?; + * Ok(()) + * } + * ``` + * + * ## Important Notes + * - First endpoint to complete wins + * - Includes conflict resolution + * - Uses multiplexed connections + * + * ## Related Components + * - `net_select_ok.rs`: Select operation for fallible futures + * - `net_join.rs`: Join operation for synchronization + */ + use crate::multiplex::MultiplexedConnKey; use crate::reliable_conn::ReliableOrderedStreamToTarget; use crate::sync::operations::net_select_ok::NetSelectOk; diff --git a/netbeam/src/sync/operations/net_select_ok.rs b/netbeam/src/sync/operations/net_select_ok.rs index 0e5ec219f..56db31fee 100644 --- a/netbeam/src/sync/operations/net_select_ok.rs +++ b/netbeam/src/sync/operations/net_select_ok.rs @@ -1,3 +1,52 @@ +/*! + * # Network Select-Ok Operation + * + * Implements a network-aware select operation that races fallible futures across two + * network endpoints. Similar to `futures::select`, but operates over a network connection + * with built-in conflict resolution and error handling. + * + * ## Features + * - Races fallible futures between network endpoints + * - First endpoint to complete successfully wins + * - Built-in conflict resolution + * - Error handling with Result type + * - State synchronization between nodes + * - Network-aware relative node types + * + * ## Usage Example + * ```rust + * use netbeam::sync::operations::net_select_ok::NetSelectOk; + * use netbeam::sync::RelativeNodeType; + * use netbeam::sync::subscription::Subscribable; + * use anyhow::Result; + * + * async fn example(connection: &S) -> Result<()> { + * // Create a select-ok operation + * let select = NetSelectOk::new( + * connection, + * RelativeNodeType::Initiator, + * async { Ok::<_, anyhow::Error>(42) } + * ); + * + * // Wait for first successful completion + * let result = select.await?; + * println!("Got result: {:?}", result); + * Ok(()) + * } + * ``` + * + * ## Important Notes + * - First successful endpoint wins + * - Handles errors with Result type + * - State is synchronized between nodes + * - Includes conflict resolution + * - Uses multiplexed connections + * + * ## Related Components + * - `net_select.rs`: Basic select operation without error handling + * - `net_try_join.rs`: Try-join operation for synchronization + */ + use crate::multiplex::MultiplexedConnKey; use crate::reliable_conn::{ReliableOrderedStreamToTarget, ReliableOrderedStreamToTargetExt}; use crate::sync::subscription::Subscribable; diff --git a/netbeam/src/sync/operations/net_try_join.rs b/netbeam/src/sync/operations/net_try_join.rs index 0953f643a..8f0f3d57c 100644 --- a/netbeam/src/sync/operations/net_try_join.rs +++ b/netbeam/src/sync/operations/net_try_join.rs @@ -1,3 +1,51 @@ +/*! + * # Network Try-Join Operation + * + * Implements a network-aware try-join operation that synchronizes fallible futures + * across two network endpoints. Similar to `futures::try_join`, but operates over + * a network connection. + * + * ## Features + * - Synchronizes fallible futures between network endpoints + * - Returns when both endpoints complete successfully + * - Early termination on first error + * - Type-safe with generic value and error types + * - State synchronization between nodes + * - Network-aware relative node types + * + * ## Usage Example + * ```rust + * use netbeam::sync::operations::net_try_join::NetTryJoin; + * use netbeam::sync::RelativeNodeType; + * use netbeam::sync::subscription::Subscribable; + * use anyhow::Result; + * + * async fn example(connection: &S) -> Result<()> { + * // Create a try-join operation + * let join = NetTryJoin::new( + * connection, + * RelativeNodeType::Initiator, + * async { Ok::<_, anyhow::Error>(42) } + * ); + * + * // Wait for both endpoints + * let result = join.await?; + * println!("Got result: {:?}", result); + * Ok(()) + * } + * ``` + * + * ## Important Notes + * - Both endpoints must complete successfully + * - Handles errors with Result type + * - State is synchronized between nodes + * - Uses multiplexed connections + * + * ## Related Components + * - `net_join.rs`: Basic join operation without error handling + * - `net_select.rs`: Select operation for multiple futures + */ + use crate::multiplex::MultiplexedConnKey; use crate::reliable_conn::{ReliableOrderedStreamToTarget, ReliableOrderedStreamToTargetExt}; use crate::sync::subscription::{Subscribable, SubscriptionBiStream}; diff --git a/netbeam/src/sync/primitives/mod.rs b/netbeam/src/sync/primitives/mod.rs index 863dcbc74..174457eea 100644 --- a/netbeam/src/sync/primitives/mod.rs +++ b/netbeam/src/sync/primitives/mod.rs @@ -1,3 +1,22 @@ +//! Network Synchronization Primitives Module +//! +//! This module provides distributed synchronization primitives that work across network boundaries. +//! These primitives enable synchronized access to shared state between different network endpoints. +//! +//! # Features +//! - Network-aware mutex for exclusive access across endpoints +//! - Network-aware read-write lock for shared/exclusive access across endpoints +//! - Trait definitions for network-compatible objects +//! +//! # Important Notes +//! - All primitives require objects to implement the `NetObject` trait +//! - Objects must be serializable, deserializable, and thread-safe +//! - Network operations may fail due to connection issues, so error handling is essential +//! +//! # Related Components +//! - [`net_mutex`] - Distributed mutual exclusion +//! - [`net_rwlock`] - Distributed read-write locking + use serde::de::DeserializeOwned; use serde::Serialize; use std::fmt::Debug; diff --git a/netbeam/src/sync/primitives/net_mutex.rs b/netbeam/src/sync/primitives/net_mutex.rs index 38cc6fdc2..cada2e507 100644 --- a/netbeam/src/sync/primitives/net_mutex.rs +++ b/netbeam/src/sync/primitives/net_mutex.rs @@ -1,3 +1,50 @@ +/*! + * # Network Mutex + * + * A distributed mutex implementation that provides synchronized access to shared + * state across network endpoints. Similar to std::sync::Mutex, but operates over + * a network connection. + * + * ## Features + * - Distributed mutual exclusion + * - Network-aware locking mechanism + * - Automatic lock release on drop + * - State synchronization between nodes + * - Deadlock prevention with timeouts + * - Support for passive background handlers + * + * ## Usage Example + * ```rust + * use netbeam::sync::primitives::net_mutex::NetMutex; + * use netbeam::sync::subscription::Subscribable; + * use anyhow::Result; + * + * async fn example(connection: &S) -> Result<()> { + * // Create a network-aware mutex + * let mutex = NetMutex::create(connection, Some(0)).await?; + * + * // Acquire the lock + * let mut guard = mutex.lock().await?; + * + * // Modify the protected data + * *guard = 42; + * + * Ok(()) + * } + * ``` + * + * ## Important Notes + * - Lock acquisition is asynchronous + * - State is synchronized on lock release + * - Implements deadlock prevention + * - Uses multiplexed connections + * - Background handlers manage state + * + * ## Related Components + * - `net_rwlock.rs`: Network-aware read-write lock + * - `subscription.rs`: Subscription system for network events + */ + use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::sync::Arc; diff --git a/netbeam/src/sync/primitives/net_rwlock.rs b/netbeam/src/sync/primitives/net_rwlock.rs index d8925cf05..5f72cabbf 100644 --- a/netbeam/src/sync/primitives/net_rwlock.rs +++ b/netbeam/src/sync/primitives/net_rwlock.rs @@ -1,5 +1,58 @@ +/*! + * # Network Read-Write Lock + * + * A distributed read-write lock implementation that provides synchronized access to + * shared state across network endpoints. Similar to std::sync::RwLock, but operates + * over a network connection with support for multiple readers and exclusive writers. + * + * ## Features + * - Distributed read-write locking + * - Multiple concurrent readers + * - Exclusive writer access + * - Network-aware locking mechanism + * - Automatic lock release on drop + * - State synchronization between nodes + * - Lock type upgrades and downgrades + * - Background state management + * + * ## Usage Example + * ```rust + * use netbeam::sync::primitives::net_rwlock::NetRwLock; + * use netbeam::sync::subscription::Subscribable; + * use anyhow::Result; + * + * async fn example(connection: &S) -> Result<()> { + * // Create a network-aware RwLock + * let rwlock = NetRwLock::create(connection, Some(0)).await?; + * + * // Acquire read lock + * let read_guard = rwlock.read().await?; + * println!("Read value: {}", *read_guard); + * drop(read_guard); + * + * // Acquire write lock + * let mut write_guard = rwlock.write().await?; + * *write_guard = 42; + * + * Ok(()) + * } + * ``` + * + * ## Important Notes + * - Lock acquisition is asynchronous + * - Multiple readers can access simultaneously + * - Writers have exclusive access + * - State is synchronized on write release + * - Uses multiplexed connections + * - Background handlers manage state + * + * ## Related Components + * - `net_mutex.rs`: Network-aware mutex + * - `subscription.rs`: Subscription system for network events + */ + use std::future::Future; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; @@ -9,8 +62,6 @@ use citadel_io::tokio::sync::{Mutex, OwnedMutexGuard}; use crate::reliable_conn::ReliableOrderedStreamToTargetExt; use crate::sync::primitives::net_mutex::{sync_establish_init, InnerChannel}; -use crate::sync::primitives::net_rwlock::read::{acquire_read, RwLockReadAcquirer}; -use crate::sync::primitives::net_rwlock::write::{acquire_write, RwLockWriteAcquirer}; use crate::sync::primitives::NetObject; use crate::sync::subscription::Subscribable; use crate::sync::subscription::SubscriptionBiStream; @@ -23,6 +74,8 @@ type InnerState = (T, Sender<()>); type OwnedLocalReadLock = Arc>>; type OwnedLocalWriteLock = OwnedMutexGuard>; +/// A distributed read-write lock implementation that provides synchronized access to +/// shared state across network endpoints. pub struct NetRwLock { // Used to hold a lock when either local or remote is engaged. We use a Mutex here over an RwLock because // if local tries to read, then we get a mutex guard with an Arc wrapped around it to allow clonable read access. @@ -36,10 +89,12 @@ pub struct NetRwLock { } impl NetRwLock { + /// Returns the node type of the underlying channel. pub fn node_type(&self) -> RelativeNodeType { self.channel.node_type() } + /// Creates a new network rwlock with the given initial value. pub async fn new_internal( channel: InnerChannel, initial_value: T, @@ -79,24 +134,28 @@ impl NetRwLock { Ok(this) } + /// Creates a new network rwlock loader with the given connection and initial value. pub fn create(conn: &S, t: Option) -> NetRwLockLoader { NetRwLockLoader { future: Box::pin(sync_establish_init(conn, t, Self::new_internal)), } } + /// Acquires a read lock on the network rwlock. pub fn read(&self) -> RwLockReadAcquirer { RwLockReadAcquirer { future: Box::pin(acquire_read(self)), } } + /// Acquires a write lock on the network rwlock. pub fn write(&self) -> RwLockWriteAcquirer { RwLockWriteAcquirer { future: Box::pin(acquire_write(self)), } } + /// Returns the number of active local reads. pub fn active_local_reads(&self) -> usize { self.local_active_read_lock .read() @@ -119,6 +178,7 @@ impl Drop for NetRwLock { } } +/// A network rwlock loader that creates a new network rwlock with the given connection and initial value. pub struct NetRwLockLoader<'a, T: NetObject, S: Subscribable + 'static> { future: ScopedFutureResult<'a, NetRwLock>, } @@ -131,283 +191,236 @@ impl Future for NetRwLockLoader<'_, T, } } -pub(crate) mod read { - use std::ops::Deref; - use std::pin::Pin; - use std::sync::Arc; - use std::task::{Context, Poll}; - - use crate::ScopedFutureResult; - use futures::Future; - - use crate::sync::primitives::net_mutex::InnerChannel; - use crate::sync::primitives::net_rwlock::drop::NetRwLockEitherGuardDropCode; - use crate::sync::primitives::net_rwlock::{ - acquire_lock, LocalLockHolder, LockType, NetRwLock, OwnedLocalReadLock, - }; - use crate::sync::primitives::NetObject; - use crate::sync::subscription::Subscribable; - use crate::sync::subscription::SubscriptionBiStream; - - pub struct RwLockReadAcquirer<'a, T: NetObject + 'static, S: Subscribable + 'static> { - pub(crate) future: ScopedFutureResult<'a, NetRwLockReadGuard>, - } +/// A read acquirer that acquires a read lock on the network rwlock. +pub struct RwLockReadAcquirer<'a, T: NetObject + 'static, S: Subscribable + 'static> { + pub(crate) future: ScopedFutureResult<'a, NetRwLockReadGuard>, +} - pub struct NetRwLockReadGuard { - inner: Option>, - conn: Arc>, - shared_store: Arc>>>, - } +/// A read guard that provides read access to the network rwlock. +pub struct NetRwLockReadGuard { + inner: Option>, + conn: Arc>, + shared_store: Arc>>>, +} - impl Future for RwLockReadAcquirer<'_, T, S> { - type Output = Result, anyhow::Error>; +impl Future for RwLockReadAcquirer<'_, T, S> { + type Output = Result, anyhow::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.future.as_mut().poll(cx) - } + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.future.as_mut().poll(cx) } +} - pub(super) async fn acquire_read( - rwlock: &NetRwLock, - ) -> Result, anyhow::Error> { - log::trace!(target: "citadel", "Running acquire_read for {:?}", rwlock.channel.node_type()); +/// Acquires a read lock on the network rwlock. +pub(super) async fn acquire_read( + rwlock: &NetRwLock, +) -> Result, anyhow::Error> { + log::trace!(target: "citadel", "Running acquire_read for {:?}", rwlock.channel.node_type()); + { + let pre_loaded = rwlock.local_active_read_lock.read(); + log::trace!(target: "citadel", "Running acquire_read for {:?} | pre_loaded ? {}", rwlock.channel.node_type(), pre_loaded.is_some()); + // if there is more than one strong reference, we can return early + if pre_loaded + .as_ref() + .map(|r| Arc::strong_count(r) > 1) + .unwrap_or(false) { - let pre_loaded = rwlock.local_active_read_lock.read(); - log::trace!(target: "citadel", "Running acquire_read for {:?} | pre_loaded ? {}", rwlock.channel.node_type(), pre_loaded.is_some()); - // if there is more than one strong reference, we can return early - if pre_loaded - .as_ref() - .map(|r| Arc::strong_count(r) > 1) - .unwrap_or(false) - { - return Ok(NetRwLockReadGuard { - inner: Some(LocalLockHolder::Read(pre_loaded.clone(), false)), - conn: rwlock.channel.clone(), - shared_store: rwlock.local_active_read_lock.clone(), - }); - } + return Ok(NetRwLockReadGuard { + inner: Some(LocalLockHolder::Read(pre_loaded.clone(), false)), + conn: rwlock.channel.clone(), + shared_store: rwlock.local_active_read_lock.clone(), + }); } - - // no read locks exist currently. Acquire a local shared read lock - acquire_lock(LockType::Read, rwlock, |inner| NetRwLockReadGuard { - inner: Some(inner), - conn: rwlock.channel.clone(), - shared_store: rwlock.local_active_read_lock.clone(), - }) - .await } - impl Deref for NetRwLockReadGuard { - type Target = T; + // no read locks exist currently. Acquire a local shared read lock + acquire_lock(LockType::Read, rwlock, |inner| NetRwLockReadGuard { + inner: Some(inner), + conn: rwlock.channel.clone(), + shared_store: rwlock.local_active_read_lock.clone(), + }) + .await +} - fn deref(&self) -> &Self::Target { - self.inner.as_ref().unwrap().deref() - } +impl Deref for NetRwLockReadGuard { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner.as_ref().unwrap().deref() } +} - impl Drop for NetRwLockReadGuard { - fn drop(&mut self) { - let this = self.inner.take().unwrap(); - // if there are two left, then this is the final rwlock active for the user. The other one is the lock stored inside the rwlock - if this.arc_strong_count().unwrap() == 2 { - log::trace!(target: "citadel", "CALLING read drop code on {:?}", self.conn.node_type()); - // immediately remove the shared store to prevent further acquires - *self.shared_store.write() = None; // 1 arc left (this) - - if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { - let future = NetRwLockEitherGuardDropCode::new::(self.conn.clone(), this); - rt.spawn(future); - } +impl Drop for NetRwLockReadGuard { + fn drop(&mut self) { + let this = self.inner.take().unwrap(); + // if there are two left, then this is the final rwlock active for the user. The other one is the lock stored inside the rwlock + if this.arc_strong_count().unwrap() == 2 { + log::trace!(target: "citadel", "CALLING read drop code on {:?}", self.conn.node_type()); + // immediately remove the shared store to prevent further acquires + *self.shared_store.write() = None; // 1 arc left (this) + + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { + let future = NetRwLockEitherGuardDropCode::new::(self.conn.clone(), this); + rt.spawn(future); } } } } -pub(crate) mod write { - use std::ops::{Deref, DerefMut}; - use std::pin::Pin; - use std::sync::Arc; - use std::task::{Context, Poll}; +/// A write acquirer that acquires a write lock on the network rwlock. +pub struct RwLockWriteAcquirer<'a, T: NetObject + 'static, S: Subscribable + 'static> { + pub(crate) future: ScopedFutureResult<'a, NetRwLockWriteGuard>, +} - use crate::ScopedFutureResult; - use futures::Future; +/// A write guard that provides write access to the network rwlock. +pub struct NetRwLockWriteGuard { + inner: Option>, + conn: Arc>, +} - use crate::sync::primitives::net_mutex::InnerChannel; - use crate::sync::primitives::net_rwlock::drop::NetRwLockEitherGuardDropCode; - use crate::sync::primitives::net_rwlock::{acquire_lock, LocalLockHolder, LockType, NetRwLock}; - use crate::sync::primitives::NetObject; - use crate::sync::subscription::Subscribable; +impl Future for RwLockWriteAcquirer<'_, T, S> { + type Output = Result, anyhow::Error>; - pub struct RwLockWriteAcquirer<'a, T: NetObject + 'static, S: Subscribable + 'static> { - pub(crate) future: ScopedFutureResult<'a, NetRwLockWriteGuard>, + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.future.as_mut().poll(cx) } +} - pub struct NetRwLockWriteGuard { - inner: Option>, - conn: Arc>, - } +/// Acquires a write lock on the network rwlock. +pub(super) async fn acquire_write( + rwlock: &NetRwLock, +) -> Result, anyhow::Error> { + acquire_lock(LockType::Write, rwlock, |inner| NetRwLockWriteGuard { + inner: Some(inner), + conn: rwlock.channel.clone(), + }) + .await +} - impl Future for RwLockWriteAcquirer<'_, T, S> { - type Output = Result, anyhow::Error>; +impl Deref for NetRwLockWriteGuard { + type Target = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.future.as_mut().poll(cx) - } + fn deref(&self) -> &Self::Target { + self.inner.as_ref().unwrap() } +} - pub(super) async fn acquire_write( - rwlock: &NetRwLock, - ) -> Result, anyhow::Error> { - acquire_lock(LockType::Write, rwlock, |inner| NetRwLockWriteGuard { - inner: Some(inner), - conn: rwlock.channel.clone(), - }) - .await +impl DerefMut for NetRwLockWriteGuard { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner.as_mut().unwrap().assert_write_mut().0 } +} - impl Deref for NetRwLockWriteGuard { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.as_ref().unwrap() +impl Drop for NetRwLockWriteGuard { + fn drop(&mut self) { + let this = self.inner.take().unwrap(); + if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { + let future = NetRwLockEitherGuardDropCode::new::(self.conn.clone(), this); + rt.spawn(future); } } +} - impl DerefMut for NetRwLockWriteGuard { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner.as_mut().unwrap().assert_write_mut().0 - } - } +/// A drop code that releases the lock with the adjacent endpoint, updating the value too for the adjacent node if a write lock was dropped. +pub(super) struct NetRwLockEitherGuardDropCode { + future: Pin> + Send>>, +} - impl Drop for NetRwLockWriteGuard { - fn drop(&mut self) { - let this = self.inner.take().unwrap(); - if let Ok(rt) = citadel_io::tokio::runtime::Handle::try_current() { - let future = NetRwLockEitherGuardDropCode::new::(self.conn.clone(), this); - rt.spawn(future); - } +impl NetRwLockEitherGuardDropCode { + pub(super) fn new( + conn: Arc>, + guard: LocalLockHolder, + ) -> Self { + Self { + future: Box::pin(net_rwlock_guard_drop_code::(conn, guard)), } } } -mod drop { - use crate::reliable_conn::ReliableOrderedStreamToTargetExt; - use crate::sync::primitives::net_rwlock::LocalLockHolder; - use crate::sync::primitives::net_rwlock::{yield_lock, InnerChannel, LockType, UpdatePacket}; - use crate::sync::primitives::NetObject; - use crate::sync::subscription::Subscribable; - use crate::sync::subscription::SubscriptionBiStream; - use std::future::Future; - use std::ops::Deref; - use std::pin::Pin; - use std::sync::Arc; - use std::task::{Context, Poll}; +impl Future for NetRwLockEitherGuardDropCode { + type Output = Result<(), anyhow::Error>; - /// Releases the lock with the adjacent endpoint, updating the value too for the adjacent node if a write lock was dropped - /// This should only be called for the final guard type - pub(super) struct NetRwLockEitherGuardDropCode { - future: Pin> + Send>>, + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.future.as_mut().poll(cx) } +} - impl NetRwLockEitherGuardDropCode { - pub(super) fn new( - conn: Arc>, - guard: LocalLockHolder, - ) -> Self { - Self { - future: Box::pin(net_rwlock_guard_drop_code::(conn, guard)), - } +/// Releases the lock with the adjacent endpoint, updating the value too for the adjacent node if a write lock was dropped. +async fn net_rwlock_guard_drop_code( + conn: Arc>, + lock: LocalLockHolder, +) -> Result<(), anyhow::Error> { + log::trace!(target: "citadel", "[NetRwLock] Drop code initialized for {:?}...", conn.node_type()); + match &lock { + LocalLockHolder::Read(..) => { + conn.send_serialized(UpdatePacket::ReleasedRead).await?; } - } - - impl Future for NetRwLockEitherGuardDropCode { - type Output = Result<(), anyhow::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.future.as_mut().poll(cx) + LocalLockHolder::Write(_guard, ..) => { + conn.send_serialized(UpdatePacket::ReleasedWrite(bincode::serialize( + &lock.deref(), + )?)) + .await?; } } - async fn net_rwlock_guard_drop_code( - conn: Arc>, - lock: LocalLockHolder, - ) -> Result<(), anyhow::Error> { - log::trace!(target: "citadel", "[NetRwLock] Drop code initialized for {:?}...", conn.node_type()); - match &lock { - LocalLockHolder::Read(..) => { - conn.send_serialized(UpdatePacket::ReleasedRead).await?; - } + let mut adjacent_trying_to_acquire = None; - LocalLockHolder::Write(_guard, ..) => { - conn.send_serialized(UpdatePacket::ReleasedWrite(bincode::serialize( - &lock.deref(), - )?)) - .await?; - } - } - - let mut adjacent_trying_to_acquire = None; + loop { + let packet = conn.recv_serialized::().await?; + log::trace!(target: "citadel", "[NetRwLock] [Drop Code] RECV {:?} on {:?}", &packet, conn.node_type()); - loop { - let packet = conn.recv_serialized::().await?; - log::trace!(target: "citadel", "[NetRwLock] [Drop Code] RECV {:?} on {:?}", &packet, conn.node_type()); + match packet { + UpdatePacket::ReleasedWrite(_new_value) => { + //unreachable!("Adjacent signalled a release of the write lock, yet, local has not yet dropped read/write") + } - match packet { - UpdatePacket::ReleasedWrite(_new_value) => { - //unreachable!("Adjacent signalled a release of the write lock, yet, local has not yet dropped read/write") + UpdatePacket::ReleasedRead => { + if let LocalLockHolder::Read(..) = &lock { + log::trace!(target: "citadel", "Yield:: Releasing Read lock"); + conn.send_serialized(UpdatePacket::ReleasedVerified(LockType::Read)) + .await?; } + } - UpdatePacket::ReleasedRead => { - if let LocalLockHolder::Read(..) = &lock { - log::trace!(target: "citadel", "Yield:: Releasing Read lock"); - conn.send_serialized(UpdatePacket::ReleasedVerified(LockType::Read)) - .await?; - } - } + UpdatePacket::ReleasedVerified(_lock_type) => { + log::trace!(target: "citadel", "[NetRwLock] [Drop Code] Release has been verified for {:?}. Adjacent node updated; will drop local lock", conn.node_type()); - UpdatePacket::ReleasedVerified(_lock_type) => { - log::trace!(target: "citadel", "[NetRwLock] [Drop Code] Release has been verified for {:?}. Adjacent node updated; will drop local lock", conn.node_type()); + if let Some(_lock_type) = adjacent_trying_to_acquire { + log::trace!(target: "citadel", "[NetRwLock] [Drop Code] Will not yet drop though, since remote requested lock access since dropping ..."); + // all we have to do is hold the lock here. The underlying lock uses a mutex, so we are safe from parallel/concurrent local calls until after the yield is complete + log::trace!(target: "citadel", "[KTX] {:?} yield_lock", conn.node_type()); + return yield_lock::(&conn, lock).await.map(|_| ()); + } - if let Some(_lock_type) = adjacent_trying_to_acquire { - log::trace!(target: "citadel", "[NetRwLock] [Drop Code] Will not yet drop though, since remote requested lock access since dropping ..."); - // all we have to do is hold the lock here. The underlying lock uses a mutex, so we are safe from parallel/concurrent local calls until after the yield is complete - log::trace!(target: "citadel", "[KTX] {:?} yield_lock", conn.node_type()); - return yield_lock::(&conn, lock).await.map(|_| ()); - } + return Ok(()); + } - return Ok(()); - } + UpdatePacket::TryAcquire(_, lock_type) => { + adjacent_trying_to_acquire = Some(lock_type); + // once the release is confirmed, we will yield the lock back to remote + // However, if we are trying to drop a read locally, and they are trying to acquire a read, we can yield a read + // no need to yield a lock either since local will need to ask again + } - UpdatePacket::TryAcquire(_, lock_type) => { - adjacent_trying_to_acquire = Some(lock_type); - // once the release is confirmed, we will yield the lock back to remote - // However, if we are trying to drop a read locally, and they are trying to acquire a read, we can yield a read - // no need to yield a lock either since local will need to ask again - } + UpdatePacket::Halt => return Err(anyhow::Error::msg("Halted from background")), - _ => {} + UpdatePacket::LockAcquired(_) => { + // this is received after sending the Released packet. We do nothing here } } } } -#[derive(Serialize, Deserialize, Debug)] -enum UpdatePacket { - TryAcquire(i64, LockType), - ReleasedWrite(Vec), - ReleasedRead, - LockAcquired(LockType), - Halt, - ReleasedVerified(LockType), -} - +/// A lock type that represents either a read or write lock. #[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq)] enum LockType { Read, Write, } -enum LocalLockHolder { +/// A local lock holder that represents either a read or write lock. +pub(super) enum LocalLockHolder { Write(Option>, bool), Read(Option>, bool), } @@ -424,12 +437,22 @@ impl Deref for LocalLockHolder { } impl LocalLockHolder { + /// Returns whether the lock holder is from the background. fn is_from_background(&self) -> bool { match self { Self::Write(_, from_bg) | Self::Read(_, from_bg) => *from_bg, } } + /// Returns the lock type of the lock holder. + fn lock_type(&self) -> LockType { + match self { + Self::Write(..) => LockType::Write, + Self::Read(..) => LockType::Read, + } + } + + /// Returns the sender of the lock holder. fn free_lock_and_get_sender(&mut self) -> Sender<()> { match self { Self::Read(r, _) => r.take().unwrap().1.clone(), @@ -437,27 +460,27 @@ impl LocalLockHolder { } } - fn lock_type(&self) -> LockType { + /// Asserts that the lock holder is a write lock and returns a mutable reference to the write lock. + fn assert_write_mut(&mut self) -> &mut OwnedLocalWriteLock { match self { - Self::Write(..) => LockType::Write, - Self::Read(..) => LockType::Read, + Self::Write(val, ..) => val.as_mut().unwrap(), + _ => { + panic!("Asserted write lock, but was a read lock"); + } } } - fn assert_write_and_downgrade(&mut self) { + /// Asserts that the lock holder is a read lock and returns a reference to the read lock. + fn assert_read(&self) -> &OwnedLocalReadLock { match self { - Self::Write(val, from_bg) => { - let new = LocalLockHolder::Read(val.take().map(Arc::new), *from_bg); - *from_bg = true; // stop destructor from being called - *self = new; - } - - Self::Read(..) => { - panic!("Asserted write, but was read") + Self::Read(val, ..) => val.as_ref().unwrap(), + _ => { + panic!("Asserted write lock, but was a read lock"); } } } + /// Upgrades the lock holder to a write lock. fn assert_read_and_upgrade(&mut self) { match self { Self::Read(val, from_bg) => { @@ -475,28 +498,26 @@ impl LocalLockHolder { } } - fn arc_strong_count(&self) -> Option { + /// Downgrades the lock holder to a read lock. + fn assert_write_and_downgrade(&mut self) { match self { - Self::Read(val, ..) => Some(Arc::strong_count(val.as_ref().unwrap())), - Self::Write(..) => None, - } - } + Self::Write(val, from_bg) => { + let new = LocalLockHolder::Read(val.take().map(Arc::new), *from_bg); + *from_bg = true; // stop destructor from being called + *self = new; + } - fn assert_write_mut(&mut self) -> &mut OwnedLocalWriteLock { - match self { - Self::Write(val, ..) => val.as_mut().unwrap(), - _ => { - panic!("Asserted write lock, but was a read lock"); + Self::Read(..) => { + panic!("Asserted write, but was read") } } } - fn assert_read(&self) -> &OwnedLocalReadLock { + /// Returns the strong count of the lock holder. + fn arc_strong_count(&self) -> Option { match self { - Self::Read(val, ..) => val.as_ref().unwrap(), - _ => { - panic!("Asserted write lock, but was a read lock"); - } + Self::Read(val, ..) => Some(Arc::strong_count(val.as_ref().unwrap())), + Self::Write(..) => None, } } } @@ -516,7 +537,7 @@ impl Drop for LocalLockHolder { } } -// the local lock will be dropped after this function, allowing local calls to acquire the lock once again +/// Yields the lock to the adjacent endpoint. async fn yield_lock( channel: &Arc>, mut lock: LocalLockHolder, @@ -581,7 +602,7 @@ async fn yield_lock( } } -/// - background_to_active_tx: only gets sent if the other end is listening +/// A passive background handler that manages the state of the network rwlock. async fn passive_background_handler( channel: Arc>, shared_state: Arc>>, @@ -664,6 +685,7 @@ async fn passive_background_handler( } } +/// Acquires a lock on the network rwlock. async fn acquire_lock( lock_type: LockType, rwlock: &NetRwLock, @@ -839,6 +861,16 @@ where } } +#[derive(Serialize, Deserialize, Debug)] +enum UpdatePacket { + TryAcquire(i64, LockType), + ReleasedWrite(Vec), + ReleasedRead, + LockAcquired(LockType), + Halt, + ReleasedVerified(LockType), +} + #[cfg(test)] mod tests { use std::sync::atomic::{AtomicU64, Ordering}; diff --git a/netbeam/src/sync/subscription.rs b/netbeam/src/sync/subscription.rs index 467e63b20..38aa17812 100644 --- a/netbeam/src/sync/subscription.rs +++ b/netbeam/src/sync/subscription.rs @@ -1,3 +1,42 @@ +//! # Subscription Stream Implementation +//! +//! Provides bidirectional subscription-based streaming capabilities for multiplexed +//! network connections, enabling reliable ordered communication between nodes. +//! +//! ## Features +//! +//! - **Bidirectional Streaming**: +//! - Reliable ordered message delivery +//! - Automatic stream management +//! - Multiplexed connections +//! - Stream identification +//! +//! - **Subscription Management**: +//! - Stream subscription handling +//! - Connection lifecycle management +//! - Node type awareness +//! - Resource cleanup +//! +//! - **Multiplexing Support**: +//! - Stream multiplexing +//! - Connection sharing +//! - Stream isolation +//! - Connection pooling +//! +//! ## Important Notes +//! +//! - Implements async/await patterns +//! - Ensures thread-safety +//! - Handles connection cleanup +//! - Maintains message ordering +//! +//! ## Related Components +//! +//! - [`MultiplexedConn`]: Connection multiplexing +//! - [`ReliableOrderedStreamToTarget`]: Stream reliability +//! - [`network_application`]: Application integration +//! - [`RelativeNodeType`]: Node type management + use crate::multiplex::{MemorySender, MultiplexedConn, MultiplexedConnKey, MultiplexedPacket}; use crate::reliable_conn::ReliableOrderedStreamToTarget; use crate::sync::network_application::{ diff --git a/netbeam/src/sync/sync_start.rs b/netbeam/src/sync/sync_start.rs index 0f9229073..d00c59e38 100644 --- a/netbeam/src/sync/sync_start.rs +++ b/netbeam/src/sync/sync_start.rs @@ -1,3 +1,42 @@ +//! # Network Synchronization Start +//! +//! Implements synchronization primitives for coordinating the start of operations +//! between network nodes, with support for payload exchange during synchronization. +//! +//! ## Features +//! +//! - **Operation Synchronization**: +//! - Two-way synchronization +//! - Payload exchange support +//! - Timing coordination +//! - Node type awareness +//! +//! - **Payload Management**: +//! - Type-safe payload exchange +//! - Serialization support +//! - Error handling +//! - Timing tracking +//! +//! - **Future Integration**: +//! - Async/await support +//! - Future composition +//! - Error propagation +//! - Cancellation handling +//! +//! ## Important Notes +//! +//! - Ensures both nodes start operations simultaneously +//! - Handles network latency compensation +//! - Provides reliable payload delivery +//! - Supports timeout configuration +//! +//! ## Related Components +//! +//! - [`subscription`]: Stream subscription management +//! - [`ReliableOrderedStreamToTarget`]: Reliable streaming +//! - [`TimeTracker`]: Operation timing +//! - [`MultiplexedConnKey`]: Connection identification + use crate::multiplex::MultiplexedConnKey; use crate::reliable_conn::{ReliableOrderedStreamToTarget, ReliableOrderedStreamToTargetExt}; use crate::sync::subscription::Subscribable; diff --git a/netbeam/src/sync/tracked_callback_channel.rs b/netbeam/src/sync/tracked_callback_channel.rs index be6ab9caf..ab586b9ae 100644 --- a/netbeam/src/sync/tracked_callback_channel.rs +++ b/netbeam/src/sync/tracked_callback_channel.rs @@ -1,3 +1,44 @@ +/*! + * # Tracked Callback Channel + * + * An enhanced version of the callback channel that provides tracking and monitoring + * capabilities for request-response patterns. + * + * ## Features + * - All features of the base CallbackChannel + * - Request tracking with unique identifiers + * - Response correlation with original requests + * - Thread-safe response tracking + * - Support for response timeouts + * - Atomic operation tracking + * + * ## Usage Example + * ```rust,no_run + * use netbeam::sync::tracked_callback_channel::TrackedCallbackChannel; + * + * # fn example() { + * // Create a new tracked channel with buffer size 10 + * let (channel, receiver) = TrackedCallbackChannel::::new(10); + * + * // Send with tracked callback + * let result = channel.send("request".to_string()); + * + * // Send without callback + * channel.send_no_callback("notification".to_string()); + * # } + * ``` + * + * ## Related Components + * - `callback_channel.rs`: Base implementation without tracking + * - `bi_channel.rs`: Bidirectional channel implementation + * + * ## Important Notes + * - Each request gets a unique tracking ID + * - Responses are correlated with requests using tracking IDs + * - Thread-safe tracking using atomic operations + * - Memory-efficient response tracking with cleanup + */ + use citadel_io::tokio::sync::mpsc::{Receiver, Sender}; use citadel_io::Mutex; use futures::Stream; @@ -9,11 +50,14 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use std::task::{Context, Poll}; +/// A tracked callback channel that provides request-response tracking and monitoring. pub struct TrackedCallbackChannel { + /// The inner implementation of the tracked callback channel. inner: Arc>, } impl Clone for TrackedCallbackChannel { + /// Clones the tracked callback channel. fn clone(&self) -> Self { Self { inner: self.inner.clone(), @@ -21,13 +65,18 @@ impl Clone for TrackedCallbackChannel { } } +/// An error that can occur when using the tracked callback channel. pub enum TrackedCallbackError { + /// An error occurred while sending a request. SendError(T), + /// An error occurred while receiving a response. RecvError, + /// An internal error occurred. InternalError(&'static str), } impl TrackedCallbackError { + /// Returns the payload of the error, if any. pub fn payload(self) -> Option { match self { Self::SendError(payload) => Some(payload), @@ -36,9 +85,11 @@ impl TrackedCallbackError { } } +/// A constant representing no response. const NO_RESPONSE: u64 = 0; impl Debug for TrackedCallbackError { + /// Formats the error for debugging. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::SendError(_) => { @@ -56,19 +107,28 @@ impl Debug for TrackedCallbackError { } } +/// The inner implementation of the tracked callback channel. struct TrackedCallbackChannelInner { + /// A map of tracking IDs to response senders. map: Mutex>>, + /// The sender for the tracked callback channel. to_channel: Sender>, + /// The atomic ID generator. id: AtomicU64, } +/// A payload for the tracked callback channel. pub struct TrackedCallbackChannelPayload { + /// The tracking ID of the payload. id: u64, + /// The payload data. pub payload: T, + /// A phantom data marker for the response type. _pd: PhantomData, } impl TrackedCallbackChannelPayload { + /// Creates a new payload with the given tracking ID and data. pub fn new(&self, payload: R) -> TrackedCallbackChannelPayload { TrackedCallbackChannelPayload { id: self.id, @@ -77,12 +137,14 @@ impl TrackedCallbackChannelPayload { } } + /// Returns whether the payload expects a response. pub fn expects_response(&self) -> bool { self.id != NO_RESPONSE } } impl TrackedCallbackChannel { + /// Creates a new tracked callback channel with the given buffer size. pub fn new(buffer: usize) -> (Self, CallbackReceiver) { let (to_channel, from_channel) = citadel_io::tokio::sync::mpsc::channel(buffer); ( @@ -99,6 +161,7 @@ impl TrackedCallbackChannel { ) } + /// Sends a request with a tracked callback. pub async fn send(&self, payload: T) -> Result> { let (rx, id) = { let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); @@ -120,6 +183,7 @@ impl TrackedCallbackChannel { rx.await.map_err(|_| TrackedCallbackError::RecvError) } + /// Sends a request without a tracked callback. pub async fn send_no_callback(&self, payload: T) -> Result<(), TrackedCallbackError> { self.inner .to_channel @@ -132,6 +196,7 @@ impl TrackedCallbackChannel { .map_err(|err| TrackedCallbackError::SendError(err.0.payload)) } + /// Tries to reply to a tracked request. pub fn try_reply( &self, payload: TrackedCallbackChannelPayload, @@ -149,13 +214,16 @@ impl TrackedCallbackChannel { } } +/// A receiver for the tracked callback channel. pub struct CallbackReceiver { + /// The inner receiver. inner: Receiver>, } impl Stream for CallbackReceiver { type Item = TrackedCallbackChannelPayload; + /// Polls the receiver for the next item. fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.inner).poll_recv(cx) } diff --git a/netbeam/src/time_tracker.rs b/netbeam/src/time_tracker.rs index bc3da888e..373a605b5 100644 --- a/netbeam/src/time_tracker.rs +++ b/netbeam/src/time_tracker.rs @@ -1,19 +1,69 @@ +//! # Time Tracking Module +//! +//! This module provides precise time tracking utilities for network operations. +//! It uses monotonic system time to ensure consistent and reliable timing +//! measurements, particularly useful for network latency calculations and +//! timing-sensitive operations. +//! +//! ## Features +//! +//! - Nanosecond precision timing +//! - Monotonic time source +//! - Overflow protection +//! - Debug formatting +//! +//! ## Example +//! +//! ```rust +//! use netbeam::time_tracker::TimeTracker; +//! +//! let tracker = TimeTracker::new(); +//! let start_time = tracker.get_global_time_ns(); +//! +//! // Perform some operation +//! +//! let elapsed = tracker.get_global_time_ns() - start_time; +//! println!("Operation took {} ns", elapsed); +//! ``` + use std::fmt::Formatter; +/// A utility for tracking time with nanosecond precision. +/// +/// TimeTracker provides a consistent way to measure time intervals +/// using the system's monotonic clock. It is particularly useful +/// for measuring network latencies and timing-sensitive operations. #[derive(Copy, Clone, Default)] pub struct TimeTracker; impl TimeTracker { + /// Creates a new TimeTracker instance. + /// + /// This is equivalent to calling `Default::default()` as + /// TimeTracker maintains no internal state. pub fn new() -> Self { Default::default() } - // This should work for about a hundred years before modulo'ing around back to zero + /// Returns the current global time in nanoseconds. + /// + /// This method returns the number of nanoseconds since the Unix epoch, + /// modulo i64::MAX to prevent overflow. This provides approximately + /// 100 years of unique timestamps before wrapping around. + /// + /// # Returns + /// + /// Returns the current time in nanoseconds as an i64. + /// The returned value will wrap around after approximately 100 years. pub fn get_global_time_ns(&self) -> i64 { (std::time::UNIX_EPOCH.elapsed().unwrap().as_nanos() % i64::MAX as u128) as i64 } } +/// Debug implementation for TimeTracker. +/// +/// Provides a human-readable format showing the current global time +/// in nanoseconds when the TimeTracker is debug-printed. impl std::fmt::Debug for TimeTracker { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { writeln!( diff --git a/suggestions.ai.json b/suggestions.ai.json new file mode 100644 index 000000000..ef423337c --- /dev/null +++ b/suggestions.ai.json @@ -0,0 +1,2051 @@ +[ + { + "file": "./citadel_sdk/src/builder/mod.rs", + "suggestions": [ + "Add more builder components for common configurations", + "Implement configuration validation helpers", + "Add more usage examples in documentation", + "Consider adding configuration serialization", + "Add configuration migration utilities", + "Consider adding configuration templates" + ] + }, + { + "file": "./citadel_sdk/src/builder/node_builder.rs", + "suggestions": [ + "Add configuration presets for common use cases", + "Implement configuration validation diagnostics", + "Add support for configuration inheritance", + "Consider adding dynamic reconfiguration", + "Implement configuration export/import", + "Add more security configuration options", + "Consider adding configuration versioning", + "Implement configuration testing utilities", + "Add configuration documentation generator", + "Consider adding configuration visualization" + ] + }, + { + "file": "./citadel_sdk/src/remote_ext.rs", + "suggestions": [ + "Add more comprehensive error handling examples", + "Implement retry strategies for network operations", + "Add connection pooling for better resource management", + "Implement rate limiting for file transfers", + "Add progress tracking for file operations", + "Consider adding batch operations for multiple transfers", + "Implement connection migration support", + "Add more examples for group communication", + "Consider adding connection quality metrics", + "Implement connection state persistence" + ] + }, + { + "file": "./citadel_proto/src/inner_arg.rs", + "suggestions": [ + "Add support for pinned references", + "Implement Send/Sync where safe", + "Add more usage examples in documentation", + "Consider adding lifetime validation helpers", + "Add compile-time type checks", + "Consider adding debug assertions", + "Add more ergonomic constructors" + ] + }, + { + "file": "./citadel_proto/src/functional.rs", + "suggestions": [ + "Add Result and Option extension methods", + "Implement function composition utilities", + "Add more tuple operations (e.g., zip, unzip)", + "Consider adding Either type for better error handling", + "Add documentation for common functional patterns", + "Consider adding property testing examples", + "Implement more collection transformations" + ] + }, + { + "file": "./citadel_proto/src/constants.rs", + "suggestions": [ + "Add configuration for packet processing metrics collection", + "Implement packet validation caching parameters", + "Add adaptive MTU size detection and adjustment", + "Include configurable compression thresholds", + "Add rate limiting parameters for flood protection", + "Consider environment-specific constant overrides", + "Add telemetry and monitoring constants" + ] + }, + { + "file": "./citadel_proto/src/auth.rs", + "suggestions": [ + "Add support for multi-factor authentication", + "Implement authentication method chaining", + "Add authentication policy management", + "Consider adding biometric authentication", + "Implement authentication rate limiting", + "Add detailed authentication logging", + "Consider adding OAuth/OIDC support", + "Add authentication metrics collection" + ] + }, + { + "file": "./citadel_proto/src/proto/validation.rs", + "suggestions": [ + "Add validation failure scenarios and error handling", + "Document security implications of validation failures", + "Add examples of packet validation process", + "Consider adding validation performance metrics", + "Document AEAD validation process in detail", + "Add validation testing guidelines", + "Add comprehensive error type system for better error handling and reporting", + "Implement validation metrics collection for monitoring and debugging", + "Add fuzzing tests for validation functions", + "Consider adding validation timeouts for DoS protection", + "Implement validation result caching for repeated packets", + "Add more detailed validation logging for security auditing", + "Consider implementing validation pipeline for better modularity" + ] + }, + { + "file": "./citadel_proto/src/proto/session_manager.rs", + "suggestions": [ + "Add examples for common session management operations", + "Document session upgrade process in detail", + "Add examples for group broadcast functionality", + "Document virtual connection management", + "Add error handling examples", + "Consider adding performance optimization notes" + ] + }, + { + "file": "./citadel_proto/src/proto/packet.rs", + "suggestions": [ + "Add packet compression support for large payloads", + "Implement packet fragmentation and reassembly", + "Add packet priority levels for QoS", + "Implement packet batching for efficiency", + "Add packet encryption versioning", + "Consider adding packet tracing for debugging", + "Add packet statistics collection" + ] + }, + { + "file": "./citadel_proto/src/proto/session.rs", + "suggestions": [ + "Add more detailed error handling documentation", + "Document performance considerations for file transfers", + "Add examples for UDP connection handling", + "Document security level configuration options", + "Add reconnection handling examples", + "Consider adding session migration documentation", + "Implement session migration between nodes", + "Add session state persistence for crash recovery", + "Consider implementing session multiplexing", + "Add session performance metrics collection", + "Implement session bandwidth control", + "Add session event hooks for external monitoring", + "Consider implementing session checkpointing", + "Add session load balancing support", + "Implement session prioritization", + "Add session debugging and tracing capabilities", + "Consider implementing session replay for testing", + "Add session resource usage monitoring" + ] + }, + { + "file": "./citadel_proto/src/proto/session_queue_handler.rs", + "suggestions": [ + "Add usage examples for common task scheduling patterns", + "Consider adding error handling documentation for task failures", + "Document best practices for task timeout values", + "Add performance considerations for queue capacity", + "Add task prioritization within reserved and ordinary task categories", + "Implement task cancellation mechanism for cleanup", + "Add metrics collection for task execution times", + "Consider implementing task batching for performance", + "Add task execution history for debugging", + "Consider implementing task dependencies", + "Add task execution retry mechanism with backoff", + "Consider implementing task queue persistence" + ] + }, + { + "file": "./citadel_user/src/lib.rs", + "suggestions": [ + "Add account migration support", + "Implement account quotas", + "Add multi-factor authentication", + "Implement account roles", + "Add audit logging", + "Implement account backup", + "Add account metrics", + "Implement account policies", + "Add account templates", + "Implement account groups" + ] + }, + { + "file": "./citadel_user/src/external_services/rtdb.rs", + "suggestions": [ + "Add offline data persistence", + "Implement data validation", + "Add data compression", + "Implement batch operations", + "Add connection pooling", + "Implement data encryption", + "Add error recovery", + "Implement data caching", + "Add connection monitoring", + "Implement data versioning" + ] + }, + { + "file": "./citadel_user/src/external_services/mod.rs", + "suggestions": [ + "Add service health checks", + "Implement service discovery", + "Add service metrics", + "Implement service fallbacks", + "Add service rate limiting", + "Implement service caching", + "Add service monitoring", + "Implement service retry logic", + "Add service load balancing", + "Implement service circuit breakers" + ] + }, + { + "file": "./citadel_user/src/external_services/google_auth.rs", + "suggestions": [ + "Add token refresh support", + "Implement token validation", + "Add token revocation", + "Implement token caching", + "Add claim customization", + "Implement key rotation", + "Add token monitoring", + "Implement rate limiting", + "Add error retries", + "Implement audit logging" + ] + }, + { + "file": "./citadel_user/src/backend/sql_backend.rs", + "suggestions": [ + "Add database migration support", + "Implement query optimization", + "Add database indexing", + "Implement database sharding", + "Add database monitoring", + "Implement database backup", + "Add database replication", + "Implement connection retries", + "Add query caching", + "Implement query logging" + ] + }, + { + "file": "./citadel_user/src/backend/redis_backend.rs", + "suggestions": [ + "Add Redis Sentinel support", + "Implement Redis pub/sub", + "Add Redis cache eviction policies", + "Implement Redis data compression", + "Add Redis monitoring metrics", + "Implement Redis backup/restore", + "Add Redis transaction support", + "Implement Redis key expiration", + "Add Redis error retries", + "Implement Redis rate limiting" + ] + }, + { + "file": "./citadel_user/src/backend/filesystem_backend.rs", + "suggestions": [ + "Add file system health checks", + "Implement file system quota management", + "Add file system backup functionality", + "Implement file system compression", + "Add file system encryption at rest", + "Implement file system defragmentation", + "Add file system metrics collection", + "Implement file system caching policies", + "Add file system integrity checks", + "Implement file system cleanup routines" + ] + }, + { + "file": "./citadel_user/src/backend/memory.rs", + "suggestions": [ + "Add memory usage metrics and monitoring", + "Implement memory limits per client", + "Add data compression options", + "Support custom serialization formats", + "Add cache eviction policies", + "Implement backup/restore functionality", + "Add memory leak detection", + "Support sharding for large datasets", + "Add performance benchmarks", + "Implement memory defragmentation" + ] + }, + { + "file": "./citadel_user/src/backend/utils/mod.rs", + "suggestions": [ + "Add transfer resumption", + "Implement transfer queuing", + "Add bandwidth limiting", + "Support chunked transfers", + "Add transfer verification", + "Implement transfer priorities", + "Add transfer encryption", + "Support parallel transfers", + "Add transfer compression", + "Implement transfer logging" + ] + }, + { + "file": "./citadel_user/src/auth/proposed_credentials.rs", + "suggestions": [ + "Add password strength validation", + "Implement MFA support", + "Add biometric authentication", + "Implement OAuth integration", + "Add password policy enforcement", + "Implement rate limiting", + "Add credential recovery", + "Implement audit logging", + "Add session management", + "Implement account lockout" + ] + }, + { + "file": "./citadel_user/src/auth/mod.rs", + "suggestions": [ + "Add multi-factor authentication", + "Support OAuth integration", + "Add biometric authentication", + "Implement rate limiting", + "Add auth event logging", + "Support auth delegation", + "Add auth policy management", + "Implement session management", + "Add auth metrics collection", + "Support auth migration tools" + ] + }, + { + "file": "./citadel_user/src/misc.rs", + "suggestions": [ + "Add error categorization", + "Implement error chaining", + "Add error recovery suggestions", + "Enhance metadata validation", + "Add path normalization", + "Implement error statistics", + "Add error documentation", + "Enhance timestamp flexibility", + "Add path security checks", + "Implement error localization" + ] + }, + { + "file": "./citadel_user/src/account_loader.rs", + "suggestions": [ + "Add async loading support", + "Implement account caching", + "Add parallel loading capabilities", + "Support account versioning", + "Add account validation hooks", + "Implement account migration", + "Add progress reporting", + "Support compressed storage", + "Add account indexing", + "Implement backup functionality" + ] + }, + { + "file": "./citadel_user/src/serialization.rs", + "suggestions": [ + "Add compression support", + "Implement format versioning", + "Add serialization validation", + "Implement streaming serialization", + "Add async serialization support", + "Implement custom type adapters", + "Add schema evolution support", + "Implement zero-copy deserialization", + "Add serialization metrics", + "Implement format migration" + ] + }, + { + "file": "./citadel_user/src/connection_metadata.rs", + "suggestions": [ + "Add connection status tracking", + "Implement connection metrics", + "Add protocol version support", + "Implement connection pooling", + "Add connection retry logic", + "Implement connection timeouts", + "Add connection quality monitoring", + "Implement protocol fallback", + "Add connection load balancing", + "Implement connection migration" + ] + }, + { + "file": "./citadel_user/src/directory_store.rs", + "suggestions": [ + "Add directory cleanup functionality", + "Implement directory size monitoring", + "Add storage quota management", + "Implement directory backup", + "Add file integrity checking", + "Implement directory compression", + "Add directory encryption", + "Implement storage rotation", + "Add storage optimization", + "Implement directory recovery" + ] + }, + { + "file": "./citadel_user/src/credentials.rs", + "suggestions": [ + "Add password complexity requirements", + "Implement password strength scoring", + "Add support for password policies", + "Implement username format patterns", + "Add reserved username handling", + "Implement credential migration", + "Add password history tracking", + "Implement password expiration", + "Add multi-factor auth support", + "Implement credential encryption" + ] + }, + { + "file": "./citadel_user/src/hypernode_account.rs", + "suggestions": [ + "Add support for custom identifier types", + "Implement caching for frequent lookups", + "Add batch operation support", + "Implement identifier validation", + "Add search result pagination", + "Implement search filters", + "Add identifier type conversion", + "Implement search result sorting", + "Add search result caching", + "Implement fuzzy username search" + ] + }, + { + "file": "./citadel_user/src/account_manager.rs", + "suggestions": [ + "Add account migration between backends", + "Implement account backup and restore", + "Add rate limiting for account operations", + "Implement account activity monitoring", + "Add support for account metadata search", + "Implement account state validation", + "Add support for account groups", + "Implement account lifecycle hooks", + "Add support for custom backend types", + "Implement account recovery mechanisms" + ] + }, + { + "file": "./citadel_user/src/client_account.rs", + "suggestions": [ + "Consider adding rate limiting for connection attempts", + "Add automated cleanup of stale peer connections", + "Implement connection quality metrics tracking", + "Add support for connection pooling", + "Consider implementing connection retry with backoff", + "Add support for connection prioritization", + "Implement connection load balancing", + "Add support for connection migration", + "Consider implementing connection multiplexing", + "Add support for connection compression" + ] + }, + { + "file": "./citadel_wire/src/lib.rs", + "suggestions": [ + "Add connection pooling and management", + "Implement connection monitoring and metrics", + "Add automatic protocol selection", + "Consider adding WebRTC support", + "Add connection recovery mechanisms" + ] + }, + { + "file": "./async_ip/src/lib.rs", + "suggestions": [ + "Add configurable timeouts for HTTP requests to prevent hanging", + "Implement caching mechanism to reduce repeated requests", + "Add support for custom IP resolution services via configuration", + "Consider adding DNS-over-HTTPS as a fallback mechanism", + "Add metrics collection for service reliability tracking", + "Implement retry mechanism with exponential backoff", + "Add support for STUN/TURN servers as alternative IP detection method", + "Consider adding async-std runtime support alongside tokio" + ] + }, + { + "file": "./netbeam/src/lib.rs", + "suggestions": [ + "Add tracing instrumentation for better observability", + "Implement connection pooling for multiplexed connections", + "Add support for connection backpressure handling", + "Consider adding async-std runtime support", + "Implement connection health monitoring", + "Add metrics collection for performance monitoring", + "Consider adding WebSocket transport support", + "Implement graceful shutdown handling", + "Add support for connection migration", + "Consider implementing a builder pattern for configuration" + ] + }, + { + "file": "./netbeam/src/reliable_conn.rs", + "suggestions": [ + "Add configurable retry policies", + "Implement connection keep-alive mechanism", + "Add support for connection pooling", + "Implement connection timeout handling", + "Add support for TLS configuration", + "Consider adding QUIC transport support", + "Implement connection migration", + "Add support for protocol versioning", + "Consider implementing flow control" + ] + }, + { + "file": "./netbeam/src/multiplex.rs", + "suggestions": [ + "Add stream prioritization support", + "Implement flow control mechanisms", + "Add support for stream quality metrics", + "Implement automatic stream compression", + "Add stream bandwidth allocation controls", + "Implement stream pooling for performance", + "Add support for stream migration", + "Implement stream state persistence", + "Add stream health monitoring", + "Support for stream quality of service (QoS)" + ] + }, + { + "file": "./netbeam/src/time_tracker.rs", + "suggestions": [ + "Add support for custom time sources", + "Implement time synchronization between peers", + "Add support for different time units", + "Consider implementing time-based rate limiting", + "Add support for timing statistics collection", + "Implement timing event callbacks", + "Add support for timing-based circuit breakers" + ] + }, + { + "file": "./citadel_types/src/lib.rs", + "suggestions": [ + "Add feature flags for optional components", + "Implement custom derive macros for common traits", + "Add type conversion traits for common use cases", + "Consider implementing builder patterns for complex types", + "Add validation traits for type constraints", + "Implement better error conversion traits", + "Add support for custom serialization formats", + "Consider implementing type-level state machines" + ] + }, + { + "file": "./citadel_types/src/crypto/mod.rs", + "suggestions": [ + "Add support for hardware security modules", + "Implement key rotation policies", + "Add support for custom memory allocators", + "Consider implementing zero-copy operations", + "Add support for additional post-quantum algorithms", + "Implement key derivation function selection", + "Add support for secure random number generation", + "Consider implementing constant-time operations", + "Add support for crypto hardware acceleration", + "Implement secure key storage interfaces", + "Add support for key backup and recovery", + "Consider implementing quantum-resistant key exchange" + ] + }, + { + "file": "./citadel_logging/src/lib.rs", + "suggestions": [ + "Add support for log file rotation and archival", + "Implement structured JSON logging format option", + "Add support for custom log formatters", + "Consider implementing log filtering by module path", + "Add support for log sampling in high-volume scenarios", + "Implement log aggregation and batching", + "Add support for remote log shipping", + "Consider implementing log compression", + "Add support for log metrics collection", + "Implement log level override by module", + "Add support for custom panic handlers", + "Consider implementing log correlation IDs" + ] + }, + { + "file": "./citadel_io/src/lib.rs", + "suggestions": [ + "Add support for async locks and synchronization primitives", + "Implement feature flags for optional components", + "Add benchmarking suite for performance comparison", + "Consider implementing WebAssembly threads support", + "Add more comprehensive error handling and custom error types", + "Consider implementing I/O utilities for file system operations", + "Add support for WebAssembly SharedArrayBuffer when available", + "Consider implementing async-std runtime support", + "Add telemetry and metrics collection", + "Consider implementing WebSocket utilities" + ] + }, + { + "file": "./citadel_io/src/standard/locks.rs", + "suggestions": [ + "Add support for try_lock operations", + "Implement timeout-based locking", + "Add support for fair locking policies", + "Consider implementing read-biased and write-biased RwLocks", + "Add support for lock upgrading and downgrading", + "Consider implementing condition variables", + "Add support for recursive locks" + ] + }, + { + "file": "./citadel_io/src/wasm/locks.rs", + "suggestions": [ + "Add support for atomics when WebAssembly threads are available", + "Implement try_lock operations", + "Add timeout-based locking simulation", + "Consider implementing lock statistics collection", + "Add support for async locks", + "Consider implementing lock elision for single-threaded contexts" + ] + }, + { + "file": "./citadel_io/src/wasm/rng.rs", + "suggestions": [ + "Add support for seeded random number generation", + "Implement additional distribution types", + "Add support for hardware-based entropy sources when available", + "Consider implementing a pooled entropy collector", + "Add support for deterministic random number generation", + "Consider implementing additional cryptographic primitives", + "Add support for custom entropy sources" + ] + }, + { + "file": "./citadel_pqcrypto/src/constructor_opts.rs", + "suggestions": [ + "Add benchmarks for different cryptographic parameter combinations", + "Consider adding validation for common parameter configurations", + "Implement parameter migration/upgrade functionality", + "Add more comprehensive examples for chained operations" + ] + }, + { + "file": "./citadel_pqcrypto/src/bytes_in_place.rs", + "suggestions": [ + "Add performance benchmarks for different buffer sizes", + "Consider implementing zero-copy buffer pooling", + "Add support for more buffer types (e.g., Ring)", + "Implement optional buffer pre-allocation strategies" + ] + }, + { + "file": "./citadel_pqcrypto/src/wire.rs", + "suggestions": [ + "Add support for protocol versioning", + "Implement parameter negotiation for optimal security", + "Add telemetry for parameter transfer performance", + "Consider implementing parameter compression" + ] + }, + { + "file": "./citadel_pqcrypto/src/replay_attack_container.rs", + "suggestions": [ + "Add configurable window size support", + "Implement adaptive window sizing based on network conditions", + "Add telemetry for replay attack detection", + "Consider implementing PID compression for efficiency" + ] + }, + { + "file": "./citadel_pqcrypto/src/standard/mod.rs", + "suggestions": [ + "Add support for different cryptographic primitives", + "Implement key exchange protocol", + "Add support for digital signatures", + "Implement message authentication codes", + "Add support for key derivation functions", + "Implement secure data storage", + "Add support for secure multi-party computation", + "Implement zero-knowledge proofs", + "Add support for homomorphic encryption", + "Implement secure data sharing" + ] + }, + { + "file": "./Suggestions for Future Improvements", + "suggestions": [ + "## Documentation Enhancements", + "1. Add architecture diagrams showing cryptographic component interactions", + "2. Create more real-world examples combining multiple components", + "3. Document performance characteristics and benchmarks", + "4. Add migration guides for future algorithm updates", + "5. Create a dedicated security guide explaining cryptographic choices", + "## Code Improvements", + "1. Add property-based tests for cryptographic operations", + "2. Implement fuzzing tests for serialization/deserialization", + "3. Add compile-time checks for cryptographic parameters", + "4. Consider constant-time alternatives for remaining operations", + "5. Add more comprehensive error types and messages", + "## Security Enhancements", + "1. Add runtime checks for weak key detection", + "2. Implement key rotation mechanisms", + "3. Add comprehensive nonce management utilities", + "4. Consider additional post-quantum algorithms", + "5. Add secure key backup/recovery mechanisms", + "6. Implement secure key distribution protocols", + "7. Add key compromise detection features", + "## Developer Experience", + "1. Create interactive documentation examples", + "2. Add more debug logging for development", + "3. Improve error messages and recovery suggestions", + "4. Create setup guides for common use cases", + "5. Add more code generation tools", + "6. Create visualization tools for cryptographic operations", + "## Performance Optimizations", + "1. Profile and optimize critical paths", + "2. Implement batched operations", + "3. Investigate hardware acceleration", + "4. Add async versions of key operations", + "5. Optimize memory usage in buffer operations", + "6. Reduce allocations in hot paths", + "7. Consider SIMD optimizations where applicable", + "## Testing and Validation", + "1. Add more comprehensive test vectors", + "2. Implement conformance testing", + "3. Add performance regression tests", + "4. Create stress tests for edge cases", + "5. Add memory leak detection tests", + "6. Implement timing attack resistance tests", + "## Integration and Deployment", + "1. Create deployment guides", + "2. Add container support", + "3. Create cloud deployment examples", + "4. Add monitoring and alerting examples", + "5. Create backup and recovery procedures", + "6. Document upgrade procedures" + ] + }, + { + "file": "./Network Configuration and Error Handling", + "suggestions": [ + "### Hypernode Type Configuration", + "1. Consider adding configuration validation for server nodes to ensure IP addresses are truly static", + "2. Implement automatic detection of network environment for optimal node type selection", + "3. Add support for hybrid node types that can switch between server and peer modes", + "4. Consider implementing connection quality metrics for node type decisions", + "### Error Handling Improvements", + "1. Add more granular error types for specific UPnP failure scenarios", + "2. Implement retry policies with exponential backoff for transient network errors", + "3. Add telemetry for tracking common error patterns in production", + "4. Consider implementing error recovery strategies for common failure modes" + ] + }, + { + "file": "./citadel_wire/src/standard/nat_identification.rs", + "suggestions": [ + "Add TURN server fallback support", + "Implement NAT behavior caching", + "Add NAT type change detection", + "Consider adding ICE protocol support", + "Add NAT traversal performance metrics" + ] + }, + { + "file": "./citadel_wire/src/standard/socket_helpers.rs", + "suggestions": [ + "Add socket pooling support", + "Implement socket keepalive configuration", + "Add socket buffer size optimization", + "Consider adding SCTP protocol support", + "Add socket statistics collection" + ] + }, + { + "file": "./citadel_wire/src/standard/upnp_handler.rs", + "suggestions": [ + "Add IPv6 support", + "Implement port mapping renewal", + "Add port mapping status monitoring", + "Support multiple gateways", + "Add port mapping persistence" + ] + }, + { + "file": "./citadel_wire/src/standard/quic.rs", + "suggestions": [ + "Add QUIC connection migration support", + "Implement 0-RTT connection resumption", + "Add congestion control configuration", + "Consider adding multipath QUIC support", + "Add QUIC datagram support" + ] + }, + { + "file": "./citadel_wire/src/standard/tls.rs", + "suggestions": [ + "Add certificate pinning support", + "Implement OCSP stapling", + "Add SNI support", + "Consider adding TLS session resumption", + "Add certificate transparency support" + ] + }, + { + "file": "./citadel_wire/src/standard/misc.rs", + "suggestions": [ + "Add support for PEM format conversion", + "Implement certificate validation", + "Add certificate expiry handling", + "Consider adding key rotation utilities", + "Add certificate chain verification" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/hole_punch_config.rs", + "suggestions": [ + "Add dynamic port range adjustment based on NAT behavior", + "Implement concurrent socket preparation", + "Add configuration persistence across restarts", + "Consider adding address prediction caching", + "Add telemetry for successful predictions" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/udp_hole_puncher.rs", + "suggestions": [ + "Implement adaptive retry intervals based on latency", + "Add connection quality monitoring", + "Consider implementing connection migration", + "Add detailed failure diagnostics", + "Implement concurrent hole punching attempts" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs", + "suggestions": [ + "Add metrics collection for NAT translation patterns", + "Implement connection health monitoring", + "Add support for multiple NAT layers", + "Consider adding address prediction heuristics", + "Implement automatic connection recovery" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs", + "suggestions": [ + "Add encryption algorithm versioning support", + "Implement packet compression for efficiency", + "Add encryption performance metrics", + "Consider adding key rotation mechanism", + "Add support for multiple STUN server fallbacks" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/method3.rs", + "suggestions": [ + "Add adaptive TTL optimization based on network conditions", + "Implement concurrent hole punching attempts", + "Add detailed failure diagnostics and logging", + "Consider adding connection quality metrics", + "Add support for connection migration" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/mod.rs", + "suggestions": [ + "Add adaptive timing based on network conditions", + "Implement method selection based on NAT type", + "Add connection quality monitoring", + "Consider parallel method attempts for speed", + "Add detailed telemetry for method success rates" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/multi/mod.rs", + "suggestions": [ + "Add dynamic port range optimization", + "Implement smart protocol selection based on network", + "Add connection quality metrics per attempt", + "Consider implementing attempt prioritization", + "Add detailed telemetry for winner selection" + ] + }, + { + "file": "./citadel_wire/src/standard/mod.rs", + "suggestions": [ + "Add protocol version management", + "Implement protocol feature negotiation", + "Add comprehensive protocol metrics", + "Consider adding protocol extensions API", + "Add protocol compatibility checking", + "Add support for custom protocol extensions", + "Implement protocol versioning for backwards compatibility" + ] + }, + { + "file": "./citadel_crypt/src/entropy_bank.rs", + "suggestions": [ + "Add entropy quality monitoring", + "Implement entropy source diversity", + "Add entropy pooling mechanism", + "Consider adding entropy health checks", + "Add entropy collection metrics", + "Consider implementing entropy mixing" + ] + }, + { + "file": "./citadel_crypt/src/endpoint_crypto_container.rs", + "suggestions": [ + "Add session state recovery mechanism", + "Implement session migration support", + "Add session metrics and telemetry", + "Consider adding session backup", + "Add session health monitoring", + "Consider implementing session replay" + ] + }, + { + "file": "./citadel_crypt/src/misc.rs", + "suggestions": [ + "Add error categorization system", + "Implement error recovery hints", + "Add error telemetry support", + "Consider adding error chaining", + "Add error statistics tracking", + "Consider implementing error patterns" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs", + "suggestions": [ + "Add dynamic partition resizing support", + "Implement partition defragmentation", + "Add partition allocation strategies", + "Consider implementing partition merging", + "Add memory usage statistics", + "Consider adding partition access patterns" + ] + }, + { + "file": "./citadel_crypt/src/packet_vector.rs", + "suggestions": [ + "Add dynamic wave sizing", + "Implement wave compression", + "Add wave metrics collection", + "Consider adding wave prioritization", + "Add wave health monitoring", + "Consider implementing wave merging" + ] + }, + { + "file": "./citadel_crypt/src/sync_toggle.rs", + "suggestions": [ + "Add transition history tracking", + "Implement multi-state toggle", + "Add state timeout support", + "Consider adding state callbacks", + "Add toggle metrics collection" + ] + }, + { + "file": "./citadel_crypt/src/stacked_ratchet.rs", + "suggestions": [ + "Consider adding benchmarks for key evolution operations", + "Add more detailed error handling for edge cases", + "Consider implementing key backup/recovery mechanisms", + "Add more comprehensive testing for post-quantum operations", + "Consider implementing key rotation policies" + ] + }, + { + "file": "./citadel_crypt/src/scramble/crypt_splitter.rs", + "suggestions": [ + "Implement adaptive wave sizing based on network conditions", + "Add packet loss prediction and preemptive retransmission", + "Implement congestion control mechanisms", + "Add support for prioritized packet transmission", + "Implement bandwidth estimation and adaptation", + "Consider adding forward error correction" + ] + }, + { + "file": "./citadel_crypt/src/streaming_crypt_scrambler.rs", + "suggestions": [ + "Add dynamic group size adjustment based on memory pressure", + "Implement streaming compression support", + "Add more detailed progress reporting", + "Consider implementing pause/resume functionality", + "Add support for multiple concurrent streams", + "Consider implementing stream prioritization" + ] + }, + { + "file": "./citadel_crypt/src/toolset.rs", + "suggestions": [ + "Add metrics collection for ratchet usage patterns", + "Implement adaptive memory limits based on system resources", + "Add support for emergency key rotation", + "Consider implementing ratchet state backup/recovery", + "Add more comprehensive validation for ratchet updates", + "Consider implementing ratchet version pruning policies" + ] + }, + { + "file": "./citadel_crypt/src/fcm/keys.rs", + "suggestions": [ + "Add credential rotation support", + "Implement secure credential encryption at rest", + "Add validation for API key format", + "Consider implementing key expiration", + "Add support for multiple FCM projects", + "Consider adding credential source tracking" + ] + }, + { + "file": "./citadel_crypt/src/fcm/fcm_ratchet.rs", + "suggestions": [ + "Add message compression to maximize 4KB limit", + "Implement message priority handling", + "Add support for FCM topic messaging", + "Consider implementing message batching", + "Add telemetry for ratchet operations", + "Consider implementing rate limiting" + ] + }, + { + "file": "./citadel_crypt/src/argon/argon_container.rs", + "suggestions": [ + "Add adaptive parameter selection based on system resources", + "Implement password strength validation", + "Add support for password policy enforcement", + "Consider implementing key stretching for derived keys", + "Add telemetry for password hashing performance", + "Consider implementing rate limiting for verification" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/sec_packet.rs", + "suggestions": [ + "Add packet validation and integrity checks", + "Implement packet compression support", + "Add packet fragmentation handling", + "Consider implementing packet pooling", + "Add packet statistics and metrics", + "Consider implementing packet prioritization" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/mod.rs", + "suggestions": [ + "Add buffer pooling support", + "Implement buffer compression", + "Add buffer metrics and telemetry", + "Consider adding buffer serialization", + "Add buffer encryption utilities", + "Consider implementing buffer versioning" + ] + }, + { + "file": "./citadel_user/src/backend/mod.rs", + "suggestions": [ + "Add MongoDB support", + "Implement connection pooling", + "Add data migration tools", + "Support backend replication", + "Add backup/restore utilities", + "Implement data compression", + "Add metrics collection", + "Support backend failover", + "Add data validation hooks", + "Implement caching layer" + ] + }, + { + "file": "./citadel_proto/src/proto/remote.rs", + "suggestions": [ + "Add request timeout configuration", + "Implement request batching for efficiency", + "Add request priority levels", + "Implement request rate limiting", + "Add request validation middleware", + "Implement request retry logic", + "Add request metrics collection", + "Implement request tracing", + "Add request compression support", + "Implement request caching" + ] + }, + { + "file": "./citadel_proto/src/proto/transfer_stats.rs", + "suggestions": [ + "Consider adding methods to calculate moving averages for more stable rate measurements", + "Add serialization support for statistics logging and analysis", + "Consider implementing statistics reset functionality", + "Add methods to calculate percentiles and other statistical measures", + "Consider adding bandwidth throttling capabilities based on transfer stats" + ] + }, + { + "file": "citadel_proto/src/proto/peer/channel.rs", + "suggestions": [ + "Add support for subgroups within channels", + "Implement message priority levels", + "Add support for temporary group memberships", + "Consider implementing group message persistence", + "Add group channel statistics and metrics", + "Implement group channel backup/restore" + ] + }, + { + "file": "./citadel_proto/src/proto/mod.rs", + "suggestions": [ + "Add performance metrics collection", + "Implement protocol versioning support", + "Add protocol extension mechanism", + "Consider adding protocol negotiation", + "Implement protocol migration support", + "Add comprehensive protocol diagnostics" + ] + }, + { + "file": "./citadel_proto/src/proto/node_result.rs", + "suggestions": [ + "Add structured error types", + "Implement result aggregation", + "Add result caching mechanism", + "Consider adding result filtering", + "Implement result streaming", + "Add detailed operation metrics" + ] + }, + { + "file": "./citadel_wire/src/peer_layer.rs", + "suggestions": [ + "Enhanced Peer Discovery", + "Implement DHT-based peer discovery for better scalability", + "Add support for peer reputation tracking", + "Introduce peer prioritization based on network metrics", + "Message Group Optimizations", + "Implement hierarchical group structures for better scaling", + "Add support for temporary/ephemeral groups", + "Introduce group message persistence options", + "Performance Improvements", + "Add connection pooling for frequently connected peers", + "Implement batch processing for peer signals", + "Optimize timeout handling with a more efficient timer wheel" + ] + }, + { + "file": "./citadel_wire/src/p2p_conn_handler.rs", + "suggestions": [ + "Connection Resilience", + "Implement automatic connection recovery mechanisms", + "Add support for connection migration between networks", + "Enhance error handling with detailed diagnostics", + "NAT Traversal Enhancements", + "Add support for additional NAT traversal techniques", + "Implement STUN/TURN fallback options", + "Optimize hole punching coordination timing", + "Performance Optimizations", + "Add connection pooling for frequently used paths", + "Implement bandwidth-aware connection management", + "Add support for connection multiplexing" + ] + }, + { + "file": "./citadel_wire/src/group_channel.rs", + "suggestions": [ + "Channel Features", + "Add support for prioritized message delivery", + "Implement message acknowledgment tracking", + "Add support for temporary message persistence", + "Group Management", + "Add support for hierarchical group permissions", + "Implement group member roles and capabilities", + "Add support for group message moderation", + "Performance Enhancements", + "Implement message batching for efficient broadcasts", + "Add support for selective message delivery", + "Optimize memory usage for large groups" + ] + }, + { + "file": "./citadel_wire/src/message_group.rs", + "suggestions": [ + "Enhanced Group Management", + "Implement hierarchical group structures", + "Add support for group roles and permissions", + "Introduce group message persistence options", + "Consent Model Improvements", + "Add support for delegated consent management", + "Implement group policy configurations", + "Add support for temporary group administrators", + "Performance Optimizations", + "Implement message caching mechanisms", + "Add support for message prioritization", + "Optimize peer state transitions" + ] + }, + { + "file": "./citadel_wire/src/peer_crypt.rs", + "suggestions": [ + "Key Exchange Enhancements", + "Add support for post-quantum cryptography", + "Implement key rotation mechanisms", + "Add support for multiple security profiles", + "NAT Traversal Improvements", + "Add support for additional NAT types", + "Implement smarter TURN server selection", + "Add bandwidth-aware routing options", + "Security Enhancements", + "Add support for hardware security modules", + "Implement perfect forward secrecy", + "Add support for custom security policies" + ] + }, + { + "file": "./citadel_wire/src/hole_punch_compat_sink_stream.rs", + "suggestions": [ + "Stream Improvements", + "Add support for stream multiplexing", + "Implement congestion control", + "Add QoS mechanisms", + "Compatibility Enhancements", + "Add support for additional protocols", + "Implement protocol version negotiation", + "Add support for protocol extensions", + "Performance Optimizations", + "Implement stream prioritization", + "Add support for zero-copy operations", + "Optimize packet buffering" + ] + }, + { + "file": "./citadel_wire/src/mod.rs", + "suggestions": [ + "Architecture Improvements", + "Implement plugin system for extensions", + "Add support for custom protocols", + "Enhance module interdependency management", + "Feature Enhancements", + "Add support for peer discovery mechanisms", + "Implement peer reputation system", + "Add support for network diagnostics", + "Documentation Improvements", + "Add comprehensive API documentation", + "Create usage tutorials and examples", + "Improve error handling documentation" + ] + }, + { + "file": "./state_subcontainers/mod.rs", + "suggestions": [ + "## Architecture Improvements", + "1. Implement state machine abstraction", + "2. Add state transition validation", + "3. Introduce state snapshot/restore", + "## Error Handling", + "1. Add detailed error reporting", + "2. Implement state recovery mechanisms", + "3. Add state validation checks", + "## Performance Optimizations", + "1. Implement state caching", + "2. Add async state transitions", + "3. Optimize memory usage" + ] + }, + { + "file": "./connect_state_container.rs", + "suggestions": [ + "## Connection Management", + "1. Add connection retry logic", + "2. Implement graceful degradation", + "3. Add connection quality metrics", + "## State Recovery", + "1. Add checkpoint/restore functionality", + "2. Implement state migration", + "3. Add connection resumption", + "## Monitoring", + "1. Add detailed connection metrics", + "2. Implement state transition logging", + "3. Add performance monitoring" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", + "suggestions": [ + "Add connection state metrics collection", + "Implement connection state persistence", + "Add detailed logging for state transitions", + "Consider adding connection retry policies", + "Implement connection state snapshots", + "Add connection state validation", + "Consider adding connection state migration", + "Add connection state timeout configuration", + "Implement connection state recovery testing", + "Add connection state monitoring interfaces" + ] + }, + { + "file": "./peer_kem_state_container.rs", + "suggestions": [ + "## Security Enhancements", + "1. Add quantum-resistant algorithms", + "2. Implement key rotation", + "3. Add security level negotiation", + "## Channel Management", + "1. Add channel multiplexing", + "2. Implement priority channels", + "3. Add channel quality metrics", + "## Performance Improvements", + "1. Optimize key exchange process", + "2. Add parallel key generation", + "3. Implement key caching" + ] + }, + { + "file": "./preconnect_state_container.rs", + "suggestions": [ + "## Setup Improvements", + "1. Add fast reconnect support", + "2. Implement connection pooling", + "3. Add connection prediction", + "## Protocol Enhancements", + "1. Add protocol negotiation", + "2. Implement version compatibility", + "3. Add feature discovery", + "## State Management", + "1. Add state compression", + "2. Implement state pruning", + "3. Add state verification" + ] + }, + { + "file": "./register_state_container.rs", + "suggestions": [ + "## Registration Features", + "1. Add multi-factor authentication", + "2. Implement progressive registration", + "3. Add registration policies", + "## Security Improvements", + "1. Add anti-spam measures", + "2. Implement rate limiting", + "3. Add fraud detection", + "## User Experience", + "1. Add registration recovery", + "2. Implement partial registration", + "3. Add registration analytics" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs", + "suggestions": [ + "Add deregistration state persistence", + "Implement deregistration retry mechanism", + "Add detailed logging for state transitions", + "Consider adding deregistration rollback support", + "Implement deregistration timeout handling", + "Add deregistration state validation", + "Consider adding batch deregistration support", + "Add metrics collection for deregistration processes", + "Implement deregistration state recovery", + "Add deregistration progress tracking" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs", + "suggestions": [ + "Implement dynamic expiry timing based on system load", + "Add detailed metrics for expiry events", + "Consider adding configurable expiry thresholds", + "Implement expiry state persistence", + "Add expiry event logging and monitoring", + "Consider adding expiry prediction", + "Implement group priority handling", + "Add expiry state recovery mechanisms", + "Consider adding expiry notifications", + "Implement expiry state snapshots" + ] + }, + { + "file": "./citadel_proto/src/proto/codec.rs", + "suggestions": [ + "Add configurable compression support", + "Implement buffer pooling for better memory reuse", + "Add metrics for buffer resize frequency", + "Consider adding batch processing capabilities", + "Add optional integrity verification" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/rekey_container.rs", + "suggestions": [ + "Add adaptive security level based on threat detection", + "Implement concurrent rekey operations", + "Add detailed metrics for rekey operations", + "Consider implementing key backup mechanisms", + "Add support for emergency key rotation" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", + "suggestions": [ + "Add connection quality metrics", + "Implement connection recovery strategies", + "Add support for connection migration", + "Consider implementing connection pooling", + "Add detailed connection diagnostics" + ] + }, + { + "file": "./citadel_proto/src/proto/peer/group_channel.rs", + "suggestions": [ + "Add support for subgroups within channels", + "Implement message priority levels", + "Add support for temporary group memberships", + "Consider implementing group message persistence", + "Add group channel statistics and metrics", + "Implement group channel backup/restore" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/mod.rs", + "suggestions": [ + "Add performance metrics tracking", + "Implement packet batching for efficiency", + "Add more comprehensive error reporting", + "Enhance packet validation logging", + "Add packet processing telemetry", + "Optimize memory allocation patterns" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/connect_packet.rs", + "suggestions": [ + "Add connection performance metrics", + "Implement connection migration", + "Add more detailed error states", + "Enhance connection debugging", + "Add connection benchmarking", + "Optimize handshake latency" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/disconnect_packet.rs", + "suggestions": [ + "Add graceful shutdown options", + "Implement reconnection support", + "Add disconnect reason tracking", + "Consider adding session persistence", + "Implement disconnect metrics", + "Add disconnect event logging" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/file_packet.rs", + "suggestions": [ + "Add transfer rate limiting", + "Implement transfer resumption", + "Add transfer compression", + "Consider adding transfer prioritization", + "Implement transfer metrics", + "Add detailed transfer logging" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/keep_alive_packet.rs", + "suggestions": [ + "Add connection quality metrics", + "Implement adaptive timing", + "Add detailed health logging", + "Enhance timeout handling", + "Add connection diagnostics", + "Optimize bandwidth usage" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/raw_primary_packet.rs", + "suggestions": [ + "Add packet processing metrics", + "Implement zero-copy optimizations", + "Add packet validation caching", + "Enhance error reporting", + "Add packet tracing", + "Optimize memory usage" + ] + }, + { + "file": "./kernel/mod.rs", + "suggestions": [ + "## Potential improvements", + "1. Consider adding metrics collection for runtime performance monitoring", + "2. Implement adaptive concurrency control based on system load", + "3. Add more comprehensive error handling and recovery mechanisms", + "4. Consider implementing a circuit breaker pattern for protocol operations", + "5. Add telemetry for better observability of kernel operations", + "6. Consider implementing graceful shutdown procedures", + "7. Add support for hot reloading of kernel configuration", + "8. Implement backpressure mechanisms for overload protection" + ] + }, + { + "file": "./kernel/kernel_trait.rs", + "suggestions": [ + "## Potential improvements", + "1. Add trait documentation with concrete implementation examples", + "2. Consider adding trait methods for performance monitoring", + "3. Add versioning support for protocol compatibility", + "4. Consider adding trait methods for connection pooling", + "5. Add support for custom serialization formats", + "6. Consider adding diagnostic/debug trait methods" + ] + }, + { + "file": "./kernel/kernel_executor.rs", + "suggestions": [ + "## Potential improvements", + "1. Add performance metrics collection", + "2. Implement dynamic concurrency adjustment", + "3. Add detailed logging for debugging", + "4. Consider implementing task prioritization", + "5. Add support for graceful shutdown", + "6. Implement backpressure mechanisms", + "7. Add support for task cancellation", + "8. Consider adding health monitoring" + ] + }, + { + "file": "./kernel/kernel_communicator.rs", + "suggestions": [ + "## Potential improvements", + "1. Add support for message prioritization", + "2. Implement message batching for efficiency", + "3. Add detailed message tracing capabilities", + "4. Consider adding flow control mechanisms", + "5. Implement message compression", + "6. Add support for message acknowledgments", + "7. Consider implementing message replay for recovery", + "8. Add monitoring for channel performance" + ] + }, + { + "file": "./proto/misc/clean_shutdown.rs", + "suggestions": [ + "Potential improvements", + "1. Add configurable shutdown timeouts", + "2. Implement shutdown progress tracking", + "3. Add detailed shutdown logging", + "4. Consider adding shutdown hooks", + "5. Implement rollback mechanisms", + "6. Add shutdown metrics collection" + ] + }, + { + "file": "./proto/misc/dual_cell.rs", + "suggestions": [ + "Potential improvements", + "1. Add debug assertions for thread safety", + "2. Consider adding performance benchmarks", + "3. Add more comprehensive type constraints", + "4. Consider implementing custom Debug", + "5. Add memory ordering options", + "6. Consider adding atomic fence controls" + ] + }, + { + "file": "./proto/misc/dual_late_init.rs", + "suggestions": [ + "Potential improvements", + "1. Add initialization timeout support", + "2. Consider adding initialization hooks", + "3. Add initialization failure recovery", + "4. Consider adding debug assertions", + "5. Add initialization metrics", + "6. Consider adding weak references" + ] + }, + { + "file": "./proto/misc/dual_rwlock.rs", + "suggestions": [ + "Potential improvements", + "1. Add reader/writer priority options", + "2. Consider adding lock timeout support", + "3. Add deadlock detection", + "4. Consider adding lock statistics", + "5. Add try_read/try_write methods", + "6. Consider adding fair locking policy" + ] + }, + { + "file": "./proto/misc/lock_holder.rs", + "suggestions": [ + "Potential improvements", + "1. Add lock acquisition timeout", + "2. Implement lock priority system", + "3. Add lock statistics tracking", + "4. Consider adding lock upgrading", + "5. Add deadlock detection", + "6. Consider adding lock borrowing" + ] + }, + { + "file": "./proto/misc/mod.rs", + "suggestions": [ + "Potential improvements", + "1. Add comprehensive module documentation", + "2. Consider grouping related utilities", + "3. Add utility function tests", + "4. Consider adding benchmarks", + "5. Add debug logging options", + "6. Consider adding profiling support" + ] + }, + { + "file": "./proto/misc/net.rs", + "suggestions": [ + "Potential improvements", + "1. Add connection pooling", + "2. Implement automatic retry logic", + "3. Add connection metrics", + "4. Consider adding protocol versioning", + "5. Add connection rate limiting", + "6. Consider adding connection prioritization" + ] + }, + { + "file": "./proto/misc/ordered_channel.rs", + "suggestions": [ + "Potential improvements", + "1. Add message prioritization", + "2. Implement message batching", + "3. Add channel metrics", + "4. Consider adding flow control", + "5. Add message compression", + "6. Consider adding message acknowledgments" + ] + }, + { + "file": "./proto/misc/udp_internal_interface.rs", + "suggestions": [ + "Potential improvements", + "1. Add performance metrics tracking", + "2. Implement UDP packet compression", + "3. Add more robust error recovery", + "4. Enhance logging for debugging", + "5. Add support for UDP multicast", + "6. Optimize buffer management" + ] + }, + { + "file": "./proto/misc/underlying_proto.rs", + "suggestions": [ + "Potential improvements", + "1. Add protocol performance metrics", + "2. Implement protocol compression", + "3. Add protocol migration tools", + "4. Enhance protocol debugging", + "5. Add protocol benchmarking", + "6. Optimize protocol handshake" + ] + }, + { + "file": "./netbeam/src/sync/subscription.rs", + "suggestions": [ + "Add stream performance metrics", + "Implement backpressure handling", + "Add stream prioritization", + "Enhance error handling", + "Add stream monitoring", + "Optimize memory usage" + ] + }, + { + "file": "./netbeam/src/sync/sync_start.rs", + "suggestions": [ + "Add synchronization metrics", + "Implement retry mechanisms", + "Add timeout configuration", + "Enhance error reporting", + "Add sync monitoring", + "Optimize timing precision" + ] + }, + { + "file": "./netbeam/src/sync/network_endpoint.rs", + "suggestions": [ + "Add endpoint metrics collection", + "Implement address caching", + "Add connection pooling", + "Enhance error handling", + "Add endpoint monitoring", + "Optimize address resolution" + ] + }, + { + "file": "./netbeam/src/sync/network_application.rs", + "suggestions": [ + "Add operation metrics tracking", + "Implement operation timeouts", + "Add operation prioritization", + "Enhance error handling", + "Add operation monitoring", + "Optimize resource usage" + ] + }, + { + "file": "./netbeam/src/sync/callback_channel.rs", + "suggestions": [ + "Add timeout support for callbacks", + "Implement channel capacity monitoring", + "Add backpressure mechanisms", + "Enhance error handling with more context", + "Add channel statistics tracking", + "Consider adding batch operations" + ] + }, + { + "file": "./netbeam/src/sync/tracked_callback_channel.rs", + "suggestions": [ + "Add request/response metrics tracking", + "Implement response timeout configuration", + "Add request prioritization", + "Enhance error handling with more context", + "Add request/response tracing", + "Consider adding request batching" + ] + }, + { + "file": "./netbeam/src/sync/mod.rs", + "suggestions": [ + "Add module-level metrics tracking", + "Implement module-wide logging", + "Add configuration system", + "Enhance error handling with more context", + "Add module-level tracing", + "Consider adding feature flags" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_join.rs", + "suggestions": [ + "Add timeout support", + "Implement cancellation", + "Add progress tracking", + "Enhance error handling", + "Add operation metrics", + "Consider adding retry logic" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_try_join.rs", + "suggestions": [ + "Add timeout support", + "Implement cancellation", + "Add progress tracking", + "Enhance error handling with more context", + "Add operation metrics", + "Consider adding retry logic", + "Add state transition validation" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_select.rs", + "suggestions": [ + "Add timeout support", + "Implement cancellation", + "Add race condition handling", + "Enhance conflict resolution", + "Add operation metrics", + "Consider adding fairness policies" + ] + }, + { + "file": "./netbeam/src/sync/primitives/net_mutex.rs", + "suggestions": [ + "Add lock acquisition metrics", + "Implement lock timeout configuration", + "Add lock contention handling", + "Enhance deadlock prevention", + "Add lock state monitoring", + "Consider adding lock fairness", + "Add lock acquisition tracing" + ] + }, + { + "file": "./netbeam/src/sync/primitives/mod.rs", + "suggestions": [ + "Add benchmarks for network synchronization operations", + "Consider implementing distributed condition variables", + "Add deadlock detection mechanisms", + "Implement timeout options for lock acquisition", + "Add metrics for lock contention and wait times" + ] + }, + { + "file": "./citadel_sdk/src/builder/node_builder.rs", + "suggestions": [ + "Add builder validation for common misconfigurations", + "Implement configuration presets for common use cases", + "Add support for configuration file loading", + "Improve error messages with configuration hints", + "Add builder method for custom certificate chain validation", + "Consider implementing Clone for NodeBuilder", + "Add more comprehensive usage examples in documentation" + ] + }, + { + "file": "./citadel_sdk/src/builder/mod.rs", + "suggestions": [ + "Add more builder components for other protocol features", + "Consider implementing a configuration validation module", + "Add builder patterns for common protocol scenarios", + "Include more comprehensive documentation with examples", + "Consider adding configuration serialization support" + ] + }, + { + "file": "./citadel_sdk/src/backend_kv_store.rs", + "suggestions": [ + "Add support for typed key-value storage with serialization", + "Implement TTL (time-to-live) for stored values", + "Add batch operations for better performance", + "Consider adding storage size limits and monitoring", + "Implement value compression for large data", + "Add support for atomic operations", + "Consider adding event notifications for value changes" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/mod.rs", + "suggestions": [ + "Add connection pool management for multiple servers", + "Implement connection retry and backoff strategies", + "Add connection monitoring and health checks", + "Consider adding connection migration support", + "Implement connection event hooks/callbacks", + "Add connection configuration validation", + "Consider adding connection metrics collection" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/single_connection.rs", + "suggestions": [ + "Add automatic reconnection handling", + "Implement connection state persistence", + "Add bandwidth control options", + "Consider implementing connection pooling", + "Add more comprehensive error handling", + "Implement connection quality metrics", + "Add support for connection migration" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/broadcast.rs", + "suggestions": [ + "Add support for multiple group owners", + "Implement group roles and permissions", + "Add group message history/persistence", + "Consider implementing group state sync", + "Add group member activity tracking", + "Implement group configuration versioning", + "Add support for group policy enforcement" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/peer_connection.rs", + "suggestions": [ + "Add connection quality monitoring", + "Implement automatic reconnection handling", + "Add bandwidth control for file transfers", + "Consider implementing connection pooling", + "Add support for connection migration", + "Implement peer discovery mechanisms", + "Add support for connection priorities" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/mod.rs", + "suggestions": [ + "Add more pre-built network patterns", + "Implement connection monitoring tools", + "Add support for custom protocol extensions", + "Consider adding metrics collection", + "Implement connection pooling patterns", + "Add more comprehensive error handling", + "Consider adding configuration validation" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs", + "suggestions": [ + "Add configurable file size limits", + "Implement file type filtering", + "Add progress reporting callbacks", + "Consider adding virus scanning integration", + "Add support for resumable transfers", + "Implement transfer rate limiting", + "Add transfer metrics collection" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/client_connect_listener.rs", + "suggestions": [ + "Add connection pooling support", + "Implement rate limiting", + "Add built-in authentication patterns", + "Consider adding connection metrics", + "Add support for connection priorities", + "Implement connection load balancing", + "Add connection event logging" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/empty.rs", + "suggestions": [ + "Add optional basic logging", + "Consider adding minimal statistics", + "Add configurable connection limits", + "Consider adding basic health checks", + "Add optional connection tracking", + "Implement basic monitoring hooks", + "Add simple diagnostics support" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/internal_service.rs", + "suggestions": [ + "Add WebSocket support", + "Implement service discovery", + "Add support for gRPC", + "Consider adding service metrics", + "Add support for service health checks", + "Implement service load balancing", + "Add service authentication" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/mod.rs", + "suggestions": [ + "Add more pre-built server patterns", + "Implement common server configurations", + "Add example implementations", + "Consider adding performance benchmarks", + "Add migration guides for upgrades", + "Implement server templates", + "Add server testing utilities" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/shared/internal_service.rs", + "suggestions": [ + "Add configurable buffer sizes", + "Implement backpressure handling", + "Add service metrics collection", + "Consider adding protocol versioning", + "Add connection pooling support", + "Implement service discovery", + "Add comprehensive error mapping" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/shared/mod.rs", + "suggestions": [ + "Add more shared utilities", + "Implement common network patterns", + "Add shared security components", + "Consider adding shared metrics", + "Add shared configuration handling", + "Implement shared testing utilities", + "Add shared type conversions" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/single_connection.rs", + "suggestions": [ + "Add automatic reconnection handling", + "Implement connection state persistence", + "Add connection quality monitoring", + "Consider adding connection pooling", + "Add detailed connection metrics", + "Implement connection migration", + "Add connection event logging" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/peer_connection.rs", + "suggestions": [ + "Add peer discovery mechanism", + "Implement connection quality metrics", + "Add automatic peer reconnection", + "Consider adding peer prioritization", + "Add peer bandwidth management", + "Implement peer load balancing", + "Add peer health monitoring" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/broadcast.rs", + "suggestions": [ + "Add group message persistence", + "Implement group member roles", + "Add group message encryption", + "Consider adding group size limits", + "Add group activity monitoring", + "Implement group backup owners", + "Add group message prioritization" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/mod.rs", + "suggestions": [ + "Add more connection patterns", + "Implement connection templates", + "Add comprehensive examples", + "Consider adding connection profiles", + "Add connection debugging tools", + "Implement connection monitoring", + "Add connection testing utilities" + ] + }, + { + "file": "./citadel_sdk/src/test_common.rs", + "suggestions": [ + "Add more comprehensive test utilities", + "Implement test scenario generators", + "Add performance testing utilities", + "Consider adding test data generators", + "Implement test configuration presets", + "Add test report generation", + "Consider adding test coverage helpers", + "Implement test cleanup utilities", + "Add test network simulation", + "Consider adding test monitoring tools" + ] + } +] From 78110a3dea7a339c61e14caa80219207434e7de0 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Thu, 5 Dec 2024 13:40:07 -0500 Subject: [PATCH 05/12] wip: thread-safe RatchetManager --- citadel_crypt/Cargo.toml | 3 + .../src/endpoint_crypto_container.rs | 694 ++- citadel_crypt/src/entropy_bank.rs | 24 +- citadel_crypt/src/fcm/mod.rs | 2 +- .../src/fcm/{fcm_ratchet.rs => ratchet.rs} | 228 +- citadel_crypt/src/lib.rs | 10 +- citadel_crypt/src/misc.rs | 7 +- citadel_crypt/src/packet_vector.rs | 25 +- citadel_crypt/src/ratchet_manager.rs | 690 +++ citadel_crypt/src/scramble/crypt_splitter.rs | 60 +- .../secure_buffer/partitioned_sec_buffer.rs | 8 +- citadel_crypt/src/secure_buffer/sec_packet.rs | 10 +- citadel_crypt/src/stacked_ratchet.rs | 973 ++-- .../src/streaming_crypt_scrambler.rs | 46 +- citadel_crypt/src/sync_toggle.rs | 51 +- citadel_crypt/src/toolset.rs | 212 +- citadel_crypt/tests/primary.rs | 179 +- citadel_logging/src/lib.rs | 12 +- citadel_pqcrypto/src/constructor_opts.rs | 28 +- citadel_pqcrypto/src/lib.rs | 18 +- citadel_pqcrypto/src/wire.rs | 4 +- citadel_pqcrypto/tests/primary.rs | 36 +- citadel_proto/backup/constants.rs | 12 +- citadel_proto/src/auth.rs | 2 +- citadel_proto/src/constants.rs | 12 +- .../src/kernel/kernel_communicator.rs | 16 +- citadel_proto/src/kernel/kernel_executor.rs | 22 +- citadel_proto/src/kernel/kernel_trait.rs | 5 +- citadel_proto/src/kernel/mod.rs | 12 +- citadel_proto/src/lib.rs | 212 +- .../src/proto/endpoint_crypto_accessor.rs | 19 +- citadel_proto/src/proto/misc/dual_cell.rs | 11 +- citadel_proto/src/proto/mod.rs | 7 +- citadel_proto/src/proto/node.rs | 82 +- citadel_proto/src/proto/node_request.rs | 41 +- citadel_proto/src/proto/node_result.rs | 69 +- citadel_proto/src/proto/packet.rs | 8 +- citadel_proto/src/proto/packet_crafter.rs | 519 +- .../proto/packet_processor/connect_packet.rs | 63 +- .../packet_processor/deregister_packet.rs | 82 +- .../packet_processor/disconnect_packet.rs | 36 +- .../src/proto/packet_processor/file_packet.rs | 63 +- .../src/proto/packet_processor/hole_punch.rs | 19 +- .../packet_processor/keep_alive_packet.rs | 18 +- .../src/proto/packet_processor/mod.rs | 6 +- .../packet_processor/peer/group_broadcast.rs | 92 +- .../src/proto/packet_processor/peer/mod.rs | 9 +- .../packet_processor/peer/peer_cmd_packet.rs | 387 +- .../peer/server/post_connect.rs | 35 +- .../peer/server/post_register.rs | 23 +- .../packet_processor/preconnect_packet.rs | 145 +- .../packet_processor/primary_group_packet.rs | 316 +- .../packet_processor/raw_primary_packet.rs | 77 +- .../proto/packet_processor/register_packet.rs | 114 +- .../proto/packet_processor/rekey_packet.rs | 102 +- .../src/proto/packet_processor/udp_packet.rs | 17 +- citadel_proto/src/proto/peer/channel.rs | 50 +- citadel_proto/src/proto/peer/group_channel.rs | 30 +- .../peer/hole_punch_compat_sink_stream.rs | 16 +- .../src/proto/peer/p2p_conn_handler.rs | 45 +- citadel_proto/src/proto/peer/peer_layer.rs | 185 +- citadel_proto/src/proto/remote.rs | 31 +- citadel_proto/src/proto/session.rs | 386 +- citadel_proto/src/proto/session_manager.rs | 223 +- .../src/proto/session_queue_handler.rs | 53 +- citadel_proto/src/proto/state_container.rs | 346 +- .../peer_kem_state_container.rs | 8 +- .../preconnect_state_container.rs | 15 +- .../register_state_container.rs | 26 +- .../state_subcontainers/rekey_container.rs | 23 +- citadel_proto/src/proto/validation.rs | 172 +- citadel_proto/tests/connections.rs | 17 +- citadel_sdk/examples/client.rs | 23 +- citadel_sdk/examples/peer.rs | 19 +- citadel_sdk/examples/server.rs | 5 +- citadel_sdk/src/backend_kv_store.rs | 11 +- citadel_sdk/src/builder/node_builder.rs | 58 +- citadel_sdk/src/fs.rs | 85 +- citadel_sdk/src/lib.rs | 16 +- citadel_sdk/src/macros.rs | 10 +- citadel_sdk/src/prefabs/client/broadcast.rs | 63 +- citadel_sdk/src/prefabs/client/mod.rs | 48 +- .../src/prefabs/client/peer_connection.rs | 158 +- .../src/prefabs/client/single_connection.rs | 69 +- citadel_sdk/src/prefabs/mod.rs | 23 +- .../server/accept_file_transfer_kernel.rs | 14 +- .../prefabs/server/client_connect_listener.rs | 16 +- citadel_sdk/src/prefabs/server/empty.rs | 14 +- .../src/prefabs/server/internal_service.rs | 25 +- .../src/prefabs/shared/internal_service.rs | 6 +- citadel_sdk/src/remote_ext.rs | 229 +- citadel_sdk/src/responses.rs | 24 +- citadel_sdk/src/test_common.rs | 41 +- citadel_sdk/tests/stress_tests.rs | 105 +- citadel_user/src/account_manager.rs | 30 +- .../src/backend/filesystem_backend.rs | 70 +- citadel_user/src/backend/memory.rs | 60 +- citadel_user/src/backend/mod.rs | 34 +- citadel_user/src/backend/redis_backend.rs | 84 +- citadel_user/src/backend/sql_backend.rs | 85 +- citadel_user/src/client_account.rs | 25 +- .../src/external_services/google_auth.rs | 6 +- citadel_user/src/external_services/mod.rs | 4 +- citadel_user/src/external_services/rtdb.rs | 4 +- .../external_services/service_interface.rs | 6 +- citadel_user/src/hypernode_account.rs | 37 +- citadel_user/tests/primary.rs | 28 +- context.ai.json | 4467 +++++++++-------- .../c2s/client_basic_transient_connection.rs | 11 +- .../c2s/client_basic_with_server_password.rs | 23 +- example-library/examples/c2s/client_echo.rs | 21 +- example-library/examples/c2s/server_basic.rs | 2 +- .../c2s/server_basic_with_password.rs | 2 +- example-library/examples/c2s/server_echo.rs | 2 +- example-library/examples/p2p/chat.rs | 21 +- example-library/examples/p2p/file_transfer.rs | 21 +- example-library/examples/p2p/revfs_delete.rs | 21 +- .../examples/p2p/revfs_read_write.rs | 21 +- example-library/examples/p2p/revfs_take.rs | 21 +- suggestions.ai.json | 12 + 120 files changed, 7567 insertions(+), 6212 deletions(-) rename citadel_crypt/src/fcm/{fcm_ratchet.rs => ratchet.rs} (60%) create mode 100644 citadel_crypt/src/ratchet_manager.rs diff --git a/citadel_crypt/Cargo.toml b/citadel_crypt/Cargo.toml index 9230f7ae8..4f91f6e7d 100644 --- a/citadel_crypt/Cargo.toml +++ b/citadel_crypt/Cargo.toml @@ -52,6 +52,8 @@ auto_impl = { workspace = true } zeroize = { workspace = true, features = ["zeroize_derive", "alloc", "serde"] } citadel_types = { workspace = true } uuid = { version = "1.8.0", features = ["v4"] } +atomic = { workspace = true } +bytemuck = { workspace = true, features = ["derive"] } [target.'cfg(not(target_family = "wasm"))'.dependencies] rayon = { workspace = true } @@ -62,6 +64,7 @@ num_cpus = { workspace = true } citadel_logging = { workspace = true } rstest = { workspace = true } lazy_static = { workspace = true } +tokio = { workspace = true, features = ["rt-multi-thread"] } [lib] doctest = false diff --git a/citadel_crypt/src/endpoint_crypto_container.rs b/citadel_crypt/src/endpoint_crypto_container.rs index 3c0a0d6fa..d7a364d62 100644 --- a/citadel_crypt/src/endpoint_crypto_container.rs +++ b/citadel_crypt/src/endpoint_crypto_container.rs @@ -65,33 +65,32 @@ #![allow(missing_docs)] use crate::misc::CryptError; -use crate::stacked_ratchet::constructor::{AliceToBobTransferType, BobToAliceTransferType}; -use crate::stacked_ratchet::{Ratchet, StackedRatchet}; -use crate::toolset::{Toolset, UpdateStatus}; +use crate::stacked_ratchet::Ratchet; +use crate::sync_toggle::{CurrentToggleState, SyncToggle}; +use crate::toolset::{Toolset, ToolsetUpdateStatus}; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_types::crypto::CryptoParameters; -use citadel_types::crypto::SecurityLevel; -use citadel_types::prelude::ObjectId; +use citadel_types::prelude::{ObjectId, SecurityLevel}; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::fmt::Debug; use uuid::Uuid; /// A container that holds the toolset as well as some boolean flags to ensure validity /// in tight concurrency situations. It is up to the networking protocol to ensure /// that the inner functions are called when appropriate #[derive(Serialize, Deserialize)] -pub struct PeerSessionCrypto { +pub struct PeerSessionCrypto { #[serde(bound = "")] pub toolset: Toolset, - pub update_in_progress: Arc, + pub update_in_progress: SyncToggle, // if local is initiator, then in the case both nodes send a FastMessage at the same time (causing an update to the keys), the initiator takes preference, and the non-initiator's upgrade attempt gets dropped (if update_in_progress) pub local_is_initiator: bool, pub rolling_object_id: ObjectId, pub rolling_group_id: u64, pub lock_set_by_alice: Option, /// Alice sends to Bob, then bob updates internally the toolset. However. Bob can't send packets to Alice quite yet using that newest version. He must first wait from Alice to commit on her end and wait for an ACK. - /// If alice sends a packet using the latest version, that's okay since we already have that drill version on Bob's side; it's just that Bob can't send packets using the latest version until AFTER receiving the ACK + /// If alice sends a packet using the latest version, that's okay since we already have that entropy_bank version on Bob's side; it's just that Bob can't send packets using the latest version until AFTER receiving the ACK pub latest_usable_version: u32, } @@ -103,7 +102,7 @@ impl PeerSessionCrypto { pub fn new(toolset: Toolset, local_is_initiator: bool) -> Self { Self { toolset, - update_in_progress: Arc::new(AtomicBool::new(false)), + update_in_progress: SyncToggle::new(), local_is_initiator, rolling_object_id: ObjectId::random(), rolling_group_id: 0, @@ -126,60 +125,93 @@ impl PeerSessionCrypto { } } - /// Gets a specific drill version, or, gets the latest version committed - pub fn get_hyper_ratchet(&self, version: Option) -> Option<&R> { + /// Gets a specific entropy_bank version, or, gets the latest version committed + pub fn get_ratchet(&self, version: Option) -> Option<&R> { self.toolset - .get_hyper_ratchet(version.unwrap_or(self.latest_usable_version)) + .get_stacked_ratchet(version.unwrap_or(self.latest_usable_version)) } /// This should only be called when Bob receives the new DOU during the ReKey phase (will receive transfer), or, when Alice receives confirmation /// that the endpoint updated the ratchet (no transfer received, since none needed) - pub fn commit_next_hyper_ratchet_version( + #[allow(clippy::type_complexity)] + pub fn commit_next_stacked_ratchet_version( &mut self, mut newest_version: R::Constructor, local_cid: u64, - local_is_alice: bool, - ) -> Result<(Option, UpdateStatus), CryptError> { - let cur_vers = self.toolset.get_most_recent_hyper_ratchet_version(); + generate_next: bool, + ) -> Result< + ( + Option<>::BobToAliceWireTransfer>, + ToolsetUpdateStatus, + ), + CryptError, + > { + let cur_vers = self.toolset.get_most_recent_stacked_ratchet_version(); let next_vers = cur_vers.wrapping_add(1); + + // Update version before any stage operations newest_version.update_version(next_vers).ok_or_else(|| { - CryptError::DrillUpdateError("Unable to progress past update_version".to_string()) + CryptError::RekeyUpdateError("Unable to progress past update_version".to_string()) })?; + if !generate_next { + let latest_ratchet = newest_version + .finish_with_custom_cid(local_cid) + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to progress past finish_with_custom_cid for bob-to-alice trigger" + .to_string(), + ) + })?; + let status = self.toolset.update_from(latest_ratchet).ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to progress past update_from for bob-to-alice trigger".to_string(), + ) + })?; + + return Ok((None, status)); + } + + /* let transfer = if local_is_alice { None } else { - // we don't want to custom CID here - Some(newest_version.stage0_bob().ok_or_else(|| { - CryptError::DrillUpdateError("Unable to progress past stage0_bob".to_string()) - })?) - }; + // Generate transfer after version update + let transfer = newest_version.stage0_bob().ok_or_else(|| { + CryptError::RekeyUpdateError("Unable to progress past stage0_bob".to_string()) + })?; + Some(transfer) + };*/ - let next_hyper_ratchet = newest_version + // Generate transfer after version update + let transfer = newest_version.stage0_bob().ok_or_else(|| { + CryptError::RekeyUpdateError("Unable to progress past stage0_bob".to_string()) + })?; + + let next_stacked_ratchet = newest_version .finish_with_custom_cid(local_cid) .ok_or_else(|| { - CryptError::DrillUpdateError( + CryptError::RekeyUpdateError( "Unable to progress past finish_with_custom_cid".to_string(), ) })?; let status = self .toolset - .update_from(next_hyper_ratchet) + .update_from(next_stacked_ratchet) .ok_or_else(|| { - CryptError::DrillUpdateError("Unable to progress past update_from".to_string()) + CryptError::RekeyUpdateError("Unable to progress past update_from".to_string()) })?; - log::trace!(target: "citadel", "[E2E] Successfully updated StackedRatchet from v{} to v{}", cur_vers, next_vers); - //self.latest_hyper_ratchet_version_committed = next_vers; + log::trace!(target: "citadel", "[E2E] Successfully updated StackedRatchet from v{cur_vers} to v{next_vers} for {local_cid}"); - Ok((transfer, status)) + Ok((Some(transfer), status)) } /// Deregisters the oldest StackedRatchet version. Requires the version input to ensure program/network consistency for debug purposes - pub fn deregister_oldest_hyper_ratchet( + pub fn deregister_oldest_stacked_ratchet( &mut self, version: u32, ) -> Result<(), CryptError> { - self.toolset.deregister_oldest_hyper_ratchet(version) + self.toolset.deregister_oldest_stacked_ratchet(version) } /// Performs an update internally, only if sync conditions allow @@ -188,30 +220,43 @@ impl PeerSessionCrypto { constructor: R::Constructor, local_is_alice: bool, local_cid: u64, - ) -> Result { - let update_in_progress = self.update_in_progress.load(Ordering::SeqCst); + triggered_by_bob_to_alice_transfer: bool, + ) -> Result, CryptError> { + let update_in_progress = + self.update_in_progress.state() == CurrentToggleState::AlreadyToggled; log::trace!(target: "citadel", "[E2E] Calling UPDATE (local_is_alice: {}. Update in progress: {})", local_is_alice, update_in_progress); - // if local is alice (relative), then update_in_progress will be expected to be true. As such, we don't want this to occur - if update_in_progress && !local_is_alice { + + // if local is alice (relative), then update_in_progress will be expected to be true. As such, we don't want an update to occur + // if local_is_alice is true, and we are updating, that means we are in the middle of a protocol resolution, and we don't want to update + if update_in_progress && local_is_alice { // update is in progress. We only update if local is NOT the initiator (this implies the packet triggering this was sent by the initiator, which takes the preference as desired) // if local is initiator, then the packet was sent by the non-initiator, and as such, we don't update on local - if self.local_is_initiator { - return Ok(KemTransferStatus::Omitted); + if !self.local_is_initiator { + return Ok(KemTransferStatus::Contended); } } // There is one last special possibility. Let's say the initiator spam sends a bunch of FastMessage packets. Since the initiator's local won't have the appropriate proposed version ID // we need to ensure that it gets the right version, The crypt container will take care of that for us - let (transfer, status) = - self.commit_next_hyper_ratchet_version(constructor, local_cid, local_is_alice)?; + let result = self.commit_next_stacked_ratchet_version( + constructor, + local_cid, + !triggered_by_bob_to_alice_transfer, + ); + if let Err(err) = &result { + log::error!(target: "citadel", "[E2E] Error during update: {:?}", err); + self.update_in_progress.toggle_off(); + } + + let (transfer, status) = result?; let ret = if let Some(transfer) = transfer { KemTransferStatus::Some(transfer, status) } else { // if it returns with None, and local isn't alice, return an error since we expected Some - if !local_is_alice { - return Err(CryptError::DrillUpdateError( - "Local is not alice, yet, conflicting program state".to_string(), + if !triggered_by_bob_to_alice_transfer { + return Err(CryptError::RekeyUpdateError( + "This should only be reached if triggered by a bob-to-alice transfer event, yet, conflicting program state".to_string(), )); } @@ -221,38 +266,39 @@ impl PeerSessionCrypto { // if ret has some, we need one more thing. If we are upgrading the ratchet here on bob's end, we need to place a lock to ensure to updates come from this end until after a TRUNCATE packet comes // if this is alice's end, we don't unlock quite yet if !local_is_alice && ret.has_some() { - self.update_in_progress.store(true, Ordering::SeqCst); + let _ = self.update_in_progress.toggle_on_if_untoggled(); self.lock_set_by_alice = Some(false); } Ok(ret) } - /// Unlocks the hold on future updates, then returns the latest hyper_ratchet + /// Unlocks the hold on future updates, then returns the latest stacked_ratchet /// Providing "false" will unconditionally unlock the ratchet pub fn maybe_unlock(&mut self, requires_locked_by_alice: bool) -> Option<&R> { if requires_locked_by_alice { if self.lock_set_by_alice.unwrap_or(false) { - if !self.update_in_progress.fetch_nand(true, Ordering::SeqCst) { + if self.update_in_progress.reset_and_get_previous() + != CurrentToggleState::AlreadyToggled + { log::error!(target: "citadel", "Expected update_in_progress to be true"); } - //self.update_in_progress.store(false, Ordering::SeqCst); self.lock_set_by_alice = None; log::trace!(target: "citadel", "Unlocking for {}", self.toolset.cid); } } else { - if !self.update_in_progress.fetch_nand(true, Ordering::SeqCst) { + if self.update_in_progress.reset_and_get_previous() + != CurrentToggleState::AlreadyToggled + { log::error!(target: "citadel", "Expected update_in_progress to be true. LSBA: {:?} | Cid: {}", self.lock_set_by_alice, self.toolset.cid); - //std::process::exit(-1); } - //self.update_in_progress.store(false, Ordering::SeqCst); self.lock_set_by_alice = None; log::trace!(target: "citadel", "Unlocking for {}", self.toolset.cid); } - self.get_hyper_ratchet(None) + self.get_ratchet(None) } /// For alice: this should be called ONLY if the update occurred locally. This updates the latest usable version at the endpoint @@ -273,26 +319,30 @@ impl PeerSessionCrypto { /// Returns a new constructor only if a concurrent update isn't occurring /// `force`: If the internal boolean was locked prior to calling this in anticipation, force should be true pub fn get_next_constructor(&mut self, force: bool) -> Option { - let set_lock = move |this: &mut Self| { - this.update_in_progress.store(true, Ordering::SeqCst); - this.lock_set_by_alice = Some(true); - this.get_hyper_ratchet(None)?.next_alice_constructor() + let next_constructor = move |this: &mut Self| { + // Only set lock_set_by_alice if we successfully get a constructor + if let Some(constructor) = this.get_ratchet(None)?.next_alice_constructor() { + this.lock_set_by_alice = Some(true); + Some(constructor) + } else { + None + } }; if force { - return set_lock(self); + return next_constructor(self); } - if self.update_in_progress.load(Ordering::SeqCst) { + if self.update_in_progress.toggle_on_if_untoggled() == CurrentToggleState::AlreadyToggled { None } else { - set_lock(self) + next_constructor(self) } } /// Refreshed the internal state to init state pub fn refresh_state(&mut self) { - self.update_in_progress = Arc::new(AtomicBool::new(false)); + self.update_in_progress.toggle_off(); self.lock_set_by_alice = None; self.rolling_group_id = 0; self.rolling_object_id = ObjectId::random(); @@ -302,37 +352,51 @@ impl PeerSessionCrypto { pub fn get_default_params(&self) -> CryptoParameters { self.toolset .get_static_auxiliary_ratchet() - .message_pqc_drill(None) + .get_message_pqc_and_entropy_bank_at_layer(None) + .expect("Expected to get message pqc and entropy bank") .0 .params } } +pub trait AssociatedSecurityLevel { + fn security_level(&self) -> SecurityLevel; +} + +pub trait AssociatedCryptoParams { + fn crypto_params(&self) -> CryptoParameters; +} + // TODO: Use GAT's to have a type AliceToBobConstructor<'a>. Get rid of these enums -pub trait EndpointRatchetConstructor: Send + Sync + 'static { - fn new_alice( - opts: Vec, - cid: u64, - new_version: u32, - security_level: Option, - ) -> Option +pub trait EndpointRatchetConstructor: Debug + Send + Sync + 'static { + type AliceToBobWireTransfer: Send + + Sync + + Serialize + + DeserializeOwned + + AssociatedSecurityLevel + + AssociatedCryptoParams; + type BobToAliceWireTransfer: Send + + Sync + + Serialize + + DeserializeOwned + + AssociatedSecurityLevel; + fn new_alice(opts: Vec, cid: u64, new_version: u32) -> Option where Self: Sized; - fn new_bob( + fn new_bob>( cid: u64, - new_drill_vers: u32, opts: Vec, - transfer: AliceToBobTransferType, - psks: &[Vec], + transfer: Self::AliceToBobWireTransfer, + psks: &[T], ) -> Option where Self: Sized; - fn stage0_alice(&self) -> Option; - fn stage0_bob(&self) -> Option; - fn stage1_alice( + fn stage0_alice(&self) -> Option; + fn stage0_bob(&mut self) -> Option; + fn stage1_alice>( &mut self, - transfer: BobToAliceTransferType, - psks: &[Vec], + transfer: Self::BobToAliceWireTransfer, + psks: &[T], ) -> Result<(), CryptError>; fn update_version(&mut self, version: u32) -> Option<()>; @@ -342,23 +406,47 @@ pub trait EndpointRatchetConstructor: Send + Sync + 'static { #[derive(Serialize, Deserialize)] #[allow(variant_size_differences)] -pub enum KemTransferStatus { - StatusNoTransfer(UpdateStatus), +pub enum KemTransferStatus { + StatusNoTransfer(ToolsetUpdateStatus), Empty, - Omitted, - Some(BobToAliceTransferType, UpdateStatus), + Contended, + #[serde(bound = "")] + Some( + >::BobToAliceWireTransfer, + ToolsetUpdateStatus, + ), } -impl KemTransferStatus { +impl Debug for KemTransferStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + KemTransferStatus::StatusNoTransfer(status) => { + write!(f, "KemTransferStatus::StatusNoTransfer({:?})", status) + } + KemTransferStatus::Empty => write!(f, "KemTransferStatus::Empty"), + KemTransferStatus::Contended => write!(f, "KemTransferStatus::Contended"), + KemTransferStatus::Some(_, status) => { + write!(f, "KemTransferStatus::Some(transfer, {status:?})") + } + } + } +} + +impl KemTransferStatus { pub fn requires_truncation(&self) -> Option { match self { - KemTransferStatus::StatusNoTransfer(UpdateStatus::CommittedNeedsSynchronization { - old_version, - .. - }) + KemTransferStatus::StatusNoTransfer( + ToolsetUpdateStatus::CommittedNeedsSynchronization { + oldest_version: old_version, + .. + }, + ) | KemTransferStatus::Some( _, - UpdateStatus::CommittedNeedsSynchronization { old_version, .. }, + ToolsetUpdateStatus::CommittedNeedsSynchronization { + oldest_version: old_version, + .. + }, ) => Some(*old_version), _ => None, @@ -366,7 +454,7 @@ impl KemTransferStatus { } pub fn omitted(&self) -> bool { - matches!(self, Self::Omitted) + matches!(self, Self::Contended) } pub fn has_some(&self) -> bool { @@ -374,50 +462,412 @@ impl KemTransferStatus { } } -/* #[cfg(test)] -mod tests { - use crate::hyper_ratchet::StackedRatchet; - use crate::hyper_ratchet::constructor::{StackedRatchetConstructor, BobToAliceTransferType}; +pub(crate) mod no_race { + use crate::endpoint_crypto_container::{KemTransferStatus, PeerSessionCrypto}; use crate::prelude::{ConstructorOpts, Toolset}; - use citadel_pqcrypto::algorithm_dictionary::{EncryptionAlgorithm, KemAlgorithm}; - use crate::drill::SecurityLevel; - use crate::endpoint_crypto_container::{PeerSessionCrypto, KemTransferStatus}; + use crate::stacked_ratchet::constructor::StackedRatchetConstructor; + use crate::stacked_ratchet::{Ratchet, StackedRatchet}; + use crate::toolset::{ToolsetUpdateStatus, MAX_RATCHETS_IN_MEMORY}; + use citadel_types::prelude::{EncryptionAlgorithm, KemAlgorithm, SecurityLevel}; - fn gen(enx: EncryptionAlgorithm, kem: KemAlgorithm, security_level: SecurityLevel, version: u32, opts: Option>) -> (StackedRatchet, StackedRatchet) { - let opts = opts.unwrap_or_else(||ConstructorOpts::new_vec_init(Some(enx + kem), (security_level.value() + 1) as usize)); - let mut cx_alice = StackedRatchetConstructor::new_alice(opts.clone(), 0, version, Some(security_level)); - let cx_bob = StackedRatchetConstructor::new_bob(0, version, opts, cx_alice.stage0_alice()).unwrap(); - cx_alice.stage1_alice(&BobToAliceTransferType::Default(cx_bob.stage0_bob().unwrap())).unwrap(); + const ALICE_CID: u64 = 10; + const BOB_CID: u64 = 20; + pub const TEST_PSKS: &[&[u8]] = &[b"test_psk_1", b"test_psk_2"]; + + fn gen>( + version: u32, + opts: Vec, + psks: &[T], + ) -> (StackedRatchet, StackedRatchet) { + let mut cx_alice = + StackedRatchetConstructor::new_alice_constructor(opts.clone(), ALICE_CID, version) + .unwrap(); + let mut cx_bob = StackedRatchetConstructor::new_bob_constructor( + BOB_CID, + version, + opts, + cx_alice.stage0_alice().unwrap(), + psks, + ) + .unwrap(); + cx_alice + .stage1_alice(cx_bob.stage0_bob().unwrap(), psks) + .unwrap(); (cx_alice.finish().unwrap(), cx_bob.finish().unwrap()) } - #[test] - fn upgrades() { - const NUM_UPDATES: usize = 1; - citadel_logging::setup_log(); - for level in 0..10u8 { - let level = SecurityLevel::from(level); - let (hr_alice, hr_bob) = gen(EncryptionAlgorithm::AES_GCM_256_SIV, KemAlgorithm::Kyber, level, 0, None); - let mut endpoint_alice = PeerSessionCrypto::new(Toolset::new(0, hr_alice), true); - let mut endpoint_bob = PeerSessionCrypto::new(Toolset::new(0, hr_bob), false); - - for vers in 1..=NUM_UPDATES { - // now, upgrade - let alice_hr_cons = endpoint_alice.get_next_constructor(false).unwrap(); - let transfer = alice_hr_cons.stage0_alice(); - - let bob_constructor = StackedRatchetConstructor::new_bob(0, vers as _, ) - match endpoint_bob.update_sync_safe(next_alice_2_bob, false, 0).unwrap() { - KemTransferStatus::Some(transfer, status) => { - let - endpoint_alice.update_sync_safe(transfer.assume_default().unwrap()) + pub(crate) fn setup_endpoint_containers( + security_level: SecurityLevel, + enx: EncryptionAlgorithm, + kem: KemAlgorithm, + ) -> ( + PeerSessionCrypto, + PeerSessionCrypto, + ) { + let opts = ConstructorOpts::new_vec_init(Some(enx + kem), security_level); + let (hr_alice, hr_bob) = gen(START_VERSION, opts, TEST_PSKS); + assert_eq!(hr_alice.version(), START_VERSION); + assert_eq!(hr_bob.version(), START_VERSION); + assert_eq!(hr_alice.get_cid(), ALICE_CID); + assert_eq!(hr_bob.get_cid(), BOB_CID); + let alice_container = PeerSessionCrypto::new(Toolset::new(ALICE_CID, hr_alice), true); + let bob_container = PeerSessionCrypto::new(Toolset::new(BOB_CID, hr_bob), false); + (alice_container, bob_container) + } + + const START_VERSION: u32 = 0; + + pub(crate) fn pre_round_assertions( + alice_container: &PeerSessionCrypto, + alice_cid: u64, + bob_container: &PeerSessionCrypto, + bob_cid: u64, + ) -> (u32, u32) { + assert_eq!( + alice_container.get_ratchet(None).unwrap().get_cid(), + alice_cid + ); + assert_eq!(bob_container.get_ratchet(None).unwrap().get_cid(), bob_cid); + + let start_version = alice_container + .toolset + .get_most_recent_stacked_ratchet_version(); + let new_version = start_version + 1; + let new_version_bob = bob_container + .toolset + .get_most_recent_stacked_ratchet_version() + + 1; + assert_eq!(new_version, new_version_bob); + (start_version, new_version) + } + + fn expect_truncation_checks( + expects_truncation: bool, + container: &PeerSessionCrypto, + status: ToolsetUpdateStatus, + requires_truncation: Option, + ) -> Option { + if expects_truncation { + if container.toolset.len() >= MAX_RATCHETS_IN_MEMORY { + let ToolsetUpdateStatus::CommittedNeedsSynchronization { + new_version: _, + oldest_version, + } = status + else { + panic!("Expected ToolsetUpdateStatus::CommittedNeedsSynchronization"); + }; + return Some(oldest_version); + } else { + assert!(requires_truncation.is_none()); + } + } + + None + } + + /// `expect_truncation` should be set to false if the round is expected to complete without truncation, meaning + /// there are less than MAX_HYPER_RATCHETS_IN_MEMORY HR's in memory and thus no truncation is needed. Otherwise, + /// set to true if truncation is expected + fn run_round_no_race( + container_0: &mut PeerSessionCrypto, + container_1: &mut PeerSessionCrypto, + expect_truncation: bool, + ) { + let cid_0 = container_0.toolset.cid; + let cid_1 = container_1.toolset.cid; + + let (start_version, next_version) = + pre_round_assertions(container_0, cid_0, container_1, cid_1); + + let mut alice_constructor = container_0.get_next_constructor(false).unwrap(); + let alice_to_bob_transfer = alice_constructor.stage0_alice().unwrap(); + + // Bob must generate his next opts recursively to continue ratcheting appropriately + let next_opts = container_1 + .get_ratchet(None) + .unwrap() + .get_next_constructor_opts(); + + let bob_constructor = StackedRatchetConstructor::new_bob_constructor( + cid_1, + next_version, + next_opts, + alice_to_bob_transfer, + TEST_PSKS, + ) + .unwrap(); + + // Perform update on Bob's side, container_1 + let kem_transfer_status_bob = container_1 + .update_sync_safe(bob_constructor, false, cid_1, true) + .unwrap(); + + let requires_truncation_bob = kem_transfer_status_bob.requires_truncation(); + + match kem_transfer_status_bob { + KemTransferStatus::Some(bob_to_alice_transfer, toolset_status_bob) => { + if !expect_truncation { + assert!(requires_truncation_bob.is_none()); + assert!( + matches!(toolset_status_bob, ToolsetUpdateStatus::Committed { new_version } if new_version == next_version) + ); + } + + // In this case, bob expects truncation, but, alice will too, in which case we let alice handle the truncation logic + let _do_nothing = expect_truncation_checks( + expect_truncation, + container_1, + toolset_status_bob, + requires_truncation_bob, + ); + + // Flow in the protocol: primary_group_packet.rs:fn attempt_kem_as_alice_finish + // alice: stage1_alice + // alice: update + // alice: if truncation is required, we call deregister_oldest_stacked_ratchet + // alice: call post_alice_stage1_or_post_stage1_bob + // alice: if truncation not required, unlock(requires_locked_by_alice=true) and end, else: + // alice: send TRUNCATE packet to bob + // bob: call deregister_oldest_stacked_ratchet on that truncated version in the packet + // bob: call post_alice_stage1_or_post_stage1_bob + // bob: call unlock(requires_locked_by_alice=false), providing false since bob is not alice + // bob: send TRUNCATE_ACK packet to alice + // alice: call unlock(requires_locked_by_alice=true) and end + + alice_constructor + .stage1_alice(bob_to_alice_transfer, TEST_PSKS) + .unwrap(); + + let kem_transfer_status_alice = container_0 + .update_sync_safe(alice_constructor, true, cid_0, false) + .unwrap(); + + let requires_truncation_alice = kem_transfer_status_alice.requires_truncation(); + + if !expect_truncation { + assert!(requires_truncation_alice.is_none()); + assert!(matches!(kem_transfer_status_alice, + KemTransferStatus::StatusNoTransfer( + ToolsetUpdateStatus::Committed { + new_version + } + ) if new_version == next_version + )); + } + + assert_eq!(requires_truncation_alice, requires_truncation_bob, "Asymmetry not allowed:requires_truncation_alice: {requires_truncation_alice:?}, requires_truncation_bob: {requires_truncation_bob:?}"); + + // Continue to follow protocol flow + + // If no truncation, we runs some tests and short-circuit here + if requires_truncation_alice.is_none() { + // Test message encryption/decryption. Since post_alice_stage1_or_post_stage1_bob has not yet been called, + // the latest usable version should still be the start version + ratchet_encrypt_decrypt_test( + container_0, + cid_0, + container_1, + cid_1, + start_version, + ); + simulate_protocol_resolution_no_truncation(container_0, container_1); + + assert_eq!(start_version + 1, next_version); + + // Test message encryption/decryption again. Now that post_alice_stage1_or_post_stage1_bob has been called, + // the latest usable version should be the new version + ratchet_encrypt_decrypt_test( + container_0, + cid_0, + container_1, + cid_1, + next_version, + ); + return; + } + + match kem_transfer_status_alice { + KemTransferStatus::StatusNoTransfer(toolset_status_alice) => { + if let Some(version_to_truncate) = expect_truncation_checks( + expect_truncation, + container_0, + toolset_status_alice, + requires_truncation_alice, + ) { + container_0 + .deregister_oldest_stacked_ratchet(version_to_truncate) + .unwrap(); + container_0.post_alice_stage1_or_post_stage1_bob(); + // Assume alice then sends a TRUNCATE packet to bob, telling him to remove this version + // send_to_bob(version_to_truncate); + container_1 + .deregister_oldest_stacked_ratchet(version_to_truncate) + .unwrap(); + container_1.post_alice_stage1_or_post_stage1_bob(); + let ratchet_bob = container_1.maybe_unlock(false).unwrap(); + // Assume bob then sends a TRUNCATE_ACK packet to alice, telling her to remove her local lock + // send_to_alice(version_to_truncate); + let ratchet_alice = container_0.maybe_unlock(true).unwrap(); + assert_eq!(ratchet_alice.version(), ratchet_bob.version()); + let expected_version = ratchet_alice.version(); + ratchet_encrypt_decrypt_test( + container_0, + cid_0, + container_1, + cid_1, + expected_version, + ); + } + } + status => { + log::warn!(target: "citadel", "KemTransferStatus for Alice is not handled in this test: {status:?}"); } - _ => panic!("Did not expect this kem status") } } + status => { + log::warn!(target: "citadel", "KemTransferStatus for Bob is not handled in this test: {status:?}") + } + } + } + + pub(crate) fn ratchet_encrypt_decrypt_test( + container_0: &PeerSessionCrypto, + cid_0: u64, + container_1: &PeerSessionCrypto, + cid_1: u64, + expected_version: u32, + ) { + let test_message = b"Hello, World!"; + let alice_ratchet = container_0.get_ratchet(None).unwrap(); + assert_eq!(alice_ratchet.version(), expected_version); + assert_eq!(alice_ratchet.get_cid(), cid_0); + let encrypted = alice_ratchet.encrypt(test_message).unwrap(); + + let bob_ratchet = container_1.get_ratchet(None).unwrap(); + assert_eq!(bob_ratchet.version(), expected_version); + assert_eq!(bob_ratchet.get_cid(), cid_1); + let decrypted = bob_ratchet.decrypt(&encrypted).unwrap(); + assert_eq!(test_message.to_vec(), decrypted); + } + + fn simulate_protocol_resolution_no_truncation( + container_0: &mut PeerSessionCrypto, + container_1: &mut PeerSessionCrypto, + ) { + container_0.post_alice_stage1_or_post_stage1_bob(); + container_1.post_alice_stage1_or_post_stage1_bob(); + let _ = container_0.maybe_unlock(false); + let _ = container_1.maybe_unlock(false); + } + + fn endpoint_container_test( + limit: usize, + expect_truncation: bool, + fx: impl for<'a> Fn( + usize, + &'a mut PeerSessionCrypto, + &'a mut PeerSessionCrypto, + ) -> ( + &'a mut PeerSessionCrypto, + &'a mut PeerSessionCrypto, + ), + ) { + citadel_logging::setup_log(); + let security_level = SecurityLevel::Standard; + + let (mut alice_container, mut bob_container) = setup_endpoint_containers( + security_level, + EncryptionAlgorithm::AES_GCM_256, + KemAlgorithm::Kyber, + ); + + // Start at 1 since we already have 1 HR in memory per node. We go to the limit of + // MAX_RATCHETS_IN_MEMORY - 1 since we need to account for the SR's already in memory + for idx in 1..(limit - 1) { + let (container_0, container_1) = fx(idx, &mut alice_container, &mut bob_container); + run_round_no_race(container_0, container_1, expect_truncation); } } + + /// The next three tests focus on either alice, bob, or alternating between the two + /// updating the state serially and never in parallel. Additionally, the tests expect + /// no truncation to occur since the number of SR's in memory is never exceeded + #[test] + fn test_endpoint_container_no_truncation_only_alice_no_race() { + citadel_logging::setup_log(); + endpoint_container_test( + MAX_RATCHETS_IN_MEMORY, + false, + |_, alice_container, bob_container| (alice_container, bob_container), + ); + } + + #[test] + fn test_endpoint_container_no_truncation_only_bob_no_race() { + citadel_logging::setup_log(); + endpoint_container_test( + MAX_RATCHETS_IN_MEMORY, + false, + |_, alice_container, bob_container| (bob_container, alice_container), + ); + } + + #[test] + fn test_endpoint_container_no_truncation_alternating_no_race() { + citadel_logging::setup_log(); + endpoint_container_test( + MAX_RATCHETS_IN_MEMORY, + false, + |idx, alice_container, bob_container| { + if idx % 2 == 0 { + (bob_container, alice_container) + } else { + (alice_container, bob_container) + } + }, + ); + } + + /// The next three tests focus on either alice, bob, or alternating between the two + /// updating the state serially and never in parallel. Unlike the previous three tests, + /// these tests expect truncation to occur since the number of SR's in memory is exceeded + const TEST_RATCHET_LIMIT: usize = MAX_RATCHETS_IN_MEMORY + 100; + + #[test] + fn test_endpoint_container_truncation_only_alice_no_race() { + citadel_logging::setup_log(); + endpoint_container_test( + TEST_RATCHET_LIMIT, + true, + |_, alice_container, bob_container| (alice_container, bob_container), + ); + } + + #[test] + fn test_endpoint_container_truncation_only_bob_no_race() { + citadel_logging::setup_log(); + endpoint_container_test( + TEST_RATCHET_LIMIT, + true, + |_, alice_container, bob_container| (bob_container, alice_container), + ); + } + + #[test] + fn test_endpoint_container_truncation_alternating_no_race() { + citadel_logging::setup_log(); + endpoint_container_test( + TEST_RATCHET_LIMIT, + true, + |idx, alice_container, bob_container| { + if idx % 2 == 0 { + (bob_container, alice_container) + } else { + (alice_container, bob_container) + } + }, + ); + } } -*/ diff --git a/citadel_crypt/src/entropy_bank.rs b/citadel_crypt/src/entropy_bank.rs index 877a26db1..7a3c98546 100644 --- a/citadel_crypt/src/entropy_bank.rs +++ b/citadel_crypt/src/entropy_bank.rs @@ -74,7 +74,7 @@ pub const BYTES_PER_STORE: usize = LARGEST_NONCE_LEN; pub type DrillEndian = BigEndian; impl EntropyBank { - /// Creates a new drill + /// Creates a new entropy_bank pub fn new( cid: u64, version: u32, @@ -94,7 +94,7 @@ impl EntropyBank { }) } - /// For generating a random nonce, independent to any drill + /// For generating a random nonce, independent to any entropy_bank pub fn generate_public_nonce( enx_algorithm: EncryptionAlgorithm, ) -> ArrayVec { @@ -282,12 +282,18 @@ impl EntropyBank { self.cid } - /// Gets the version of the drill + /// Gets the version of the entropy_bank pub fn get_version(&self) -> u32 { self.version } - /// Downloads the data necessary to create a drill + /// Updates the version of the entropy_bank + pub fn update_version(&mut self, version: u32) -> Result<(), CryptError> { + self.version = version; + Ok(()) + } + + /// Downloads the data necessary to create a entropy_bank fn generate_random_array() -> Result<[u8; BYTES_PER_STORE], CryptError> { let mut bytes: [u8; BYTES_PER_STORE] = [0u8; BYTES_PER_STORE]; let mut trng = thread_rng(); @@ -298,13 +304,13 @@ impl EntropyBank { /// Serializes self to a vector pub fn serialize_to_vec(&self) -> Result, CryptError> { - bincode::serialize(self).map_err(|err| CryptError::DrillUpdateError(err.to_string())) + bincode::serialize(self).map_err(|err| CryptError::RekeyUpdateError(err.to_string())) } /// Deserializes self from a set of bytes - pub fn deserialize_from>(drill: T) -> Result> { - bincode::deserialize(drill.as_ref()) - .map_err(|err| CryptError::DrillUpdateError(err.to_string())) + pub fn deserialize_from>(entropy_bank: T) -> Result> { + bincode::deserialize(entropy_bank.as_ref()) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string())) } } @@ -328,7 +334,7 @@ pub struct EntropyBank { } /// Returns the approximate number of bytes needed to serialize a Drill -pub const fn get_approx_serialized_drill_len() -> usize { +pub const fn get_approx_serialized_entropy_bank_len() -> usize { 4 + 8 + BYTES_PER_STORE + (DRILL_RANGE * 16 * 2) } diff --git a/citadel_crypt/src/fcm/mod.rs b/citadel_crypt/src/fcm/mod.rs index c783bbf05..9be161df2 100644 --- a/citadel_crypt/src/fcm/mod.rs +++ b/citadel_crypt/src/fcm/mod.rs @@ -1,3 +1,3 @@ -pub mod fcm_ratchet; /// opts pub mod keys; +pub mod ratchet; diff --git a/citadel_crypt/src/fcm/fcm_ratchet.rs b/citadel_crypt/src/fcm/ratchet.rs similarity index 60% rename from citadel_crypt/src/fcm/fcm_ratchet.rs rename to citadel_crypt/src/fcm/ratchet.rs index 10d57002b..8bbf67915 100644 --- a/citadel_crypt/src/fcm/fcm_ratchet.rs +++ b/citadel_crypt/src/fcm/ratchet.rs @@ -52,13 +52,13 @@ //! - [`PostQuantumContainer`](citadel_pqcrypto::PostQuantumContainer): PQ crypto operations //! - [`Ratchet`](crate::stacked_ratchet::Ratchet): Base ratchet trait -use crate::endpoint_crypto_container::EndpointRatchetConstructor; +use crate::endpoint_crypto_container::{ + AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, +}; use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; -use crate::stacked_ratchet::constructor::{AliceToBobTransferType, BobToAliceTransferType}; use crate::stacked_ratchet::Ratchet; use arrayvec::ArrayVec; -use citadel_pqcrypto::bytes_in_place::EzBuffer; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_pqcrypto::wire::{AliceToBobTransferParameters, BobToAliceTransferParameters}; use citadel_pqcrypto::PostQuantumContainer; @@ -66,7 +66,7 @@ use citadel_types::crypto::CryptoParameters; use citadel_types::crypto::SecurityLevel; use citadel_types::crypto::LARGEST_NONCE_LEN; use serde::{Deserialize, Serialize}; -use std::borrow::Cow; +use std::fmt::Debug; use std::sync::Arc; #[derive(Clone, Serialize, Deserialize)] @@ -78,101 +78,58 @@ pub struct ThinRatchet { impl ThinRatchet { /// decrypts using a custom nonce configuration pub fn decrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - drill.decrypt(pqc, contents) + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; + entropy_bank.decrypt(pqc, contents) } /// Encrypts the data into a Vec pub fn encrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - drill.encrypt(pqc, contents) + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; + entropy_bank.encrypt(pqc, contents) } } #[derive(Serialize, Deserialize)] pub struct ThinRatchetInner { - drill: EntropyBank, + entropy_bank: EntropyBank, pqc: PostQuantumContainer, } impl Ratchet for ThinRatchet { type Constructor = ThinRatchetConstructor; - fn get_cid(&self) -> u64 { - self.inner.drill.cid - } - - fn version(&self) -> u32 { - self.inner.drill.version - } - - fn has_verified_packets(&self) -> bool { - self.inner.pqc.has_verified_packets() - } - - fn reset_ara(&self) { - self.inner.pqc.reset_counters() - } - fn get_default_security_level(&self) -> SecurityLevel { SecurityLevel::Standard } - fn message_pqc_drill(&self, _idx: Option) -> (&PostQuantumContainer, &EntropyBank) { - (&self.inner.pqc, &self.inner.drill) + fn get_message_pqc_and_entropy_bank_at_layer( + &self, + idx: Option, + ) -> Result<(&PostQuantumContainer, &EntropyBank), CryptError> { + if let Some(idx) = idx { + if idx != 0 { + return Err(CryptError::OutOfBoundsError); + } + } + + Ok((&self.inner.pqc, &self.inner.entropy_bank)) } - fn get_scramble_drill(&self) -> &EntropyBank { - &self.inner.drill + fn get_scramble_pqc_and_entropy_bank(&self) -> (&PostQuantumContainer, &EntropyBank) { + // Thin Ratchets have no difference between scramble and message ratchets + self.get_message_pqc_and_entropy_bank_at_layer(None) + .expect("This should never fail") } fn get_next_constructor_opts(&self) -> Vec { - vec![ConstructorOpts::new_from_previous( + vec![ConstructorOpts::new_ratcheted( Some(self.inner.pqc.params), self.inner.pqc.get_chain().unwrap().clone(), )] } - fn protect_message_packet( - &self, - _security_level: Option, - header_len_bytes: usize, - packet: &mut T, - ) -> Result<(), CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - drill.protect_packet(pqc, header_len_bytes, packet) - } - - fn validate_message_packet, T: EzBuffer>( - &self, - _security_level: Option, - header: H, - packet: &mut T, - ) -> Result<(), CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - drill.validate_packet_in_place_split(pqc, header, packet) - } - - fn local_encrypt<'a, T: Into>>( - &self, - contents: T, - _security_level: SecurityLevel, - ) -> Result, CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - drill.local_encrypt(pqc, contents.into()) - } - - fn local_decrypt<'a, T: Into>>( - &self, - contents: T, - _security_level: SecurityLevel, - ) -> Result, CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - let contents = contents.into(); - if contents.is_empty() { - return Ok(vec![]); - } - drill.local_decrypt(pqc, contents) + fn message_ratchet_count(&self) -> usize { + 1 } } @@ -181,61 +138,53 @@ impl Ratchet for ThinRatchet { pub struct ThinRatchetConstructor { params: CryptoParameters, pqc: PostQuantumContainer, - drill: Option, + entropy_bank: Option, nonce: ArrayVec, cid: u64, version: u32, } +impl Debug for ThinRatchetConstructor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ThinRatchetConstructor") + .field("params", &self.params) + .field("cid", &self.cid) + .field("version", &self.version) + .finish() + } +} + impl EndpointRatchetConstructor for ThinRatchetConstructor { - fn new_alice( - mut opts: Vec, - cid: u64, - new_version: u32, - _security_level: Option, - ) -> Option { - ThinRatchetConstructor::new_alice(cid, new_version, opts.remove(0)) + type AliceToBobWireTransfer = FcmAliceToBobTransfer; + type BobToAliceWireTransfer = FcmBobToAliceTransfer; + + fn new_alice(opts: Vec, cid: u64, new_version: u32) -> Option { + ThinRatchetConstructor::new_alice_constructor(cid, new_version, opts.into_iter().next()?) } - fn new_bob( + fn new_bob>( _cid: u64, - _new_drill_vers: u32, - mut opts: Vec, - transfer: AliceToBobTransferType, - psks: &[Vec], + opts: Vec, + transfer: Self::AliceToBobWireTransfer, + psks: &[T], ) -> Option { - match transfer { - AliceToBobTransferType::Fcm(transfer) => { - ThinRatchetConstructor::new_bob(opts.remove(0), transfer, psks) - } - - _ => { - log::error!(target: "citadel", "Incompatible Ratchet Type passed! [X-43]"); - None - } - } + ThinRatchetConstructor::new_bob(opts.into_iter().next()?, transfer, psks) } - fn stage0_alice(&self) -> Option { - Some(AliceToBobTransferType::Fcm(self.stage0_alice()?)) + fn stage0_alice(&self) -> Option { + self.stage0_alice() } - fn stage0_bob(&self) -> Option { - Some(BobToAliceTransferType::Fcm(self.stage0_bob()?)) + fn stage0_bob(&mut self) -> Option { + self.stage0_bob() } - fn stage1_alice( + fn stage1_alice>( &mut self, - transfer: BobToAliceTransferType, - psks: &[Vec], + transfer: Self::BobToAliceWireTransfer, + psks: &[T], ) -> Result<(), CryptError> { - match transfer { - BobToAliceTransferType::Fcm(transfer) => self.stage1_alice(transfer, psks), - - _ => Err(CryptError::DrillUpdateError( - "Incompatible Ratchet Type passed! [X-44]".to_string(), - )), - } + self.stage1_alice(transfer, psks) } fn update_version(&mut self, version: u32) -> Option<()> { @@ -262,42 +211,60 @@ pub struct FcmAliceToBobTransfer { pub version: u32, } -#[derive(Serialize, Deserialize, Clone)] +impl AssociatedSecurityLevel for FcmAliceToBobTransfer { + fn security_level(&self) -> SecurityLevel { + SecurityLevel::Standard + } +} + +impl AssociatedCryptoParams for FcmAliceToBobTransfer { + fn crypto_params(&self) -> CryptoParameters { + self.params + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct FcmBobToAliceTransfer { params_tx: BobToAliceTransferParameters, - encrypted_drill_bytes: Vec, + encrypted_entropy_bank_bytes: Vec, +} + +impl AssociatedSecurityLevel for FcmBobToAliceTransfer { + fn security_level(&self) -> SecurityLevel { + SecurityLevel::Standard + } } impl ThinRatchetConstructor { /// FCM limits messages to 4Kb, so we need to use firesaber alone - pub fn new_alice(cid: u64, version: u32, opts: ConstructorOpts) -> Option { + pub fn new_alice_constructor(cid: u64, version: u32, opts: ConstructorOpts) -> Option { let params = opts.cryptography.unwrap_or_default(); let pqc = PostQuantumContainer::new_alice(opts).ok()?; Some(Self { params, pqc, - drill: None, + entropy_bank: None, nonce: EntropyBank::generate_public_nonce(params.encryption_algorithm), cid, version, }) } - pub fn new_bob( + pub fn new_bob>( opts: ConstructorOpts, transfer: FcmAliceToBobTransfer, - psks: &[Vec], + psks: &[T], ) -> Option { let params = transfer.params; let pqc = PostQuantumContainer::new_bob(opts, transfer.transfer_params, psks).ok()?; - let drill = + let entropy_bank = EntropyBank::new(transfer.cid, transfer.version, params.encryption_algorithm).ok()?; Some(Self { params, pqc, - drill: Some(drill), + entropy_bank: Some(entropy_bank), nonce: transfer.nonce, cid: transfer.cid, version: transfer.version, @@ -315,42 +282,45 @@ impl ThinRatchetConstructor { }) } - pub fn stage0_bob(&self) -> Option { + pub fn stage0_bob(&mut self) -> Option { Some(FcmBobToAliceTransfer { params_tx: self.pqc.generate_bob_to_alice_transfer().ok()?, - encrypted_drill_bytes: self + encrypted_entropy_bank_bytes: self .pqc - .encrypt(self.drill.as_ref()?.serialize_to_vec().ok()?, &self.nonce) + .encrypt( + self.entropy_bank.as_ref()?.serialize_to_vec().ok()?, + &self.nonce, + ) .ok()?, }) } - pub fn stage1_alice( + pub fn stage1_alice>( &mut self, transfer: FcmBobToAliceTransfer, - psks: &[Vec], + psks: &[T], ) -> Result<(), CryptError> { self.pqc .alice_on_receive_ciphertext(transfer.params_tx, psks) - .map_err(|err| CryptError::DrillUpdateError(err.to_string()))?; + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; let bytes = self .pqc - .decrypt(&transfer.encrypted_drill_bytes, &self.nonce) - .map_err(|err| CryptError::DrillUpdateError(err.to_string()))?; - let drill = EntropyBank::deserialize_from(&bytes[..])?; - self.drill = Some(drill); + .decrypt(&transfer.encrypted_entropy_bank_bytes, &self.nonce) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; + let entropy_bank = EntropyBank::deserialize_from(&bytes[..])?; + self.entropy_bank = Some(entropy_bank); Ok(()) } pub fn update_version(&mut self, version: u32) -> Option<()> { self.version = version; - self.drill.as_mut()?.version = version; + self.entropy_bank.as_mut()?.version = version; Some(()) } pub fn finish_with_custom_cid(mut self, cid: u64) -> Option { self.cid = cid; - self.drill.as_mut()?.cid = cid; + self.entropy_bank.as_mut()?.cid = cid; self.finish() } @@ -363,9 +333,9 @@ impl TryFrom for ThinRatchet { type Error = (); fn try_from(value: ThinRatchetConstructor) -> Result { - let drill = value.drill.ok_or(())?; + let entropy_bank = value.entropy_bank.ok_or(())?; let pqc = value.pqc; - let inner = ThinRatchetInner { drill, pqc }; + let inner = ThinRatchetInner { entropy_bank, pqc }; Ok(ThinRatchet { inner: Arc::new(inner), }) diff --git a/citadel_crypt/src/lib.rs b/citadel_crypt/src/lib.rs index a52ba371f..1861b1d88 100644 --- a/citadel_crypt/src/lib.rs +++ b/citadel_crypt/src/lib.rs @@ -55,9 +55,9 @@ pub mod prelude { /// For argon-related functionality pub mod argon; -/// An abstraction binding the drill and the PQC +/// An abstraction binding the entropy_bank and the PQC pub mod endpoint_crypto_container; -/// Organizes the different types of drills that can be used. Currently, there is only one: The Standard Drill +/// Organizes the different types of entropy_banks that can be used. Currently, there is only one: The Standard Drill pub mod entropy_bank; /// Contains the cryptographic primitives for handling FCM interactions on the network pub mod fcm; @@ -69,11 +69,13 @@ pub mod packet_vector; pub mod scramble; /// For secure byte handling pub mod secure_buffer; -/// This is a container for holding the drill and PQC, and is intended to replace the separate use of the drill/PQC +/// This is a container for holding the entropy_bank and PQC, and is intended to replace the separate use of the entropy_bank/PQC pub mod stacked_ratchet; /// Allows thread-pooled asynchronous and parallel file processing pub mod streaming_crypt_scrambler; +/// +pub mod ratchet_manager; pub mod sync_toggle; -/// Provides drill management, update, and versioning. This is what's exposed to the citadel_user api. The drills themselves are abstracted beneath +/// Provides entropy_bank management, update, and versioning. This is what's exposed to the citadel_user api. The entropy_banks themselves are abstracted beneath pub mod toolset; diff --git a/citadel_crypt/src/misc.rs b/citadel_crypt/src/misc.rs index 2cd966601..1aeeabbcd 100644 --- a/citadel_crypt/src/misc.rs +++ b/citadel_crypt/src/misc.rs @@ -50,13 +50,14 @@ use rand::thread_rng; use std::fmt::{Display, Formatter}; /// Default Error type for this crate +#[derive(Clone)] pub enum CryptError { /// Encrypt Error Encrypt(T), /// Decrypt Error Decrypt(T), /// Drill update error - DrillUpdateError(T), + RekeyUpdateError(T), /// Out of bounds OutOfBoundsError, /// This occurs if the byte-valued security level desired does not correspond to an actual [SecurityLevel] @@ -72,7 +73,7 @@ impl CryptError { match self { CryptError::Encrypt(s) => s.into(), CryptError::Decrypt(s) => s.into(), - CryptError::DrillUpdateError(s) => s.into(), + CryptError::RekeyUpdateError(s) => s.into(), CryptError::OutOfBoundsError => "[CryptError] Out of bounds exception".to_string(), CryptError::BadSecuritySetting => "[CryptError] Bad security setting".to_string(), } @@ -85,7 +86,7 @@ impl CryptError { match self { CryptError::Encrypt(s) => s.as_ref(), CryptError::Decrypt(s) => s.as_ref(), - CryptError::DrillUpdateError(s) => s.as_ref(), + CryptError::RekeyUpdateError(s) => s.as_ref(), CryptError::OutOfBoundsError => "[CryptError] Out of bounds exception", CryptError::BadSecuritySetting => "[CryptError] Bad security setting", } diff --git a/citadel_crypt/src/packet_vector.rs b/citadel_crypt/src/packet_vector.rs index 465cd2f7f..f9e454735 100644 --- a/citadel_crypt/src/packet_vector.rs +++ b/citadel_crypt/src/packet_vector.rs @@ -61,7 +61,7 @@ pub struct PacketVector { /// The group ID of this packet pub group_id: u64, /// The sequence is the position in the wave ID. Repeating sequences CANNOT exist, and as such, - /// the drill generator must ensure all values in the port range are non-repeating. + /// the entropy_bank generator must ensure all values in the port range are non-repeating. /// /// /// A wave is a set of packets in scrambled order in respect to the in/out ports. There are a maximum @@ -81,7 +81,7 @@ pub struct PacketVector { /// /// * `true_sequence`: The original true sequence number. /// * `group_id`: The group ID of the packet. -/// * `drill`: The entropy bank used for port scrambling. +/// * `entropy_bank`: The entropy bank used for port scrambling. /// /// # Returns /// @@ -89,13 +89,16 @@ pub struct PacketVector { pub fn generate_packet_vector( true_sequence: usize, group_id: u64, - drill: &EntropyBank, + entropy_bank: &EntropyBank, ) -> PacketVector { // To get the wave_id, we must floor divide the true sequence by the port range. The remainder is the sequence - let port_range = &drill.get_multiport_width(); + let port_range = &entropy_bank.get_multiport_width(); let (true_wave_id, relative_sequence) = true_sequence.div_mod_floor(port_range); - // To scramble the true values, we get their corresponding values in the drill - let (local_port, remote_port) = *drill.scramble_mappings.get(relative_sequence).unwrap(); + // To scramble the true values, we get their corresponding values in the entropy_bank + let (local_port, remote_port) = *entropy_bank + .scramble_mappings + .get(relative_sequence) + .unwrap(); PacketVector { group_id, @@ -106,14 +109,14 @@ pub fn generate_packet_vector( } } -/// Generates packet coordinates from wave ID, source port, local port, and scramble drill. +/// Generates packet coordinates from wave ID, source port, local port, and scramble entropy_bank. /// /// # Parameters /// /// * `wave_id`: The wave ID of the packet. /// * `src_port`: The source port of the packet. /// * `local_port`: The local port of the packet. -/// * `scramble_drill`: The entropy bank used for port scrambling. +/// * `scramble_entropy_bank`: The entropy bank used for port scrambling. /// /// # Returns /// @@ -123,11 +126,11 @@ pub fn generate_packet_coordinates_inv( wave_id: u32, src_port: u16, local_port: u16, - scramble_drill: &EntropyBank, + scramble_entropy_bank: &EntropyBank, ) -> Option { - for (idx, (in_port, out_port)) in scramble_drill.scramble_mappings.iter().enumerate() { + for (idx, (in_port, out_port)) in scramble_entropy_bank.scramble_mappings.iter().enumerate() { if *in_port == src_port && *out_port == local_port { - let port_range = scramble_drill.scramble_mappings.len(); + let port_range = scramble_entropy_bank.scramble_mappings.len(); let true_position = (wave_id as usize * port_range) + idx; return Some(true_position); } diff --git a/citadel_crypt/src/ratchet_manager.rs b/citadel_crypt/src/ratchet_manager.rs new file mode 100644 index 000000000..f14a6d03c --- /dev/null +++ b/citadel_crypt/src/ratchet_manager.rs @@ -0,0 +1,690 @@ +//! Ratchet Manager - Secure Key Ratcheting Protocol Implementation +//! +//! This module implements a secure key ratcheting protocol manager that handles the +//! communication and state management between two peers (Alice and Bob) during key +//! updates and ratcheting operations. +//! +//! # Features +//! - Manages bidirectional communication for key ratcheting between peers +//! - Handles state synchronization during ratchet updates +//! - Supports Pre-Shared Keys (PSKs) for additional security +//! - Implements truncation of old ratchet states +//! - Provides safe access to underlying crypto container +//! +//! # Usage +//! The RatchetManager requires a Sink for sending messages, a Stream for receiving messages, +//! and a Ratchet implementation. It manages the full key ratcheting protocol between peers: +//! +//! ```rust,no_run +//! use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; +//! use citadel_crypt::ratchet_manager::{RatchetManager, RatchetMessage}; +//! +//! async fn example( +//! sender: S, +//! receiver: I, +//! container: PeerSessionCrypto, +//! psks: &[Vec] +//! ) -> Result<(), Box> +//! where +//! S: Sink + Unpin, +//! I: Stream + Unpin, +//! R: Ratchet, +//! { +//! let mut manager = RatchetManager::new(sender, receiver, container, psks); +//! // Trigger a ratchet update +//! manager.rekey().await?; +//! Ok(()) +//! } +//! ``` +//! +//! # Important Notes +//! - The ratcheting protocol is asynchronous and requires both peers to complete their respective roles +//! - The protocol ensures forward secrecy through regular key updates +//! - Truncation of old ratchet states helps manage memory usage while maintaining security +//! +//! # Related Components +//! - [`PeerSessionCrypto`]: The underlying crypto container managed by this component +//! - [`Ratchet`]: The trait defining the ratcheting behavior +//! - [`EndpointRatchetConstructor`]: Handles the construction of new ratchet states +//! + +use crate::endpoint_crypto_container::{ + EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, +}; +use crate::misc::CryptError; +use crate::stacked_ratchet::Ratchet; +use atomic::Atomic; +use bytemuck::NoUninit; +use citadel_io::tokio::sync::Mutex as TokioMutex; +use citadel_io::tokio_stream::Stream; +use citadel_io::{Mutex, RwLock}; +use futures::{Sink, SinkExt, StreamExt}; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +pub struct RatchetManager +where + R: Ratchet, +{ + sender: Arc>, + receiver: Arc>>, + container: Arc>>, + cid: u64, + psks: Arc>>, + role: Arc>, + constructor: Arc>>, + is_initiator: bool, + state: Arc>, + local_listener: + Arc>>>>, +} + +impl Clone for RatchetManager { + fn clone(&self) -> Self { + Self { + sender: self.sender.clone(), + receiver: self.receiver.clone(), + container: self.container.clone(), + cid: self.cid, + psks: self.psks.clone(), + role: self.role.clone(), + constructor: self.constructor.clone(), + is_initiator: self.is_initiator, + state: self.state.clone(), + local_listener: self.local_listener.clone(), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, NoUninit)] +#[repr(u8)] +pub enum RekeyState { + Running, + Halted, + Idle, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, NoUninit)] +#[repr(u8)] +pub enum RekeyRole { + Idle, + Leader, + Loser, +} + +#[derive(Serialize, Deserialize)] +pub enum RatchetMessage { + AliceToBob(Vec), // Serialized transfer + BobToAlice(Vec), // Serialized transfer + Truncate(u32), // Version to truncate + LeaderCanFinish, + LoserCanFinish, +} + +impl Debug for RatchetMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RatchetMessage::AliceToBob(_) => write!(f, "AliceToBob"), + RatchetMessage::BobToAlice(_) => write!(f, "BobToAlice"), + RatchetMessage::Truncate(_) => write!(f, "Truncate"), + RatchetMessage::LeaderCanFinish => write!(f, "LeaderCanFinish"), + RatchetMessage::LoserCanFinish => write!(f, "LoserCanFinish"), + } + } +} + +impl RatchetManager +where + S: Sink + Send + Unpin + 'static, + I: Stream + Send + Unpin + 'static, + R: Ratchet, + >::Error: std::fmt::Debug, +{ + pub fn new>( + sender: S, + receiver: I, + container: PeerSessionCrypto, + psks: &[T], + ) -> Self { + let cid = container.toolset.cid; + let is_initiator = container.local_is_initiator; + + let this = Self { + sender: Arc::new(TokioMutex::new(sender)), + receiver: Arc::new(Mutex::new(Some(receiver))), + container: Arc::new(RwLock::new(container)), + cid, + is_initiator, + constructor: Arc::new(Mutex::new(None)), + psks: Arc::new(psks.iter().map(|psk| psk.as_ref().to_vec()).collect()), + role: Arc::new(Atomic::new(RekeyRole::Idle)), + state: Arc::new(Atomic::new(RekeyState::Idle)), + local_listener: Arc::new(Mutex::new(None)), + }; + + this.clone().spawn_rekey_process(); + this + } + + /// Returns true if the re-key was a success, false if no re-key was needed + pub async fn trigger_rekey(&mut self) -> Result { + if self.state() == RekeyState::Halted { + return Err(CryptError::RekeyUpdateError( + "Rekey process is halted".to_string(), + )); + } + + let constructor = { self.container.write().get_next_constructor(false) }; + + if let Some(constructor) = constructor { + let transfer = constructor.stage0_alice().ok_or_else(|| { + CryptError::RekeyUpdateError("Failed to get initial transfer".to_string()) + })?; + + let serialized = bincode::serialize(&transfer) + .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + + self.sender + .lock() + .await + .send(RatchetMessage::AliceToBob(serialized)) + .await + .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + log::debug!(target: "citadel", "Client {} sent initial AliceToBob transfer", self.cid); + + *self.constructor.lock() = Some(constructor); + let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); + + *self.local_listener.lock() = Some(tx); + let err = rx.await.map_err(|_| { + CryptError::RekeyUpdateError("Failed to wait for local listener".to_string()) + })?; + + if let Some(err) = err { + return Err(err); + } + + Ok(true) + } else { + Ok(false) + } + } + + fn spawn_rekey_process(self) { + struct DropWrapper { + state: Arc>, + } + + impl Drop for DropWrapper { + fn drop(&mut self) { + self.state.store(RekeyState::Halted, Ordering::Relaxed); + } + } + + let task = async move { + let _drop_wrapper = DropWrapper { + state: self.state.clone(), + }; + let mut listener = { self.receiver.lock().take().unwrap() }; + loop { + self.set_state(RekeyState::Running); + let result = self.rekey(&mut listener).await; + self.set_state(RekeyState::Idle); + + let err = result.err(); + + if let Some(notifier) = self.local_listener.lock().take() { + let _ = notifier.send(err.clone()); + } + + if let Some(err) = err { + log::error!("cid {} rekey error: {err:?}", self.cid); + break; + } + } + }; + + drop(citadel_io::tokio::task::spawn(task)); + } + + /// Runs a single round of re-keying, listening to events and returning + /// once a single re-key occurs. This function is intended to be used in a loop + /// to continuously be ready for re-keying. + async fn rekey(&self, receiver: &mut I) -> Result<(), CryptError> { + let is_initiator = self.is_initiator; + let mut completed_as_leader = false; + let mut completed_as_loser = false; + + while !((!is_initiator && completed_as_leader) || (is_initiator && completed_as_loser)) { + let msg = receiver.next().await; + log::debug!(target: "citadel", "Client {} received message {msg:?}", self.cid); + match msg { + Some(RatchetMessage::AliceToBob(transfer_data)) => { + log::debug!("cid {} received AliceToBob", self.cid); + + // Process the AliceToBob message as Bob + let transfer = bincode::deserialize(&transfer_data) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; + + let status = { + let mut container = self.container.write(); + // Get next_opts from the container + let next_opts = container + .get_ratchet(None) + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Failed to get stacked ratchet".to_string(), + ) + })? + .get_next_constructor_opts(); + + // Create Bob constructor + let bob_constructor = + >::new_bob( + self.cid, next_opts, transfer, &self.psks, + ) + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Failed to create bob constructor".to_string(), + ) + })?; + + container.update_sync_safe( + bob_constructor, + self.role() != RekeyRole::Loser, + self.cid, + false, + )? + }; + + match status { + KemTransferStatus::Some(transfer, _) => { + let serialized = bincode::serialize(&transfer) + .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + self.sender + .lock() + .await + .send(RatchetMessage::BobToAlice(serialized)) + .await + .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + self.set_role(RekeyRole::Loser); + log::debug!( + target: "citadel", + "Client {} is {:?}. Sent BobToAlice", + self.cid, + RekeyRole::Loser + ); + } + KemTransferStatus::Contended => { + // The package that we received did not result in a re-key. OUR package will result in a re-key. + // Therefore, we will wait for the adjacent node to drive us to completion so we both have the same ratchet + self.set_role(RekeyRole::Leader); + log::debug!("cid {} is {:?}. contention detected. We will wait for the adjacent node to drive us to completion", self.cid, RekeyRole::Leader); + } + _ => { + log::warn!( + "cid {} unexpected status for AliceToBob Transfer: {status:?}", + self.cid + ); + } + } + } + Some(RatchetMessage::BobToAlice(transfer_data)) => { + // Allow Leader if contention, or Idle if no contention + if self.role() == RekeyRole::Loser { + return Err(CryptError::RekeyUpdateError(format!( + "Unexpected BobToAlice message since our role is not Leader, but {:?}", + self.role() + ))); + } + + let mut constructor = { self.constructor.lock().take() }; + + log::debug!(target: "citadel", "Client {} received BobToAlice", self.cid); + if let Some(mut alice_constructor) = constructor.take() { + let transfer = bincode::deserialize(&transfer_data).map_err(|e| { + CryptError::RekeyUpdateError(format!( + "Failed to deserialize transfer: {e}" + )) + })?; + + alice_constructor.stage1_alice(transfer, &self.psks)?; + let status = { + self.container.write().update_sync_safe( + alice_constructor, + false, + self.cid, + true, + )? + }; + + let truncation_required = status.requires_truncation(); + + let expected_status = matches!( + status, + KemTransferStatus::StatusNoTransfer(..) | KemTransferStatus::Contended + ); + + if expected_status { + if let Some(version_to_truncate) = truncation_required { + { + self.container + .write() + .deregister_oldest_stacked_ratchet(version_to_truncate)?; + } + + self.sender + .lock() + .await + .send(RatchetMessage::Truncate(version_to_truncate)) + .await + .map_err(|err| { + CryptError::RekeyUpdateError(format!("{err:?}")) + })?; + // We need to wait to be marked as complete + } else { + // Send TruncateAck to Bob so he can finish + self.sender + .lock() + .await + .send(RatchetMessage::LoserCanFinish) + .await + .map_err(|err| { + CryptError::RekeyUpdateError(format!("{err:?}")) + })?; + } + } else { + log::warn!(target:"citadel", "Client {} unexpected status as Leader: {status:?}", self.cid); + } + } else { + return Err(CryptError::RekeyUpdateError( + "Unexpected BobToAlice message with no loaded local constructor" + .to_string(), + )); + } + } + + Some(RatchetMessage::Truncate(version_to_truncate)) => { + // Allow Loser if contention, or Idle if no contention + if self.role() == RekeyRole::Leader { + return Err(CryptError::RekeyUpdateError(format!( + "Unexpected Truncate message since our role is not Bob, but {:?}", + self.role() + ))); + } + + log::debug!(target: "citadel", "Client {} received Truncate", self.cid); + + { + let mut container = self.container.write(); + container.deregister_oldest_stacked_ratchet(version_to_truncate)?; + + container.post_alice_stage1_or_post_stage1_bob(); + let _ = container.maybe_unlock(false); + } + + completed_as_loser = true; + + self.sender + .lock() + .await + .send(RatchetMessage::LeaderCanFinish) + .await + .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + } + Some(RatchetMessage::LeaderCanFinish) => { + // Allow Leader if contention, or Idle if no contention + if self.role() == RekeyRole::Loser { + return Err(CryptError::RekeyUpdateError(format!( + "Unexpected AliceCanFinish message since our role is not Bob, but {:?}", + self.role() + ))); + } + log::debug!(target: "citadel", "Client {} received LeaderCanFinish 00", self.cid); + + { + let mut container = self.container.write(); + container.post_alice_stage1_or_post_stage1_bob(); + let _ = container.maybe_unlock(false); + } + + log::debug!("cid {} received LeaderCanFinish", self.cid); + completed_as_leader = true; + } + + Some(RatchetMessage::LoserCanFinish) => { + // Allow Loser if contention, or Idle if no contention + if self.role() == RekeyRole::Leader { + return Err(CryptError::RekeyUpdateError( + format!("Unexpected LoserCanFinish message since our role is not Loser, but {:?}", self.role()) + )); + } + + log::debug!(target: "citadel", "Client {} received LoserCanFinish", self.cid); + + { + let mut container = self.container.write(); + container.post_alice_stage1_or_post_stage1_bob(); + let _ = container.maybe_unlock(false); + } + completed_as_loser = true; + + // Send a LeaderCanFinish to unlock them + self.sender + .lock() + .await + .send(RatchetMessage::LeaderCanFinish) + .await + .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + } + + None => { + return Err(CryptError::RekeyUpdateError( + "Unexpected end of stream".to_string(), + )); + } + } + } + + log::debug!( + target: "citadel", + "Client {} completed re-key. Alice: {}, Bob: {}. Final version: {}. Is initiator: {}", + self.cid, + completed_as_leader, + completed_as_loser, + self.get_ratchet(None).unwrap().version(), + is_initiator + ); + + debug_assert_eq!( + completed_as_leader, !is_initiator, + "Client {} completed wrong role. Is initiator: {is_initiator}, Completed as Leader: {completed_as_leader}", + self.cid + ); + + log::debug!(target: "citadel", "*** cid {} rekey completed", self.cid); + + Ok(()) + } + + pub fn get_ratchet(&self, version: Option) -> Option { + self.container.read().get_ratchet(version).cloned() + } + + pub fn role(&self) -> RekeyRole { + self.role.load(Ordering::Relaxed) + } + + fn set_role(&self, role: RekeyRole) { + self.role.store(role, Ordering::Relaxed); + } + + pub fn state(&self) -> RekeyState { + self.state.load(Ordering::Relaxed) + } + + fn set_state(&self, state: RekeyState) { + self.state.store(state, Ordering::Relaxed); + } +} + +#[cfg(test)] +mod racy { + use crate::endpoint_crypto_container::no_race::TEST_PSKS; + use crate::ratchet_manager::{RatchetManager, RatchetMessage, RekeyRole}; + use crate::stacked_ratchet::Ratchet; + use citadel_io::tokio; + use citadel_types::prelude::{EncryptionAlgorithm, KemAlgorithm, SecurityLevel}; + use futures::{Sink, Stream}; + use rstest::rstest; + use std::time::Duration; + + async fn run_round_racy< + S: Sink + Unpin + Send + 'static, + I: Stream + Unpin + Send + 'static, + R: Ratchet, + >( + container_0: RatchetManager, + container_1: RatchetManager, + container_0_delay: Option, + ) where + >::Error: std::fmt::Debug, + { + let cid_0 = container_0.cid; + let cid_1 = container_1.cid; + + let (_start_version, _next_version) = + crate::endpoint_crypto_container::no_race::pre_round_assertions( + &*container_0.container.read(), + cid_0, + &*container_1.container.read(), + cid_1, + ); + + let task = |mut container: RatchetManager, delay: Option| async move { + if let Some(delay) = delay { + tokio::time::sleep(delay).await; + } + container.trigger_rekey().await + }; + + // Randomly assign a delay to Alice or Bob, if applicable + let (delay_0, delay_1) = if let Some(delay) = container_0_delay { + if delay.as_millis() % 2 == 0 { + (Some(delay), None) + } else { + (None, Some(delay)) + } + } else { + (None, None) + }; + + // Spawn Alice's task + let alice_handle = tokio::spawn(task(container_0.clone(), delay_0)); + + // Spawn Bob's task + let bob_handle = tokio::spawn(task(container_1.clone(), delay_1)); + + // Wait for both tasks to complete + let (alice_result, bob_result) = tokio::join!(alice_handle, bob_handle); + + // Update original containers with final state + let rekey_0_res = alice_result.unwrap().unwrap(); + let rekey_1_res = bob_result.unwrap().unwrap(); + + assert!(rekey_0_res, "Alice failed to rekey"); + assert!(rekey_1_res, "Bob failed to rekey"); + + // Verify final state + let alice_ratchet = container_0.get_ratchet(None).unwrap(); + let bob_ratchet = container_1.get_ratchet(None).unwrap(); + assert_eq!(alice_ratchet.version(), bob_ratchet.version()); + + let alice_ratchet_version = alice_ratchet.version(); + + crate::endpoint_crypto_container::no_race::ratchet_encrypt_decrypt_test( + &*container_0.container.read(), + cid_0, + &*container_1.container.read(), + cid_1, + alice_ratchet_version, + ); + } + + #[rstest] + #[timeout(std::time::Duration::from_secs(30))] + #[tokio::test] + async fn test_endpoint_container_racy_basic() { + citadel_logging::setup_log(); + let security_level = SecurityLevel::Standard; + + let (alice_container, bob_container) = + crate::endpoint_crypto_container::no_race::setup_endpoint_containers( + security_level, + EncryptionAlgorithm::AES_GCM_256, + KemAlgorithm::Kyber, + ); + + let (tx_alice, rx_bob) = futures::channel::mpsc::unbounded(); + let (tx_bob, rx_alice) = futures::channel::mpsc::unbounded(); + + let alice_manager = RatchetManager::new(tx_alice, rx_alice, alice_container, TEST_PSKS); + let bob_manager = RatchetManager::new(tx_bob, rx_bob, bob_container, TEST_PSKS); + + const ROUNDS: usize = 100; + for _ in 0..ROUNDS { + run_round_racy(alice_manager.clone(), bob_manager.clone(), None).await; + } + } + + #[rstest] + #[timeout(std::time::Duration::from_secs(30))] + #[tokio::test(flavor = "multi_thread")] + async fn test_endpoint_container_racy_with_random_start_lag() { + citadel_logging::setup_log(); + let security_level = SecurityLevel::Standard; + + let (alice_container, bob_container) = + crate::endpoint_crypto_container::no_race::setup_endpoint_containers( + security_level, + EncryptionAlgorithm::AES_GCM_256, + KemAlgorithm::Kyber, + ); + + let (tx_alice, rx_bob) = futures::channel::mpsc::unbounded(); + let (tx_bob, rx_alice) = futures::channel::mpsc::unbounded(); + + let alice_manager = RatchetManager::new(tx_alice, rx_alice, alice_container, TEST_PSKS); + let bob_manager = RatchetManager::new(tx_bob, rx_bob, bob_container, TEST_PSKS); + + let mut initiator_leader_count = 0; + let mut non_initiator_leader_count = 0; + + const ROUNDS: usize = 100; + for _ in 0..ROUNDS { + let delay = rand::random::() % 100; + let delay = Duration::from_millis(delay as u64); + run_round_racy(alice_manager.clone(), bob_manager.clone(), Some(delay)).await; + + if alice_manager.role() == RekeyRole::Leader { + if alice_manager.is_initiator { + initiator_leader_count += 1; + } else { + non_initiator_leader_count += 1; + } + } + + if bob_manager.role() == RekeyRole::Leader { + if bob_manager.is_initiator { + initiator_leader_count += 1; + } else { + non_initiator_leader_count += 1; + } + } + } + + log::info!("initiator_leader_count: {initiator_leader_count}, non_initiator_leader_count: {non_initiator_leader_count}"); + } +} diff --git a/citadel_crypt/src/scramble/crypt_splitter.rs b/citadel_crypt/src/scramble/crypt_splitter.rs index 073ff832d..f8d1a412e 100644 --- a/citadel_crypt/src/scramble/crypt_splitter.rs +++ b/citadel_crypt/src/scramble/crypt_splitter.rs @@ -116,7 +116,7 @@ pub fn calculate_aes_gcm_plaintext_length_from_ciphertext_length( #[allow(clippy::too_many_arguments)] pub fn generate_scrambler_metadata>( - msg_drill: &EntropyBank, + msg_entropy_bank: &EntropyBank, plain_text: T, header_size_bytes: usize, security_level: SecurityLevel, @@ -135,7 +135,7 @@ pub fn generate_scrambler_metadata>( let max_packet_payload_size = get_max_packet_size(enx, sig_alg, security_level); let overhead = max_packet_payload_size - MAX_WAVEFORM_PACKET_SIZE; - let max_packets_per_wave = msg_drill.get_multiport_width(); + let max_packets_per_wave = msg_entropy_bank.get_multiport_width(); //let aes_gcm_overhead = get_aes_gcm_overhead(); // the below accounts for the stretch in size as we map n plaintext bytes to calculate_aes_gcm_output_length(n) bytes // Since we run the encryption algorithm once per wave, to get the number of plaintext bytes per wave we need, multiple the above by the max packets per wave and subtract @@ -195,7 +195,7 @@ pub fn generate_scrambler_metadata>( #[allow(clippy::too_many_arguments)] fn get_scramble_encrypt_config<'a, R: Ratchet>( - hyper_ratchet: &'a R, + stacked_ratchet: &'a R, plain_text: &'a [u8], header_size_bytes: usize, security_level: SecurityLevel, @@ -212,10 +212,11 @@ fn get_scramble_encrypt_config<'a, R: Ratchet>( ), CryptError, > { - let (msg_pqc, msg_drill) = hyper_ratchet.message_pqc_drill(None); - let scramble_drill = hyper_ratchet.get_scramble_drill(); + let (msg_pqc, msg_entropy_bank) = + stacked_ratchet.get_message_pqc_and_entropy_bank_at_layer(None)?; + let scramble_entropy_bank = stacked_ratchet.get_scramble_pqc_and_entropy_bank(); let cfg = generate_scrambler_metadata( - msg_drill, + msg_entropy_bank, plain_text, header_size_bytes, security_level, @@ -226,7 +227,7 @@ fn get_scramble_encrypt_config<'a, R: Ratchet>( transfer_type, empty_transfer, )?; - Ok((cfg, msg_drill, msg_pqc, scramble_drill)) + Ok((cfg, msg_entropy_bank, msg_pqc, scramble_entropy_bank.1)) } /// Each packet contains an empty array open to inscription of a header coupled with a ciphertext @@ -245,7 +246,7 @@ pub struct PacketCoordinate { pub fn par_scramble_encrypt_group, R: Ratchet, F, const N: usize>( plain_text: T, security_level: SecurityLevel, - hyper_ratchet: &R, + stacked_ratchet: &R, static_aux_ratchet: &R, header_size_bytes: usize, target_cid: u64, @@ -276,8 +277,8 @@ where plain_text = Cow::Owned(local_encrypted); } - let (mut cfg, msg_drill, msg_pqc, scramble_drill) = get_scramble_encrypt_config( - hyper_ratchet, + let (mut cfg, msg_entropy_bank, msg_pqc, scramble_entropy_bank) = get_scramble_encrypt_config( + stacked_ratchet, &plain_text, header_size_bytes, security_level, @@ -301,9 +302,9 @@ where wave_idx, bytes_to_encrypt_for_this_wave, &cfg, - msg_drill, + msg_entropy_bank, msg_pqc, - scramble_drill, + scramble_entropy_bank, target_cid, object_id, header_size_bytes, @@ -357,15 +358,15 @@ fn scramble_encrypt_wave( wave_idx: usize, bytes_to_encrypt_for_this_wave: &[u8], cfg: &GroupReceiverConfig, - msg_drill: &EntropyBank, + msg_entropy_bank: &EntropyBank, msg_pqc: &PostQuantumContainer, - scramble_drill: &EntropyBank, + scramble_entropy_bank: &EntropyBank, target_cid: u64, object_id: ObjectId, header_size_bytes: usize, header_inscriber: impl Fn(&PacketVector, &EntropyBank, ObjectId, u64, &mut BytesMut) + Send + Sync, ) -> Vec<(usize, PacketCoordinate)> { - let ciphertext = msg_drill + let ciphertext = msg_entropy_bank .encrypt(msg_pqc, bytes_to_encrypt_for_this_wave) .unwrap(); @@ -378,8 +379,15 @@ fn scramble_encrypt_wave( BytesMut::with_capacity(ciphertext_packet_bytes.len() + header_size_bytes); let true_packet_sequence = (wave_idx * cfg.max_packets_per_wave as usize) + relative_packet_idx; - let vector = generate_packet_vector(true_packet_sequence, cfg.group_id, scramble_drill); - header_inscriber(&vector, scramble_drill, object_id, target_cid, &mut packet); + let vector = + generate_packet_vector(true_packet_sequence, cfg.group_id, scramble_entropy_bank); + header_inscriber( + &vector, + scramble_entropy_bank, + object_id, + target_cid, + &mut packet, + ); packet.put(ciphertext_packet_bytes); (true_packet_sequence, PacketCoordinate { packet, vector }) }) @@ -441,6 +449,8 @@ pub enum GroupReceiverStatus { WAVE_COMPLETE(u32), /// A set of true_sequences that need retransmission NEEDS_RETRANSMISSION(u32), + /// Bad ratchet + CORRUPT_RATCHET(String), } /// A device used for reconstructing Groups. It is meant for the receiving end. For receiver ends, the use @@ -579,7 +589,7 @@ impl GroupReceiver { /// /// The max_payload_size does not account for the packet's header /// - /// The drill is needed in order to get the multiport width (determines max packets per wave) + /// The entropy_bank is needed in order to get the multiport width (determines max packets per wave) #[allow(unused_results)] pub fn new(cfg: GroupReceiverConfig, wave_timeout_ms: usize, group_timeout_ms: usize) -> Self { use bitvec::prelude::*; @@ -664,7 +674,7 @@ impl GroupReceiver { _group_id: u64, true_sequence: usize, wave_id: u32, - hyper_ratchet: &R, + stacked_ratchet: &R, packet: T, ) -> GroupReceiverStatus { let packet = packet.as_ref(); @@ -726,9 +736,17 @@ impl GroupReceiver { if wave_store.packets_received == wave_store.packets_in_wave { let ciphertext_bytes_for_this_wave = &wave_store.ciphertext_buffer[..wave_store.bytes_written]; - let (msg_pqc, msg_drill) = hyper_ratchet.message_pqc_drill(None); + let (msg_pqc, msg_entropy_bank) = match stacked_ratchet + .get_message_pqc_and_entropy_bank_at_layer(None) + { + Ok((msg_pqc, msg_entropy_bank)) => (msg_pqc, msg_entropy_bank), + Err(err) => { + log::error!(target: "citadel", "Unable to get message pqc and entropy bank. Reason: {err:?}"); + return GroupReceiverStatus::CORRUPT_RATCHET(err.into_string()); + } + }; - match msg_drill.decrypt(msg_pqc, ciphertext_bytes_for_this_wave) { + match msg_entropy_bank.decrypt(msg_pqc, ciphertext_bytes_for_this_wave) { Ok(plaintext) => { let plaintext = plaintext.as_slice(); diff --git a/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs b/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs index 0be1e575f..efb635524 100644 --- a/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs +++ b/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs @@ -246,7 +246,7 @@ mod tests { #[test] #[should_panic] fn partitioned_sec_buffer_0() { - citadel_logging::setup_log_no_panic_hook(); + citadel_logging::should_panic_test(); let _ = PartitionedSecBuffer::<0>::new().unwrap(); } @@ -262,7 +262,7 @@ mod tests { #[test] #[should_panic] fn partitioned_sec_buffer_1_improper() { - citadel_logging::setup_log_no_panic_hook(); + citadel_logging::should_panic_test(); let mut buf = PartitionedSecBuffer::<1>::new().unwrap(); buf.reserve_partition(1, 10).unwrap(); } @@ -270,7 +270,7 @@ mod tests { #[test] #[should_panic] fn partitioned_sec_buffer_1_improper_2() { - citadel_logging::setup_log_no_panic_hook(); + citadel_logging::should_panic_test(); let mut buf = PartitionedSecBuffer::<1>::new().unwrap(); buf.reserve_partition(0, 10).unwrap(); buf.partition_window(1).unwrap().fill(1); @@ -307,7 +307,7 @@ mod tests { #[test] #[should_panic] fn partitioned_sec_buffer_2_improper() { - citadel_logging::setup_log_no_panic_hook(); + citadel_logging::should_panic_test(); let mut buf = PartitionedSecBuffer::<2>::new().unwrap(); //buf.reserve_partition(0, 10).unwrap(); buf.reserve_partition(1, 3).unwrap(); diff --git a/citadel_crypt/src/secure_buffer/sec_packet.rs b/citadel_crypt/src/secure_buffer/sec_packet.rs index 5ac5783c7..2a2c255e5 100644 --- a/citadel_crypt/src/secure_buffer/sec_packet.rs +++ b/citadel_crypt/src/secure_buffer/sec_packet.rs @@ -17,8 +17,10 @@ //! ```rust //! use citadel_crypt::secure_buffer::sec_packet::SecureMessagePacket; //! +//! const N: usize = 32; +//! //! // Create a new packet -//! let mut packet = SecureMessagePacket::<32>::new().unwrap(); +//! let mut packet = SecureMessagePacket::::new().unwrap(); //! //! // Write payload first //! packet.write_payload(64, |buf| { @@ -28,13 +30,13 @@ //! //! // Write header second //! packet.write_header(|buf| { -//! buf[0] = 1; // packet type +//! buf.copy_from_slice(&[0u8; N]); //! Ok(()) //! }).unwrap(); //! //! // Write extension last and get final bytes -//! let bytes = packet.write_payload_extension(32, |buf| { -//! buf.copy_from_slice(&[0u8; 32]); +//! let bytes = packet.write_payload_extension(10, |buf| { +//! buf.copy_from_slice(&[0u8; 10]); //! Ok(()) //! }).unwrap(); //! ``` diff --git a/citadel_crypt/src/stacked_ratchet.rs b/citadel_crypt/src/stacked_ratchet.rs index b1b8a3a8e..eade07518 100644 --- a/citadel_crypt/src/stacked_ratchet.rs +++ b/citadel_crypt/src/stacked_ratchet.rs @@ -61,7 +61,6 @@ use crate::endpoint_crypto_container::EndpointRatchetConstructor; use crate::entropy_bank::EntropyBank; -use crate::fcm::fcm_ratchet::ThinRatchet; use crate::misc::CryptError; use crate::stacked_ratchet::constructor::StackedRatchetConstructor; use bytes::BytesMut; @@ -87,44 +86,100 @@ pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + ' type Constructor: EndpointRatchetConstructor + Serialize + for<'a> Deserialize<'a>; /// Returns the client ID - fn get_cid(&self) -> u64; + fn get_cid(&self) -> u64 { + self.get_message_pqc_and_entropy_bank_at_layer(None) + .expect("StackedRatchet::get_cid") + .1 + .cid + } /// Returns the version - fn version(&self) -> u32; + fn version(&self) -> u32 { + self.get_message_pqc_and_entropy_bank_at_layer(None) + .expect("StackedRatchet::version") + .1 + .version + } /// Determines if any of the ratchets have verified packets - fn has_verified_packets(&self) -> bool; + fn has_verified_packets(&self) -> bool { + let max = self.message_ratchet_count(); + for n in 0..max { + if let Ok((pqc, _entropy_bank)) = + self.get_message_pqc_and_entropy_bank_at_layer(Some(n)) + { + if pqc.has_verified_packets() { + return true; + } + } + } + + self.get_scramble_pqc_and_entropy_bank() + .0 + .has_verified_packets() + } /// Resets the anti-replay attack counters - fn reset_ara(&self); + fn reset_ara(&self) { + let max = self.message_ratchet_count(); + for n in 0..max { + if let Ok((pqc, _entropy_bank)) = + self.get_message_pqc_and_entropy_bank_at_layer(Some(n)) + { + pqc.reset_counters(); + } + } + + self.get_scramble_pqc_and_entropy_bank().0.reset_counters() + } /// Returns the default security level fn get_default_security_level(&self) -> SecurityLevel; - /// Returns the message PQC and drill for the specified index - fn message_pqc_drill(&self, idx: Option) -> (&PostQuantumContainer, &EntropyBank); + /// Returns the message PQC and entropy_bank for the specified index + fn get_message_pqc_and_entropy_bank_at_layer( + &self, + idx: Option, + ) -> Result<(&PostQuantumContainer, &EntropyBank), CryptError>; - /// Returns the scramble drill - fn get_scramble_drill(&self) -> &EntropyBank; + /// Returns the scramble entropy_bank + fn get_scramble_pqc_and_entropy_bank(&self) -> (&PostQuantumContainer, &EntropyBank); /// Returns the next constructor options fn get_next_constructor_opts(&self) -> Vec; - /// Protects a message packet + /// Protects a message packet using the entire ratchet's security features fn protect_message_packet( &self, security_level: Option, header_len_bytes: usize, packet: &mut T, - ) -> Result<(), CryptError>; + ) -> Result<(), CryptError> { + let idx = self.verify_level(security_level)?; - /// Validates a message packet + for n in 0..=idx { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + entropy_bank.protect_packet(pqc, header_len_bytes, packet)?; + } + + Ok(()) + } + + /// Validates a message packet using the entire ratchet's security features fn validate_message_packet, T: EzBuffer>( &self, security_level: Option, header: H, packet: &mut T, - ) -> Result<(), CryptError>; + ) -> Result<(), CryptError> { + let idx = self.verify_level(security_level)?; + for n in (0..=idx).rev() { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + entropy_bank.validate_packet_in_place_split(pqc, &header, packet)?; + } + + Ok(()) + } /// Returns the next Alice constructor fn next_alice_constructor(&self) -> Option { @@ -132,124 +187,10 @@ pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + ' self.get_next_constructor_opts(), self.get_cid(), self.version().wrapping_add(1), - Some(self.get_default_security_level()), ) } - /// Encrypts data locally - fn local_encrypt<'a, T: Into>>( - &self, - contents: T, - security_level: SecurityLevel, - ) -> Result, CryptError>; - - /// Decrypts data locally - fn local_decrypt<'a, T: Into>>( - &self, - contents: T, - security_level: SecurityLevel, - ) -> Result, CryptError>; -} - -/// For returning a variable hyper ratchet from a function -pub enum RatchetType { - Default(R), - Fcm(Fcm), -} - -impl RatchetType { - /// Returns the default ratchet if it exists - pub fn assume_default(self) -> Option { - if let RatchetType::Default(r) = self { - Some(r) - } else { - None - } - } -} - -impl Ratchet for StackedRatchet { - type Constructor = StackedRatchetConstructor; - - fn get_cid(&self) -> u64 { - self.get_cid() - } - - fn version(&self) -> u32 { - self.version() - } - - fn has_verified_packets(&self) -> bool { - self.has_verified_packets() - } - - fn reset_ara(&self) { - for ratchet in self.inner.message.inner.iter() { - ratchet.pqc.reset_counters(); - } - - self.inner.scramble.pqc.reset_counters(); - } - - fn get_default_security_level(&self) -> SecurityLevel { - self.get_default_security_level() - } - - fn message_pqc_drill(&self, idx: Option) -> (&PostQuantumContainer, &EntropyBank) { - self.message_pqc_drill(idx) - } - - fn get_scramble_drill(&self) -> &EntropyBank { - self.get_scramble_drill() - } - - // This may panic if any of the ratchets are in an incomplete state - fn get_next_constructor_opts(&self) -> Vec { - let mut meta_chain_hasher = sha3::Sha3_256::default(); - for chain in self - .inner - .message - .inner - .iter() - .map(|r| r.pqc.get_chain().unwrap()) - { - meta_chain_hasher.update(&chain.chain[..]); - } - - let meta_chain = meta_chain_hasher.finalize(); - //self.inner.message.inner.iter().map(|r| ConstructorOpts::new_from_previous(Some(r.pqc.params), r.pqc.get_chain().unwrap().clone())).collect() - self.inner - .message - .inner - .iter() - .map(|r| { - let prev_chain = r.pqc.get_chain().unwrap(); - let next_chain = - RecursiveChain::new(&meta_chain[..], prev_chain.alice, prev_chain.bob, false) - .unwrap(); - ConstructorOpts::new_from_previous(Some(r.pqc.params), next_chain) - }) - .collect() - } - - fn protect_message_packet( - &self, - security_level: Option, - header_len_bytes: usize, - packet: &mut T, - ) -> Result<(), CryptError> { - self.protect_message_packet(security_level, header_len_bytes, packet) - } - - fn validate_message_packet, T: EzBuffer>( - &self, - security_level: Option, - header: H, - packet: &mut T, - ) -> Result<(), CryptError> { - self.validate_message_packet(security_level, header, packet) - } - + /// Encrypts using a local key that is not shared with anyone. Relevant for RE-VFS fn local_encrypt<'a, T: Into>>( &self, contents: T, @@ -258,13 +199,14 @@ impl Ratchet for StackedRatchet { let idx = self.verify_level(Some(security_level))?; let mut data = contents.into(); for n in 0..=idx { - let (pqc, drill) = self.message_pqc_drill(Some(n)); - data = Cow::Owned(drill.local_encrypt(pqc, &data)?); + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + data = Cow::Owned(entropy_bank.local_encrypt(pqc, &data)?); } Ok(data.into_owned()) } + /// Decrypts using a local key that is not shared with anyone. Relevant for RE-VFS fn local_decrypt<'a, T: Into>>( &self, contents: T, @@ -277,97 +219,32 @@ impl Ratchet for StackedRatchet { let idx = self.verify_level(Some(security_level))?; for n in (0..=idx).rev() { - let (pqc, drill) = self.message_pqc_drill(Some(n)); - data = Cow::Owned(drill.local_decrypt(pqc, &data)?); + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + data = Cow::Owned(entropy_bank.local_decrypt(pqc, &data)?); } Ok(data.into_owned()) } -} - -impl StackedRatchet { - /// Determines if either the PQC's anti-replay attack containers have been engaged - pub fn has_verified_packets(&self) -> bool { - let max = self.inner.message.inner.len(); - for n in 0..max { - if self.get_message_pqc(Some(n)).has_verified_packets() { - return true; - } - } - - self.get_scramble_pqc().has_verified_packets() - } - - /// returns the scramble PQC - pub fn get_scramble_pqc(&self) -> &PostQuantumContainer { - &self.inner.scramble.pqc - } - - /// returns the message pqc and drill. Panics if idx is OOB - #[inline] - pub fn message_pqc_drill(&self, idx: Option) -> (&PostQuantumContainer, &EntropyBank) { - let idx = idx.unwrap_or(0); - ( - &self.inner.message.inner[idx].pqc, - &self.inner.message.inner[idx].drill, - ) - } - /// returns the message pqc and drill - #[inline] - pub fn scramble_pqc_drill(&self) -> (&PostQuantumContainer, &EntropyBank) { - (&self.inner.scramble.pqc, &self.inner.scramble.drill) - } + fn message_ratchet_count(&self) -> usize; /// Verifies the target security level, returning the corresponding idx - pub fn verify_level( + fn verify_level( &self, security_level: Option, ) -> Result> { let security_level = security_level.unwrap_or(SecurityLevel::Standard); - if security_level.value() as usize >= self.inner.message.inner.len() { - log::warn!(target: "citadel", "OOB: Security value: {}, max: {} (default: {:?})|| Version: {}", security_level.value() as usize, self.inner.message.inner.len() - 1, self.get_default_security_level(), self.version()); + let message_ratchet_count = self.message_ratchet_count(); + if security_level.value() as usize >= message_ratchet_count { + log::warn!(target: "citadel", "OOB: Security value: {}, max: {} (default: {:?})|| Version: {}", security_level.value() as usize, message_ratchet_count- 1, self.get_default_security_level(), self.version()); Err(CryptError::OutOfBoundsError) } else { Ok(security_level.value() as usize) } } - /// Protects the packet, treating the header as AAD, and the payload as the data that gets encrypted - pub fn protect_message_packet( - &self, - security_level: Option, - header_len_bytes: usize, - packet: &mut T, - ) -> Result<(), CryptError> { - let idx = self.verify_level(security_level)?; - - for n in 0..=idx { - let (pqc, drill) = self.message_pqc_drill(Some(n)); - drill.protect_packet(pqc, header_len_bytes, packet)?; - } - - Ok(()) - } - - /// Validates a packet in place - pub fn validate_message_packet, T: EzBuffer>( - &self, - security_level: Option, - header: H, - packet: &mut T, - ) -> Result<(), CryptError> { - let idx = self.verify_level(security_level)?; - for n in (0..=idx).rev() { - let (pqc, drill) = self.message_pqc_drill(Some(n)); - drill.validate_packet_in_place_split(pqc, &header, packet)?; - } - - Ok(()) - } - /// Validates in-place when the header + payload have already been split - pub fn validate_message_packet_in_place_split>( + fn validate_message_packet_in_place_split>( &self, security_level: Option, header: H, @@ -375,71 +252,82 @@ impl StackedRatchet { ) -> Result<(), CryptError> { let idx = self.verify_level(security_level)?; for n in (0..=idx).rev() { - let (pqc, drill) = self.message_pqc_drill(Some(n)); - drill.validate_packet_in_place_split(pqc, &header, packet)?; + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + entropy_bank.validate_packet_in_place_split(pqc, &header, packet)?; } Ok(()) } - /// Encrypts the data into a Vec - pub fn encrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - drill.encrypt(pqc, contents) + /// decrypts using a custom nonce configuration + fn decrypt>(&self, contents: T) -> Result, CryptError> { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; + entropy_bank.decrypt(pqc, contents) } /// Encrypts the data into a Vec - pub fn encrypt_scrambler>( - &self, - contents: T, - ) -> Result, CryptError> { - let (pqc, drill) = self.scramble_pqc_drill(); - drill.encrypt(pqc, contents) - } - - /// decrypts using a custom nonce configuration - pub fn decrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, drill) = self.message_pqc_drill(None); - drill.decrypt(pqc, contents) + fn encrypt>(&self, contents: T) -> Result, CryptError> { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; + entropy_bank.encrypt(pqc, contents) } +} - /// decrypts using a custom nonce configuration - pub fn decrypt_scrambler>( - &self, - contents: T, - ) -> Result, CryptError> { - let (pqc, drill) = self.scramble_pqc_drill(); - drill.decrypt(pqc, contents) - } +impl Ratchet for StackedRatchet { + type Constructor = StackedRatchetConstructor; - /// Returns the message drill - pub fn get_message_drill(&self, idx: Option) -> &EntropyBank { - &self.inner.message.inner[idx.unwrap_or(0)].drill + /// Gets the default security level (will use all available keys) + fn get_default_security_level(&self) -> SecurityLevel { + self.inner.default_security_level } - /// Returns the message pqc - pub fn get_message_pqc(&self, idx: Option) -> &PostQuantumContainer { - &self.inner.message.inner[idx.unwrap_or(0)].pqc + fn get_message_pqc_and_entropy_bank_at_layer( + &self, + idx: Option, + ) -> Result<(&PostQuantumContainer, &EntropyBank), CryptError> { + let idx = idx.unwrap_or(0); + self.inner + .message + .inner + .get(idx) + .map(|r| (&r.pqc, &r.entropy_bank)) + .ok_or(CryptError::OutOfBoundsError) } - /// Returns the scramble drill - pub fn get_scramble_drill(&self) -> &EntropyBank { - &self.inner.scramble.drill + fn get_scramble_pqc_and_entropy_bank(&self) -> (&PostQuantumContainer, &EntropyBank) { + (&self.inner.scramble.pqc, &self.inner.scramble.entropy_bank) } - /// Returns the [StackedRatchet]'s version - pub fn version(&self) -> u32 { - self.inner.message.inner[0].drill.version - } + // This may panic if any of the ratchets are in an incomplete state + fn get_next_constructor_opts(&self) -> Vec { + let mut meta_chain_hasher = sha3::Sha3_256::default(); + for chain in self + .inner + .message + .inner + .iter() + .map(|r| r.pqc.get_chain().unwrap()) + { + meta_chain_hasher.update(&chain.chain[..]); + } - /// Returns the CID - pub fn get_cid(&self) -> u64 { - self.inner.message.inner[0].drill.cid + let meta_chain = meta_chain_hasher.finalize(); + //self.inner.message.inner.iter().map(|r| ConstructorOpts::new_from_previous(Some(r.pqc.params), r.pqc.get_chain().unwrap().clone())).collect() + self.inner + .message + .inner + .iter() + .map(|r| { + let prev_chain = r.pqc.get_chain().unwrap(); + let next_chain = + RecursiveChain::new(&meta_chain[..], prev_chain.alice, prev_chain.bob, false) + .unwrap(); + ConstructorOpts::new_ratcheted(Some(r.pqc.params), next_chain) + }) + .collect() } - /// Gets the default security level (will use all available keys) - pub fn get_default_security_level(&self) -> SecurityLevel { - self.inner.default_security_level + fn message_ratchet_count(&self) -> usize { + self.inner.message.inner.len() } } @@ -457,13 +345,13 @@ pub(crate) struct MessageRatchet { #[derive(Serialize, Deserialize, Debug)] pub(crate) struct MessageRatchetInner { - pub(crate) drill: EntropyBank, + pub(crate) entropy_bank: EntropyBank, pub(crate) pqc: PostQuantumContainer, } #[derive(Serialize, Deserialize, Debug)] pub(crate) struct ScrambleRatchet { - pub(crate) drill: EntropyBank, + pub(crate) entropy_bank: EntropyBank, pub(crate) pqc: PostQuantumContainer, } @@ -477,21 +365,23 @@ impl From for StackedRatchet { /// For constructing the StackedRatchet during KEM stage pub mod constructor { - use crate::endpoint_crypto_container::EndpointRatchetConstructor; + use crate::endpoint_crypto_container::{ + AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, + }; use crate::entropy_bank::EntropyBank; - use crate::fcm::fcm_ratchet::{FcmAliceToBobTransfer, FcmBobToAliceTransfer, ThinRatchet}; use crate::prelude::CryptError; - use crate::stacked_ratchet::{Ratchet, StackedRatchet}; + use crate::stacked_ratchet::StackedRatchet; use arrayvec::ArrayVec; use bytes::BufMut; use bytes::BytesMut; - use citadel_pqcrypto::constructor_opts::ConstructorOpts; + use citadel_pqcrypto::constructor_opts::{ConstructorOpts, ImpliedSecurityLevel}; use citadel_pqcrypto::wire::{AliceToBobTransferParameters, BobToAliceTransferParameters}; use citadel_pqcrypto::PostQuantumContainer; use citadel_types::crypto::CryptoParameters; use citadel_types::crypto::SecurityLevel; use citadel_types::crypto::LARGEST_NONCE_LEN; use serde::{Deserialize, Serialize}; + use std::fmt::{Debug, Formatter}; /// Used during the key exchange process #[derive(Serialize, Deserialize)] @@ -506,137 +396,188 @@ pub mod constructor { params: CryptoParameters, } - /// For differentiating between two types when inputting into function parameters - #[derive(Serialize, Deserialize)] - pub enum ConstructorType { - Default(R::Constructor), - Fcm(Fcm::Constructor), + impl Debug for StackedRatchetConstructor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ThinRatchetConstructor") + .field("params", &self.params) + .field("cid", &self.cid) + .field("version", &self.new_version) + .finish() + } } - impl ConstructorType { - pub fn stage1_alice( - &mut self, - transfer: BobToAliceTransferType, - psks: &[Vec], - ) -> Result<(), CryptError> { - match self { - ConstructorType::Default(con) => con.stage1_alice(transfer, psks), - ConstructorType::Fcm(con) => con.stage1_alice(transfer, psks), - } + impl EndpointRatchetConstructor for StackedRatchetConstructor { + type AliceToBobWireTransfer = AliceToBobTransfer; + type BobToAliceWireTransfer = BobToAliceTransfer; + + fn new_alice(opts: Vec, cid: u64, new_version: u32) -> Option { + StackedRatchetConstructor::new_alice_constructor(opts, cid, new_version) } - pub fn assume_default(self) -> Option { - if let ConstructorType::Default(c) = self { - Some(c) - } else { - None - } + fn new_bob>( + cid: u64, + opts: Vec, + transfer: Self::AliceToBobWireTransfer, + psks: &[T], + ) -> Option { + StackedRatchetConstructor::new_bob_constructor( + cid, + transfer.new_version, + opts, + transfer, + psks, + ) } - pub fn assume_fcm(self) -> Option { - if let ConstructorType::Fcm(c) = self { - Some(c) - } else { - None - } + fn stage0_alice(&self) -> Option { + self.stage0_alice() } - pub fn assume_default_ref(&self) -> Option<&R::Constructor> { - if let ConstructorType::Default(c) = self { - Some(c) - } else { - None - } + fn stage0_bob(&mut self) -> Option { + self.stage0_bob() } - pub fn assume_fcm_ref(&self) -> Option<&Fcm::Constructor> { - if let ConstructorType::Fcm(c) = self { - Some(c) - } else { - None + fn stage1_alice>( + &mut self, + transfer: Self::BobToAliceWireTransfer, + psks: &[T], + ) -> Result<(), CryptError> { + let nonce_msg = &self.nonce_message; + for (container, bob_param_tx) in self + .message + .inner + .iter_mut() + .zip(transfer.msg_bob_params_txs.clone()) + { + container + .pqc + .alice_on_receive_ciphertext(bob_param_tx, psks) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; } - } - pub fn is_fcm(&self) -> bool { - matches!(self, Self::Fcm(..)) - } - } + for (idx, container) in self.message.inner.iter_mut().enumerate() { + // now, using the message pqc, decrypt the message entropy_bank + let decrypted_msg_entropy_bank = match container.pqc.decrypt( + &transfer + .encrypted_msg_entropy_banks + .get(idx) + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })?[..], + nonce_msg, + ) { + Ok(entropy_bank) => entropy_bank, + Err(err) => { + return Err(CryptError::RekeyUpdateError(err.to_string())); + } + }; + let decrypted_entropy_bank = + EntropyBank::deserialize_from(&decrypted_msg_entropy_bank[..])?; + container.entropy_bank = Some(decrypted_entropy_bank); + } - /// For denoting the different transfer types that have local lifetimes - #[derive(Serialize, Deserialize)] - pub enum AliceToBobTransferType { - Default(AliceToBobTransfer), - Fcm(FcmAliceToBobTransfer), - } + let nonce_scramble = &self.nonce_scramble; + self.scramble + .pqc + .alice_on_receive_ciphertext(transfer.scramble_bob_params_tx, psks) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; + // do the same as above + let decrypted_scramble_entropy_bank = self + .scramble + .pqc + .decrypt( + &transfer.encrypted_scramble_entropy_bank[..], + nonce_scramble, + ) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; - impl AliceToBobTransferType { - pub fn get_declared_new_version(&self) -> u32 { - match self { - AliceToBobTransferType::Default(tx) => tx.new_version, - AliceToBobTransferType::Fcm(tx) => tx.version, + let decrypted_entropy_bank = + EntropyBank::deserialize_from(&decrypted_scramble_entropy_bank[..])?; + self.scramble.entropy_bank = Some(decrypted_entropy_bank); + + // version check + if self + .scramble + .entropy_bank + .as_ref() + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })? + .version + != self.message.inner[0] + .entropy_bank + .as_ref() + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })? + .version + { + return Err(CryptError::RekeyUpdateError( + "Message entropy_bank version != scramble entropy_bank version".to_string(), + )); } - } - } - impl EndpointRatchetConstructor for StackedRatchetConstructor { - fn new_alice( - opts: Vec, - cid: u64, - new_version: u32, - security_level: Option, - ) -> Option { - StackedRatchetConstructor::new_alice(opts, cid, new_version, security_level) + if self + .scramble + .entropy_bank + .as_ref() + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })? + .cid + != self.message.inner[0] + .entropy_bank + .as_ref() + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })? + .cid + { + return Err(CryptError::RekeyUpdateError( + "Message entropy_bank cid != scramble entropy_bank cid".to_string(), + )); + } + + Ok(()) } - fn new_bob( - cid: u64, - new_drill_vers: u32, - opts: Vec, - transfer: AliceToBobTransferType, - psks: &[Vec], - ) -> Option { - match transfer { - AliceToBobTransferType::Default(transfer) => { - StackedRatchetConstructor::new_bob(cid, new_drill_vers, opts, transfer, psks) - } + fn update_version(&mut self, version: u32) -> Option<()> { + self.new_version = version; - _ => { - log::error!(target: "citadel", "Incompatible Ratchet Type passed! [X-22]"); - None - } + for container in self.message.inner.iter_mut() { + container.entropy_bank.as_mut()?.version = version; } - } - fn stage0_alice(&self) -> Option { - Some(AliceToBobTransferType::Default(self.stage0_alice()?)) - } - - fn stage0_bob(&self) -> Option { - Some(BobToAliceTransferType::Default(self.stage0_bob()?)) + self.scramble.entropy_bank.as_mut()?.version = version; + Some(()) } - fn stage1_alice( - &mut self, - transfer: BobToAliceTransferType, - psks: &[Vec], - ) -> Result<(), CryptError> { - self.stage1_alice(transfer, psks) - } + fn finish_with_custom_cid(mut self, cid: u64) -> Option { + for container in self.message.inner.iter_mut() { + container.entropy_bank.as_mut()?.cid = cid; + } - fn update_version(&mut self, version: u32) -> Option<()> { - self.update_version(version) - } + self.scramble.entropy_bank.as_mut()?.cid = cid; - fn finish_with_custom_cid(self, cid: u64) -> Option { - self.finish_with_custom_cid(cid) + self.finish() } fn finish(self) -> Option { - self.finish() + StackedRatchet::try_from(self).ok() } } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] /// Transferred during KEM pub struct AliceToBobTransfer { pub params: CryptoParameters, @@ -649,22 +590,33 @@ pub mod constructor { new_version: u32, } - #[derive(Serialize, Deserialize)] + impl AssociatedSecurityLevel for AliceToBobTransfer { + fn security_level(&self) -> SecurityLevel { + self.security_level + } + } + + impl AssociatedCryptoParams for AliceToBobTransfer { + fn crypto_params(&self) -> CryptoParameters { + self.params + } + } + + #[derive(Serialize, Deserialize, Debug)] /// Transferred during KEM pub struct BobToAliceTransfer { msg_bob_params_txs: Vec, scramble_bob_params_tx: BobToAliceTransferParameters, - encrypted_msg_drills: Vec>, - encrypted_scramble_drill: Vec, + encrypted_msg_entropy_banks: Vec>, + encrypted_scramble_entropy_bank: Vec, // the security level pub security_level: SecurityLevel, } - /// for denoting different types - #[derive(Serialize, Deserialize)] - pub enum BobToAliceTransferType { - Default(BobToAliceTransfer), - Fcm(FcmBobToAliceTransfer), + impl AssociatedSecurityLevel for BobToAliceTransfer { + fn security_level(&self) -> SecurityLevel { + self.security_level + } } impl BobToAliceTransfer { @@ -701,13 +653,12 @@ pub mod constructor { impl StackedRatchetConstructor { /// Called during the initialization stage - pub fn new_alice( + pub fn new_alice_constructor( opts: Vec, cid: u64, new_version: u32, - security_level: Option, ) -> Option { - let security_level = security_level.unwrap_or(SecurityLevel::Standard); + let security_level = opts.implied_security_level(); log::trace!(target: "citadel", "[ALICE] creating container with {:?} security level", security_level); //let count = security_level.value() as usize + 1; let len = opts.len(); @@ -716,7 +667,7 @@ pub mod constructor { .into_iter() .filter_map(|opts| { Some(MessageRatchetConstructorInner { - drill: None, + entropy_bank: None, pqc: PostQuantumContainer::new_alice(opts).ok()?, }) }) @@ -730,7 +681,7 @@ pub mod constructor { params, message: MessageRatchetConstructor { inner: keys }, scramble: ScrambleRatchetConstructor { - drill: None, + entropy_bank: None, pqc: PostQuantumContainer::new_alice(ConstructorOpts::new_init(Some(params))) .ok()?, }, @@ -743,12 +694,12 @@ pub mod constructor { } /// Called when bob receives alice's pk's - pub fn new_bob( + pub fn new_bob_constructor>( cid: u64, - new_drill_vers: u32, + new_version: u32, opts: Vec, transfer: AliceToBobTransfer, - psks: &[Vec], + psks: &[T], ) -> Option { log::trace!(target: "citadel", "[BOB] creating container with {:?} security level", transfer.security_level); let count = transfer.security_level.value() as usize + 1; @@ -758,11 +709,10 @@ pub mod constructor { .into_iter() .zip(opts) .filter_map(|(params_tx, opts)| { + let entropy_bank = + EntropyBank::new(cid, new_version, params.encryption_algorithm).ok()?; Some(MessageRatchetConstructorInner { - drill: Some( - EntropyBank::new(cid, new_drill_vers, params.encryption_algorithm) - .ok()?, - ), + entropy_bank: Some(entropy_bank), pqc: PostQuantumContainer::new_bob(opts, params_tx, psks).ok()?, }) }) @@ -773,13 +723,14 @@ pub mod constructor { return None; } + let scramble_entropy_bank = + EntropyBank::new(cid, new_version, params.encryption_algorithm).ok()?; + Some(Self { params, message: MessageRatchetConstructor { inner: keys }, scramble: ScrambleRatchetConstructor { - drill: Some( - EntropyBank::new(cid, new_drill_vers, params.encryption_algorithm).ok()?, - ), + entropy_bank: Some(scramble_entropy_bank), pqc: PostQuantumContainer::new_bob( ConstructorOpts::new_init(Some(params)), transfer.scramble_alice_params, @@ -790,7 +741,7 @@ pub mod constructor { nonce_message: transfer.msg_nonce, nonce_scramble: transfer.scramble_nonce, cid, - new_version: new_drill_vers, + new_version, security_level: transfer.security_level, }) } @@ -828,8 +779,8 @@ pub mod constructor { }) } - /// Returns the (message_bob_ct, scramble_bob_ct, msg_drill_serialized, scramble_drill_serialized) - pub fn stage0_bob(&self) -> Option { + /// Returns the (message_bob_ct, scramble_bob_ct, msg_entropy_bank_serialized, scramble_entropy_bank_serialized) + pub fn stage0_bob(&mut self) -> Option { let expected_count = self.message.inner.len(); let security_level = self.security_level; let msg_bob_cts: Vec = self @@ -843,155 +794,163 @@ pub mod constructor { } let scramble_bob_ct = self.scramble.pqc.generate_bob_to_alice_transfer().ok()?; + // now, generate the serialized bytes let nonce_msg = &self.nonce_message; let nonce_scramble = &self.nonce_scramble; - let encrypted_msg_drills: Vec> = self + let encrypted_msg_entropy_banks: Vec> = self .message .inner - .iter() + .iter_mut() .filter_map(|inner| { - inner - .pqc - .encrypt(inner.drill.as_ref()?.serialize_to_vec().ok()?, nonce_msg) - .ok() + let entropy_bank = inner.entropy_bank.as_mut()?; + let serialized = entropy_bank.serialize_to_vec().ok()?; + let encrypted = inner.pqc.encrypt(serialized, nonce_msg).ok()?; + Some(encrypted) }) .collect(); - if encrypted_msg_drills.len() != expected_count { + if encrypted_msg_entropy_banks.len() != expected_count { return None; } - let encrypted_scramble_drill = self - .scramble - .pqc - .encrypt( - self.scramble.drill.as_ref()?.serialize_to_vec().ok()?, - nonce_scramble, - ) - .ok()?; + let scramble_entropy_bank = self.scramble.entropy_bank.as_mut()?; + let serialized = scramble_entropy_bank.serialize_to_vec().ok()?; + let encrypted_scramble_entropy_bank = + self.scramble.pqc.encrypt(serialized, nonce_scramble).ok()?; let transfer = BobToAliceTransfer { msg_bob_params_txs: msg_bob_cts, scramble_bob_params_tx: scramble_bob_ct, - encrypted_msg_drills, - encrypted_scramble_drill, + encrypted_msg_entropy_banks, + encrypted_scramble_entropy_bank, security_level, }; Some(transfer) } - /// Returns Some(()) if process succeeded - pub fn stage1_alice( + /// Returns Ok(()) if process succeeded + pub fn stage1_alice>( &mut self, - transfer: BobToAliceTransferType, - psks: &[Vec], + transfer: BobToAliceTransfer, + psks: &[T], ) -> Result<(), CryptError> { - if let BobToAliceTransferType::Default(transfer) = transfer { - let nonce_msg = &self.nonce_message; - - for (container, bob_param_tx) in self - .message - .inner - .iter_mut() - .zip(transfer.msg_bob_params_txs) - { - container - .pqc - .alice_on_receive_ciphertext(bob_param_tx, psks) - .map_err(|err| CryptError::DrillUpdateError(err.to_string()))?; - } + let nonce_msg = &self.nonce_message; + for (container, bob_param_tx) in self + .message + .inner + .iter_mut() + .zip(transfer.msg_bob_params_txs.clone()) + { + container + .pqc + .alice_on_receive_ciphertext(bob_param_tx, psks) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; + } - for (idx, container) in self.message.inner.iter_mut().enumerate() { - // now, using the message pqc, decrypt the message drill - let decrypted_msg_drill = container - .pqc - .decrypt( - &transfer.encrypted_msg_drills.get(idx).ok_or_else(|| { - CryptError::DrillUpdateError( - "Unable to get encrypted_msg_drills".to_string(), - ) - })?[..], - nonce_msg, - ) - .map_err(|err| CryptError::DrillUpdateError(err.to_string()))?; - container.drill = - Some(EntropyBank::deserialize_from(&decrypted_msg_drill[..])?); - } + for (idx, container) in self.message.inner.iter_mut().enumerate() { + // now, using the message pqc, decrypt the message entropy_bank + let decrypted_msg_entropy_bank = match container.pqc.decrypt( + &transfer + .encrypted_msg_entropy_banks + .get(idx) + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })?[..], + nonce_msg, + ) { + Ok(entropy_bank) => entropy_bank, + Err(err) => { + return Err(CryptError::RekeyUpdateError(err.to_string())); + } + }; + let mut decrypted_entropy_bank = + EntropyBank::deserialize_from(&decrypted_msg_entropy_bank[..])?; + // Overwrite the CID since the entropy bank bob encrypted had his CID + decrypted_entropy_bank.cid = self.cid; + container.entropy_bank = Some(decrypted_entropy_bank); + } - let nonce_scramble = &self.nonce_scramble; - self.scramble - .pqc - .alice_on_receive_ciphertext(transfer.scramble_bob_params_tx, psks) - .map_err(|err| CryptError::DrillUpdateError(err.to_string()))?; - // do the same as above - let decrypted_scramble_drill = self - .scramble - .pqc - .decrypt(&transfer.encrypted_scramble_drill[..], nonce_scramble) - .map_err(|err| CryptError::DrillUpdateError(err.to_string()))?; - self.scramble.drill = Some(EntropyBank::deserialize_from( - &decrypted_scramble_drill[..], - )?); - - // version check - if self - .scramble - .drill + let nonce_scramble = &self.nonce_scramble; + self.scramble + .pqc + .alice_on_receive_ciphertext(transfer.scramble_bob_params_tx, psks) + .map_err(|err| { + println!( + "[DEBUG] Checkpoint 3 - Alice on receive scramble ciphertext error: {:?}", + err + ); + CryptError::RekeyUpdateError(err.to_string()) + })?; + // do the same as above + let decrypted_scramble_entropy_bank = self + .scramble + .pqc + .decrypt( + &transfer.encrypted_scramble_entropy_bank[..], + nonce_scramble, + ) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; + let mut decrypted_entropy_bank = + EntropyBank::deserialize_from(&decrypted_scramble_entropy_bank[..])?; + // Overwrite the CID since the entropy bank bob encrypted had his CID + decrypted_entropy_bank.cid = self.cid; + self.scramble.entropy_bank = Some(decrypted_entropy_bank); + // version check + if self + .scramble + .entropy_bank + .as_ref() + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })? + .version + != self.message.inner[0] + .entropy_bank .as_ref() .ok_or_else(|| { - CryptError::DrillUpdateError( - "Unable to get encrypted_msg_drills".to_string(), + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), ) })? .version - != self.message.inner[0] - .drill - .as_ref() - .ok_or_else(|| { - CryptError::DrillUpdateError( - "Unable to get encrypted_msg_drills".to_string(), - ) - })? - .version - { - return Err(CryptError::DrillUpdateError( - "Message drill version != scramble drill version".to_string(), - )); - } + { + return Err(CryptError::RekeyUpdateError( + "Message entropy_bank version != scramble entropy_bank version".to_string(), + )); + } - if self - .scramble - .drill + if self + .scramble + .entropy_bank + .as_ref() + .ok_or_else(|| { + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), + ) + })? + .cid + != self.message.inner[0] + .entropy_bank .as_ref() .ok_or_else(|| { - CryptError::DrillUpdateError( - "Unable to get encrypted_msg_drills".to_string(), + CryptError::RekeyUpdateError( + "Unable to get encrypted_msg_entropy_banks".to_string(), ) })? .cid - != self.message.inner[0] - .drill - .as_ref() - .ok_or_else(|| { - CryptError::DrillUpdateError( - "Unable to get encrypted_msg_drills".to_string(), - ) - })? - .cid - { - return Err(CryptError::DrillUpdateError( - "Message drill cid != scramble drill cid".to_string(), - )); - } - - Ok(()) - } else { - Err(CryptError::DrillUpdateError( - "Incompatible Ratchet Type passed! [X-40]".to_string(), - )) + { + return Err(CryptError::RekeyUpdateError( + "Message entropy_bank cid != scramble entropy_bank cid".to_string(), + )); } + + Ok(()) } /// Upgrades the construction into the StackedRatchet @@ -1004,41 +963,41 @@ pub mod constructor { self.new_version = version; for container in self.message.inner.iter_mut() { - container.drill.as_mut()?.version = version; + container.entropy_bank.as_mut()?.version = version; } - self.scramble.drill.as_mut()?.version = version; + self.scramble.entropy_bank.as_mut()?.version = version; Some(()) } - /// Sometimes, replacing the CID is useful such as during peer KEM exhcange wherein + /// Sometimes, replacing the CID is useful such as during peer KEM exchange wherein /// the CIDs between both parties are different. If a version is supplied, the version /// will be updated pub fn finish_with_custom_cid(mut self, cid: u64) -> Option { for container in self.message.inner.iter_mut() { - container.drill.as_mut()?.cid = cid; + container.entropy_bank.as_mut()?.cid = cid; } - self.scramble.drill.as_mut()?.cid = cid; + self.scramble.entropy_bank.as_mut()?.cid = cid; self.finish() } } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub(super) struct MessageRatchetConstructor { pub(super) inner: Vec, } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub(super) struct MessageRatchetConstructorInner { - pub(super) drill: Option, + pub(super) entropy_bank: Option, pub(super) pqc: PostQuantumContainer, } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub(super) struct ScrambleRatchetConstructor { - pub(super) drill: Option, + pub(super) entropy_bank: Option, pub(super) pqc: PostQuantumContainer, } } @@ -1053,22 +1012,22 @@ impl TryFrom for StackedRatchet { let default_security_level = SecurityLevel::for_value(message.inner.len() - 1).ok_or(())?; // make sure the shared secret is loaded let _ = scramble.pqc.get_shared_secret().map_err(|_| ())?; - let scramble_drill = scramble.drill.ok_or(())?; + let scramble_entropy_bank = scramble.entropy_bank.ok_or(())?; let mut inner = Vec::with_capacity(message.inner.len()); for container in message.inner { // make sure shared secret is loaded let _ = container.pqc.get_shared_secret().map_err(|_| ())?; - let message_drill = container.drill.ok_or(())?; + let message_entropy_bank = container.entropy_bank.ok_or(())?; - if message_drill.version != scramble_drill.version - || message_drill.cid != scramble_drill.cid + if message_entropy_bank.version != scramble_entropy_bank.version + || message_entropy_bank.cid != scramble_entropy_bank.cid { return Err(()); } inner.push(MessageRatchetInner { - drill: message_drill, + entropy_bank: message_entropy_bank, pqc: container.pqc, }); } @@ -1076,7 +1035,7 @@ impl TryFrom for StackedRatchet { let message = MessageRatchet { inner }; let scramble = ScrambleRatchet { - drill: scramble_drill, + entropy_bank: scramble_entropy_bank, pqc: scramble.pqc, }; diff --git a/citadel_crypt/src/streaming_crypt_scrambler.rs b/citadel_crypt/src/streaming_crypt_scrambler.rs index b38d8f9ce..1b5eb2cf8 100644 --- a/citadel_crypt/src/streaming_crypt_scrambler.rs +++ b/citadel_crypt/src/streaming_crypt_scrambler.rs @@ -47,7 +47,7 @@ //! group_sender, //! stop_receiver, //! SecurityLevel::Standard, -//! hyper_ratchet, +//! stacked_ratchet, //! static_aux_ratchet, //! header_size, //! target_cid, @@ -84,7 +84,7 @@ use crate::packet_vector::PacketVector; use crate::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupSenderDevice}; use crate::misc::CryptError; -use crate::stacked_ratchet::StackedRatchet; +use crate::stacked_ratchet::Ratchet; use citadel_io::tokio_stream::{Stream, StreamExt}; use citadel_io::Mutex; use citadel_types::crypto::SecurityLevel; @@ -236,15 +236,20 @@ impl>> From for BytesSource { /// /// This is ran on a separate thread on the threadpool. Returns the number of bytes and number of groups #[allow(clippy::too_many_arguments)] -pub fn scramble_encrypt_source( +pub fn scramble_encrypt_source< + S: ObjectSource, + F: HeaderInscriberFn, + const N: usize, + R: Ratchet, +>( mut source: S, max_group_size: Option, object_id: ObjectId, group_sender: GroupChanneler, CryptError>>, stop: Receiver<()>, security_level: SecurityLevel, - hyper_ratchet: StackedRatchet, - static_aux_ratchet: StackedRatchet, + stacked_ratchet: R, + static_aux_ratchet: R, header_size_bytes: usize, target_cid: u64, group_id: u64, @@ -286,7 +291,7 @@ pub fn scramble_encrypt_source) -> Result<(), CryptError> { .map_err(|err| CryptError::Encrypt(err.to_string())) } -async fn file_streamer( +async fn file_streamer( group_sender: GroupChanneler, CryptError>>, - mut file_scrambler: AsyncCryptScrambler, + mut file_scrambler: AsyncCryptScrambler, ) -> Result<(), CryptError> { while let Some(val) = file_scrambler.next().await { group_sender @@ -335,10 +340,10 @@ async fn file_streamer( } #[allow(dead_code)] -struct AsyncCryptScrambler { +struct AsyncCryptScrambler { reader: BufReader, - hyper_ratchet: StackedRatchet, - static_aux_ratchet: StackedRatchet, + stacked_ratchet: Ra, + static_aux_ratchet: Ra, security_level: SecurityLevel, transfer_type: TransferType, file_len: usize, @@ -356,7 +361,7 @@ struct AsyncCryptScrambler { cur_task: Option, CryptError>>>, } -impl AsyncCryptScrambler { +impl AsyncCryptScrambler { fn poll_task( groups_rendered: &mut usize, read_cursor: &mut usize, @@ -378,15 +383,18 @@ impl AsyncCryptScrambler } } -impl Unpin for AsyncCryptScrambler {} +impl Unpin + for AsyncCryptScrambler +{ +} -impl AsyncCryptScrambler { +impl AsyncCryptScrambler { fn poll_scramble_next_group( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { let Self { - hyper_ratchet, + stacked_ratchet, static_aux_ratchet, file_len, read_cursor, @@ -421,7 +429,7 @@ impl AsyncCryptScrambler let header_inscriber = header_inscriber.clone(); let buffer = buffer.clone(); let security_level = *security_level; - let hyper_ratchet = hyper_ratchet.clone(); + let stacked_ratchet = stacked_ratchet.clone(); let static_aux_ratchet = static_aux_ratchet.clone(); let header_size_bytes = *header_size_bytes; let target_cid = *target_cid; @@ -432,7 +440,7 @@ impl AsyncCryptScrambler par_scramble_encrypt_group( &buffer.lock()[..poll_len], security_level, - &hyper_ratchet, + &stacked_ratchet, &static_aux_ratchet, header_size_bytes, target_cid, @@ -457,7 +465,9 @@ impl AsyncCryptScrambler } } -impl Stream for AsyncCryptScrambler { +impl Stream + for AsyncCryptScrambler +{ type Item = GroupSenderDevice; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/citadel_crypt/src/sync_toggle.rs b/citadel_crypt/src/sync_toggle.rs index 5258496fa..611217135 100644 --- a/citadel_crypt/src/sync_toggle.rs +++ b/citadel_crypt/src/sync_toggle.rs @@ -61,7 +61,7 @@ pub struct SyncToggle { inner: std::sync::Arc, } -const ORDERING: std::sync::atomic::Ordering = std::sync::atomic::Ordering::SeqCst; +const ORDERING: std::sync::atomic::Ordering = std::sync::atomic::Ordering::Relaxed; /// Represents the current state of a toggle operation. #[derive(Debug, Eq, PartialEq, Copy, Clone)] @@ -84,7 +84,7 @@ impl SyncToggle { /// Attempts to toggle the state to true if it's currently false. /// - /// Returns `JustToggled` if the state was changed to true, `AlreadyToggled` if the state was already true, and `Untoggled` if the state is still false. + /// Returns `JustToggled` if the state was changed to true, `AlreadyToggled` if the state was already true pub fn toggle_on_if_untoggled(&self) -> CurrentToggleState { if self.inner.fetch_nand(false, ORDERING) { CurrentToggleState::AlreadyToggled @@ -93,13 +93,24 @@ impl SyncToggle { } } + /// Resets the toggle state to false and returns the previous state. + /// + /// Returns `AlreadyToggled` if it was previously true, `Untoggled` if it was already false + pub fn reset_and_get_previous(&self) -> CurrentToggleState { + if self.inner.fetch_and(false, ORDERING) { + CurrentToggleState::AlreadyToggled + } else { + CurrentToggleState::Untoggled + } + } + /// Resets the toggle state to false. pub fn toggle_off(&self) { self.inner.store(false, ORDERING) } /// Returns the current state of the toggle. - pub fn get(&self) -> CurrentToggleState { + pub fn state(&self) -> CurrentToggleState { if self.inner.load(ORDERING) { CurrentToggleState::AlreadyToggled } else { @@ -115,33 +126,55 @@ mod tests { #[test] fn test_sync_toggle() { let toggle = SyncToggle::new(); - assert_eq!(toggle.get(), CurrentToggleState::Untoggled); + assert_eq!(toggle.state(), CurrentToggleState::Untoggled); assert_eq!( toggle.toggle_on_if_untoggled(), CurrentToggleState::JustToggled ); toggle.toggle_off(); - assert_eq!(toggle.get(), CurrentToggleState::Untoggled); + assert_eq!(toggle.state(), CurrentToggleState::Untoggled); assert_eq!( toggle.toggle_on_if_untoggled(), CurrentToggleState::JustToggled ); - assert_eq!(toggle.get(), CurrentToggleState::AlreadyToggled); + assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled); assert_eq!( toggle.toggle_on_if_untoggled(), CurrentToggleState::AlreadyToggled ); - assert_eq!(toggle.get(), CurrentToggleState::AlreadyToggled); + assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled); assert_eq!( toggle.toggle_on_if_untoggled(), CurrentToggleState::AlreadyToggled ); - assert_eq!(toggle.get(), CurrentToggleState::AlreadyToggled); + assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled); toggle.toggle_off(); - assert_eq!(toggle.get(), CurrentToggleState::Untoggled); + assert_eq!(toggle.state(), CurrentToggleState::Untoggled); assert_eq!( toggle.toggle_on_if_untoggled(), CurrentToggleState::JustToggled ); } + + #[test] + fn test_sync_toggle_reset_and_get_previous() { + let toggle = SyncToggle::new(); + + // Test resetting when already false + assert_eq!(toggle.state(), CurrentToggleState::Untoggled); + assert_eq!( + toggle.reset_and_get_previous(), + CurrentToggleState::Untoggled + ); + assert_eq!(toggle.state(), CurrentToggleState::Untoggled); + + // Test resetting when true + let _ = toggle.toggle_on_if_untoggled(); + assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled); + assert_eq!( + toggle.reset_and_get_previous(), + CurrentToggleState::AlreadyToggled + ); + assert_eq!(toggle.state(), CurrentToggleState::Untoggled); + } } diff --git a/citadel_crypt/src/toolset.rs b/citadel_crypt/src/toolset.rs index c9d9c7e39..8e3d8d792 100644 --- a/citadel_crypt/src/toolset.rs +++ b/citadel_crypt/src/toolset.rs @@ -31,13 +31,13 @@ //! Some(UpdateStatus::CommittedNeedsSynchronization { new_version, old_version }) => { //! println!("Updated to {} but need to sync version {}", new_version, old_version); //! // Implement synchronization logic -//! toolset.deregister_oldest_hyper_ratchet(old_version).unwrap(); +//! toolset.deregister_oldest_stacked_ratchet(old_version).unwrap(); //! } //! None => println!("Update failed"), //! } //! //! // Access ratchets -//! if let Some(current) = toolset.get_most_recent_hyper_ratchet() { +//! if let Some(current) = toolset.get_most_recent_stacked_ratchet() { //! // Use current ratchet for encryption //! } //! ``` @@ -62,11 +62,13 @@ use crate::misc::CryptError; use crate::stacked_ratchet::{Ratchet, StackedRatchet}; use std::ops::RangeInclusive; -/// Returns the max number of drill that can be stored in memory +/// The maximum number of ratchets to store in memory. Note that, most of the time, the true number in memory +/// will be the max - 1, since the max is only reached when the most recent ratchet is added and the toolset +/// is in the state of pending synchronization/truncation #[cfg(debug_assertions)] -pub const MAX_HYPER_RATCHETS_IN_MEMORY: usize = 6; +pub const MAX_RATCHETS_IN_MEMORY: usize = 6; #[cfg(not(debug_assertions))] -pub const MAX_HYPER_RATCHETS_IN_MEMORY: usize = 128; +pub const MAX_RATCHETS_IN_MEMORY: usize = 128; /// The reserved version for the static aux ratchet pub const STATIC_AUX_VERSION: u32 = 0; @@ -77,19 +79,19 @@ pub const STATIC_AUX_VERSION: u32 = 0; pub struct Toolset { /// the CID of the owner pub cid: u64, - most_recent_hyper_ratchet_version: u32, - oldest_hyper_ratchet_version: u32, + most_recent_stacked_ratchet_version: u32, + oldest_stacked_ratchet_version: u32, #[serde(bound = "")] map: VecDeque, - /// The static auxiliary drill was made to cover a unique situation that is consequence of dropping-off the back of the VecDeque upon upgrade: - /// As the back gets dropped, any data encrypted using that version now becomes undecipherable forever. The solution to this is having a static drill, but this + /// The static auxiliary entropy_bank was made to cover a unique situation that is consequence of dropping-off the back of the VecDeque upon upgrade: + /// As the back gets dropped, any data encrypted using that version now becomes undecipherable forever. The solution to this is having a static entropy_bank, but this /// does indeed compromise safety. This should NEVER be used for network data transmission (except for first packets), and should only /// really be used when encrypting data which is stored under the local filesystem via HyxeFiles. Since a HyxeFile, for example, hides revealing data /// with a complex file path, any possible hacker wouldn't necessarily be able to correlate the HyxeFile with the correct CID unless additional work was done. /// Local filesystems should be encrypted anyways (otherwise voids warranty), but, having the HyxeFile layer is really just a "weak" layer of protection /// designed to derail any currently existing or historical viruses that may look for conventional means of breaking-through data #[serde(bound = "")] - static_auxiliary_hyper_ratchet: R, + static_auxiliary_stacked_ratchet: R, } // This clone should only be called in the middle of a session @@ -97,125 +99,133 @@ impl Clone for Toolset { fn clone(&self) -> Self { Self { cid: self.cid, - most_recent_hyper_ratchet_version: self.most_recent_hyper_ratchet_version, - oldest_hyper_ratchet_version: self.oldest_hyper_ratchet_version, + most_recent_stacked_ratchet_version: self.most_recent_stacked_ratchet_version, + oldest_stacked_ratchet_version: self.oldest_stacked_ratchet_version, map: self.map.clone(), - static_auxiliary_hyper_ratchet: self.static_auxiliary_hyper_ratchet.clone(), + static_auxiliary_stacked_ratchet: self.static_auxiliary_stacked_ratchet.clone(), } } } -#[derive(Serialize, Deserialize, Clone, Copy)] -pub enum UpdateStatus { +#[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Debug)] +pub enum ToolsetUpdateStatus { // new version has been committed, and the number of HRs is still less than the total max. No E2E synchronization required - Committed { new_version: u32 }, + Committed { + new_version: u32, + }, // The maximum number of acceptable HR's have been stored in memory, but will not be removed until both endpoints can agree // to removing the version - CommittedNeedsSynchronization { new_version: u32, old_version: u32 }, + CommittedNeedsSynchronization { + new_version: u32, + oldest_version: u32, + }, } impl Toolset { - /// Creates a new [Toolset]. Designates the `hyper_ratchet` as the static auxiliary ratchet - /// hyper_ratchet should be version 0 - pub fn new(cid: u64, hyper_ratchet: R) -> Self { - let mut map = VecDeque::with_capacity(MAX_HYPER_RATCHETS_IN_MEMORY); - map.push_front(hyper_ratchet.clone()); + /// Creates a new [Toolset]. Designates the `stacked_ratchet` as the static auxiliary ratchet + /// stacked_ratchet should be version 0 + pub fn new(cid: u64, stacked_ratchet: R) -> Self { + let mut map = VecDeque::with_capacity(MAX_RATCHETS_IN_MEMORY); + map.push_front(stacked_ratchet.clone()); Toolset { cid, - most_recent_hyper_ratchet_version: 0, - oldest_hyper_ratchet_version: 0, + most_recent_stacked_ratchet_version: 0, + oldest_stacked_ratchet_version: 0, map, - static_auxiliary_hyper_ratchet: hyper_ratchet, + static_auxiliary_stacked_ratchet: stacked_ratchet, } } pub fn new_debug( cid: u64, - hyper_ratchet: R, - most_recent_hyper_ratchet_version: u32, - oldest_hyper_ratchet_version: u32, + stacked_ratchet: R, + most_recent_stacked_ratchet_version: u32, + oldest_stacked_ratchet_version: u32, ) -> Self { - let mut map = VecDeque::with_capacity(MAX_HYPER_RATCHETS_IN_MEMORY); - map.push_front(hyper_ratchet.clone()); + let mut map = VecDeque::with_capacity(MAX_RATCHETS_IN_MEMORY); + map.push_front(stacked_ratchet.clone()); Toolset { cid, - most_recent_hyper_ratchet_version, - oldest_hyper_ratchet_version, + most_recent_stacked_ratchet_version, + oldest_stacked_ratchet_version, map, - static_auxiliary_hyper_ratchet: hyper_ratchet, + static_auxiliary_stacked_ratchet: stacked_ratchet, } } /// Updates from an inbound DrillUpdateObject. Returns the new Drill - pub fn update_from(&mut self, new_hyper_ratchet: R) -> Option { - let latest_hr_version = self.get_most_recent_hyper_ratchet_version(); + pub fn update_from(&mut self, new_stacked_ratchet: R) -> Option { + let latest_hr_version = self.get_most_recent_stacked_ratchet_version(); - if new_hyper_ratchet.get_cid() != self.cid { - log::error!(target: "citadel", "The supplied hyper ratchet does not belong to the expected CID (expected: {}, obtained: {})", self.cid, new_hyper_ratchet.get_cid()); + if new_stacked_ratchet.get_cid() != self.cid { + log::error!(target: "citadel", "The supplied hyper ratchet does not belong to the expected CID (expected: {}, obtained: {})", self.cid, new_stacked_ratchet.get_cid()); return None; } - if latest_hr_version != new_hyper_ratchet.version().wrapping_sub(1) { - log::error!(target: "citadel", "The supplied hyper ratchet is not precedent to the drill update object (expected: {}, obtained: {})", latest_hr_version + 1, new_hyper_ratchet.version()); + if latest_hr_version != new_stacked_ratchet.version().wrapping_sub(1) { + log::error!(target: "citadel", "The supplied hyper ratchet is not precedent to the entropy_bank update object (expected: {}, obtained: {})", latest_hr_version + 1, new_stacked_ratchet.version()); return None; } - let update_status = self.append_hyper_ratchet(new_hyper_ratchet); + let update_status = self.append_stacked_ratchet(new_stacked_ratchet); let cur_version = match &update_status { - UpdateStatus::Committed { new_version } - | UpdateStatus::CommittedNeedsSynchronization { new_version, .. } => *new_version, + ToolsetUpdateStatus::Committed { new_version } + | ToolsetUpdateStatus::CommittedNeedsSynchronization { new_version, .. } => { + *new_version + } }; - self.most_recent_hyper_ratchet_version = cur_version; + self.most_recent_stacked_ratchet_version = cur_version; - let prev_version = self.most_recent_hyper_ratchet_version.wrapping_sub(1); - log::trace!(target: "citadel", "[{}] Upgraded {} to {}. Adjusted index of current: {}. Adjusted index of (current - 1): {} || OLDEST: {} || LEN: {}", MAX_HYPER_RATCHETS_IN_MEMORY, prev_version, cur_version, self.get_adjusted_index(cur_version), self.get_adjusted_index(prev_version), self.get_oldest_hyper_ratchet_version(), self.map.len()); + let prev_version = self.most_recent_stacked_ratchet_version.wrapping_sub(1); + log::trace!(target: "citadel", "[{}] Upgraded {} to {}. Adjusted index of current: {}. Adjusted index of (current - 1): {} || OLDEST: {} || LEN: {}", MAX_RATCHETS_IN_MEMORY, prev_version, cur_version, self.get_adjusted_index(cur_version), self.get_adjusted_index(prev_version), self.get_oldest_stacked_ratchet_version(), self.map.len()); Some(update_status) } #[allow(unused_results)] - ///Replacing drills is not allowed, and is why this subroutine returns an error when a collision is detected + ///Replacing entropy_banks is not allowed, and is why this subroutine returns an error when a collision is detected /// /// Returns the new hyper ratchet version - fn append_hyper_ratchet(&mut self, hyper_ratchet: R) -> UpdateStatus { + fn append_stacked_ratchet(&mut self, stacked_ratchet: R) -> ToolsetUpdateStatus { //debug_assert!(self.map.len() <= MAX_HYPER_RATCHETS_IN_MEMORY); - let new_version = hyper_ratchet.version(); - //println!("max hypers: {} @ {} bytes ea", MAX_HYPER_RATCHETS_IN_MEMORY, get_approx_bytes_per_hyper_ratchet()); - self.map.push_front(hyper_ratchet); - if self.map.len() > MAX_HYPER_RATCHETS_IN_MEMORY { - let old_version = self.get_oldest_hyper_ratchet_version(); - log::trace!(target: "citadel", "[Toolset Update] Needs Truncation. Old version: {}", old_version); - UpdateStatus::CommittedNeedsSynchronization { + let new_version = stacked_ratchet.version(); + //println!("max hypers: {} @ {} bytes ea", MAX_HYPER_RATCHETS_IN_MEMORY, get_approx_bytes_per_stacked_ratchet()); + self.map.push_front(stacked_ratchet); + if self.map.len() >= MAX_RATCHETS_IN_MEMORY { + let oldest_version = self.get_oldest_stacked_ratchet_version(); + log::trace!(target: "citadel", "[Toolset Update] Needs Truncation. Oldest version: {}", oldest_version); + ToolsetUpdateStatus::CommittedNeedsSynchronization { new_version, - old_version, + oldest_version, } } else { - UpdateStatus::Committed { new_version } + ToolsetUpdateStatus::Committed { new_version } } } - /// When append_hyper_ratchet returns CommittedNeedsSynchronization on Bob's side, Bob should first + /// When append_stacked_ratchet returns CommittedNeedsSynchronization on Bob's side, Bob should first /// send a packet to Alice telling her that capacity has been reached and that version V should be dropped. /// Alice will then prevent herself from sending any more packets using version V, and will locally run this /// function. Next, Alice should alert Bob telling him that it's now safe to remove version V. Bob then runs /// this function last. By doing this, Alice no longer sends packets that may be no longer be valid #[allow(unused_results)] - pub fn deregister_oldest_hyper_ratchet(&mut self, version: u32) -> Result<(), CryptError> { - if self.map.len() <= MAX_HYPER_RATCHETS_IN_MEMORY { - return Err(CryptError::DrillUpdateError( + pub fn deregister_oldest_stacked_ratchet(&mut self, version: u32) -> Result<(), CryptError> { + if self.map.len() < MAX_RATCHETS_IN_MEMORY { + return Err(CryptError::RekeyUpdateError( "Cannot call for deregistration unless the map len is maxed out".to_string(), )); } - let oldest = self.get_oldest_hyper_ratchet_version(); + let oldest = self.get_oldest_stacked_ratchet_version(); if oldest != version { - Err(CryptError::DrillUpdateError(format!( + Err(CryptError::RekeyUpdateError(format!( "Unable to deregister. Provided version: {version}, expected version: {oldest}", ))) } else { self.map.pop_back().ok_or(CryptError::OutOfBoundsError)?; - self.oldest_hyper_ratchet_version = self.oldest_hyper_ratchet_version.wrapping_add(1); - log::trace!(target: "citadel", "[Toolset] Deregistered version {}. New oldest: {} | LEN: {}", version, self.oldest_hyper_ratchet_version, self.len()); + self.oldest_stacked_ratchet_version = + self.oldest_stacked_ratchet_version.wrapping_add(1); + log::trace!(target: "citadel", "[Toolset] Deregistered version {}. New oldest: {} | LEN: {}", version, self.oldest_stacked_ratchet_version, self.len()); Ok(()) } } @@ -226,65 +236,65 @@ impl Toolset { self.map.len() } - /// Returns the latest drill version - pub fn get_most_recent_hyper_ratchet(&self) -> Option<&R> { + /// Returns the latest entropy_bank version + pub fn get_most_recent_stacked_ratchet(&self) -> Option<&R> { self.map.front() } - /// Returns the oldest drill in the VecDeque - pub fn get_oldest_hyper_ratchet(&self) -> Option<&R> { + /// Returns the oldest entropy_bank in the VecDeque + pub fn get_oldest_stacked_ratchet(&self) -> Option<&R> { self.map.back() } - /// Gets the oldest drill version - pub fn get_oldest_hyper_ratchet_version(&self) -> u32 { - self.oldest_hyper_ratchet_version + /// Gets the oldest entropy_bank version + pub fn get_oldest_stacked_ratchet_version(&self) -> u32 { + self.oldest_stacked_ratchet_version } - /// Returns the most recent drill - pub fn get_most_recent_hyper_ratchet_version(&self) -> u32 { - self.most_recent_hyper_ratchet_version + /// Returns the most recent entropy_bank + pub fn get_most_recent_stacked_ratchet_version(&self) -> u32 { + self.most_recent_stacked_ratchet_version } - /// Returns the static auxiliary drill. There is no "set" function, because this really + /// Returns the static auxiliary entropy_bank. There is no "set" function, because this really /// shouldn't be changing internally as this is depended upon by datasets which require a fixed encryption /// version which would otherwise normally get dropped from the VecDeque semi-actively. /// /// This panics if the internal map is empty /// - /// The static auxilliary drill is used for RECOVERY MODE. I.e., if the version are out - /// of sync, then the static auxiliary drill is used to obtain the nonce for the AES GCM + /// The static auxilliary entropy_bank is used for RECOVERY MODE. I.e., if the version are out + /// of sync, then the static auxiliary entropy_bank is used to obtain the nonce for the AES GCM /// mode of encryption pub fn get_static_auxiliary_ratchet(&self) -> &R { - &self.static_auxiliary_hyper_ratchet + &self.static_auxiliary_stacked_ratchet } - /// The index within the vec deque does not necessarily track the drill versions. + /// The index within the vec deque does not necessarily track the entropy_bank versions. /// This function adjusts for that #[inline] fn get_adjusted_index(&self, version: u32) -> usize { - self.most_recent_hyper_ratchet_version.wrapping_sub(version) as usize + self.most_recent_stacked_ratchet_version + .wrapping_sub(version) as usize } - /// Returns a specific drill version - pub fn get_hyper_ratchet(&self, version: u32) -> Option<&R> { + /// Returns a specific entropy_bank version + pub fn get_stacked_ratchet(&self, version: u32) -> Option<&R> { let idx = self.get_adjusted_index(version); - //println!("Getting idx {} which should have v{}", idx, version); let res = self.map.get(idx); if res.is_none() { - log::error!(target: "citadel", "Attempted to get ratchet v{}, but does not exist! len: {}. Oldest: {}. Newest: {}", version, &self.map.len(), self.oldest_hyper_ratchet_version, self.most_recent_hyper_ratchet_version); + log::error!(target: "citadel", "Attempted to get ratchet v{} for cid={}, but does not exist! len: {}. Oldest: {}. Newest: {}", version, self.cid, self.map.len(), self.oldest_stacked_ratchet_version, self.most_recent_stacked_ratchet_version); } res } - /// Returns a range of drills. Returns None if any drill in the range is missing - pub fn get_hyper_ratchets(&self, versions: RangeInclusive) -> Option> { + /// Returns a range of entropy_banks. Returns None if any entropy_bank in the range is missing + pub fn get_stacked_ratchets(&self, versions: RangeInclusive) -> Option> { let mut ret = Vec::with_capacity((*versions.end() - *versions.start() + 1) as usize); for version in versions { - if let Some(drill) = self.get_hyper_ratchet(version) { - ret.push(drill); + if let Some(entropy_bank) = self.get_stacked_ratchet(version) { + ret.push(entropy_bank); } else { return None; } @@ -295,18 +305,18 @@ impl Toolset { /// Serializes the toolset to a buffer pub fn serialize_to_vec(&self) -> Result, CryptError> { - bincode::serialize(self).map_err(|err| CryptError::DrillUpdateError(err.to_string())) + bincode::serialize(self).map_err(|err| CryptError::RekeyUpdateError(err.to_string())) } /// Deserializes from a slice of bytes pub fn deserialize_from_bytes>(input: T) -> Result> { bincode::deserialize(input.as_ref()) - .map_err(|err| CryptError::DrillUpdateError(err.to_string())) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string())) } /// Resets the internal state to the default, if necessary. At the beginning of each session, this should be called pub fn verify_init_state(&self) -> Option<()> { - self.static_auxiliary_hyper_ratchet.reset_ara(); + self.static_auxiliary_stacked_ratchet.reset_ara(); Some(()) } } @@ -314,18 +324,18 @@ impl Toolset { /// Makes replacing/synchronizing toolsets easier /// input: (static_aux_ratchet, f(0)) pub type StaticAuxRatchet = StackedRatchet; -impl From<(StaticAuxRatchet, StackedRatchet)> for Toolset { - fn from(drill: (StaticAuxRatchet, StackedRatchet)) -> Self { - let most_recent_hyper_ratchet_version = drill.1.version(); - let oldest_hyper_ratchet_version = most_recent_hyper_ratchet_version; // for init, just like in the normal constructor - let mut map = VecDeque::with_capacity(MAX_HYPER_RATCHETS_IN_MEMORY); - map.insert(0, drill.1); +impl From<(R, R)> for Toolset { + fn from(entropy_bank: (R, R)) -> Self { + let most_recent_stacked_ratchet_version = entropy_bank.1.version(); + let oldest_stacked_ratchet_version = most_recent_stacked_ratchet_version; // for init, just like in the normal constructor + let mut map = VecDeque::with_capacity(MAX_RATCHETS_IN_MEMORY); + map.insert(0, entropy_bank.1); Self { - cid: drill.0.get_cid(), - oldest_hyper_ratchet_version, - most_recent_hyper_ratchet_version, + cid: entropy_bank.0.get_cid(), + oldest_stacked_ratchet_version, + most_recent_stacked_ratchet_version, map, - static_auxiliary_hyper_ratchet: drill.0, + static_auxiliary_stacked_ratchet: entropy_bank.0, } } } diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index e124ab389..c4af09ed0 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -12,7 +12,7 @@ mod tests { use citadel_crypt::packet_vector::PacketVector; use citadel_crypt::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupReceiver}; use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; - use citadel_crypt::toolset::{Toolset, UpdateStatus, MAX_HYPER_RATCHETS_IN_MEMORY}; + use citadel_crypt::toolset::{Toolset, ToolsetUpdateStatus, MAX_RATCHETS_IN_MEMORY}; #[cfg(not(target_family = "wasm"))] use citadel_io::tokio; use citadel_pqcrypto::constructor_opts::ConstructorOpts; @@ -189,9 +189,9 @@ mod tests { println!("{:?}\n", &cids_order_decrypt); let output = chain.links.iter().rfold(onion_packet, |mut acc, (cid, container)| { println!("At {} (onion packet len: {})", cid, acc.len()); - let (pqc, drill) = container.get_hyper_ratchet(None).unwrap().message_pqc_drill(None); + let (pqc, entropy_bank) = container.get_stacked_ratchet(None).unwrap().message_pqc_entropy_bank(None); let payload = acc.split_off(HEADER_LEN); - drill.aes_gcm_decrypt(0, pqc, payload) + entropy_bank.aes_gcm_decrypt(0, pqc, payload) .map(|vec| bytes::BytesMut::from(&vec[..])).unwrap() }); @@ -219,18 +219,18 @@ mod tests { } #[test] - fn hyper_ratchets() { + fn stacked_ratchets() { citadel_logging::setup_log(); for x in 0u8..KEM_ALGORITHM_COUNT { for sec in 0..SecurityLevel::Extreme.value() { - let _ = hyper_ratchet::( + let _ = stacked_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::AES_GCM_256, Some(sec.into()), false, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); - let _ = hyper_ratchet::( + let _ = stacked_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::ChaCha20Poly_1305, Some(sec.into()), false, @@ -242,18 +242,18 @@ mod tests { } #[test] - fn hyper_ratchets_fcm() { + fn stacked_ratchets_fcm() { citadel_logging::setup_log(); for x in 0u8..KEM_ALGORITHM_COUNT { for sec in 0..SecurityLevel::Extreme.value() { - let _ = hyper_ratchet::( + let _ = stacked_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::AES_GCM_256, Some(sec.into()), true, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); - let _ = hyper_ratchet::( + let _ = stacked_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::ChaCha20Poly_1305, Some(sec.into()), true, @@ -268,7 +268,7 @@ mod tests { fn security_levels() { citadel_logging::setup_log(); for sec in 0..SecurityLevel::Extreme.value() { - let ratchet = hyper_ratchet::( + let ratchet = stacked_ratchet::( KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256, Some(sec.into()), false, @@ -285,7 +285,7 @@ mod tests { } } - fn hyper_ratchet>( + fn stacked_ratchet>( algorithm: Z, security_level: Option, is_fcm: bool, @@ -295,32 +295,30 @@ mod tests { let algorithm = algorithm.into(); log::trace!(target: "citadel", "Using {:?} with {:?} @ {:?} security level | is FCM: {}", algorithm.kem_algorithm, algorithm.encryption_algorithm, security_level, is_fcm); let algorithm = Some(algorithm); - let count = (security_level.unwrap_or_default().value() + 1) as usize; - let mut alice_hyper_ratchet = R::Constructor::new_alice( - ConstructorOpts::new_vec_init(algorithm, count), + let security_level = security_level.unwrap_or_default(); + let mut alice_stacked_ratchet = R::Constructor::new_alice( + ConstructorOpts::new_vec_init(algorithm, security_level), 99, 0, - security_level, ) .unwrap(); - let transfer = alice_hyper_ratchet.stage0_alice().unwrap(); + let transfer = alice_stacked_ratchet.stage0_alice().unwrap(); - let bob_hyper_ratchet = R::Constructor::new_bob( + let mut bob_stacked_ratchet = R::Constructor::new_bob( 99, - 0, - ConstructorOpts::new_vec_init(algorithm, count), + ConstructorOpts::new_vec_init(algorithm, security_level), transfer, bob_psks, ) .unwrap(); - let transfer = bob_hyper_ratchet.stage0_bob().unwrap(); + let transfer = bob_stacked_ratchet.stage0_bob().unwrap(); - alice_hyper_ratchet + alice_stacked_ratchet .stage1_alice(transfer, alice_psks) .unwrap(); - let alice_hyper_ratchet = alice_hyper_ratchet.finish().unwrap(); - let bob_hyper_ratchet = bob_hyper_ratchet.finish().unwrap(); + let alice_stacked_ratchet = alice_stacked_ratchet.finish().unwrap(); + let bob_stacked_ratchet = bob_stacked_ratchet.finish().unwrap(); const MESSAGE: &[u8] = b"Hello, world!" as &[u8]; const HEADER_LEN: usize = 50; @@ -335,20 +333,20 @@ mod tests { let plaintext_packet = packet.clone(); - alice_hyper_ratchet - .protect_message_packet(security_level, HEADER_LEN, &mut packet) + alice_stacked_ratchet + .protect_message_packet(Some(security_level), HEADER_LEN, &mut packet) .unwrap(); assert_ne!(packet, plaintext_packet); let mut header = packet.split_to(HEADER_LEN); - bob_hyper_ratchet - .validate_message_packet(security_level, &header[..], &mut packet) + bob_stacked_ratchet + .validate_message_packet(Some(security_level), &header[..], &mut packet) .unwrap(); header.unsplit(packet); assert_eq!(header, plaintext_packet); - alice_hyper_ratchet + alice_stacked_ratchet } #[rstest] @@ -379,7 +377,7 @@ mod tests { ) { toolset::(enx, kem, sig); #[cfg(feature = "fcm")] - toolset::(enx, kem, sig); + toolset::(enx, kem, sig); } fn toolset(enx: EncryptionAlgorithm, kem: KemAlgorithm, sig: SigAlgorithm) { @@ -413,29 +411,34 @@ mod tests { ) .unwrap(); match res { - UpdateStatus::Committed { .. } => { - assert!(x < MAX_HYPER_RATCHETS_IN_MEMORY as u32); - assert_eq!(0, toolset.get_oldest_hyper_ratchet_version()); - assert_eq!(x, toolset.get_most_recent_hyper_ratchet_version()); + ToolsetUpdateStatus::Committed { .. } => { + assert!(x < MAX_RATCHETS_IN_MEMORY as u32); + assert_eq!(0, toolset.get_oldest_stacked_ratchet_version()); + assert_eq!(x, toolset.get_most_recent_stacked_ratchet_version()); } - UpdateStatus::CommittedNeedsSynchronization { old_version, .. } => { + ToolsetUpdateStatus::CommittedNeedsSynchronization { + oldest_version: old_version, + .. + } => { assert_eq!(old_version, 0); // we're not truncating it yet, so it should be 0 - assert!(x + 1 > MAX_HYPER_RATCHETS_IN_MEMORY as u32); - assert_eq!(0, toolset.get_oldest_hyper_ratchet_version()); // this shouldn't change because the oldest needs to be manually removed - assert_eq!(x, toolset.get_most_recent_hyper_ratchet_version()); + assert!(x + 1 >= MAX_RATCHETS_IN_MEMORY as u32); + assert_eq!(0, toolset.get_oldest_stacked_ratchet_version()); // this shouldn't change because the oldest needs to be manually removed + assert_eq!(x, toolset.get_most_recent_stacked_ratchet_version()); } } } for x in 0..COUNT { - if toolset.deregister_oldest_hyper_ratchet(x).is_ok() { - assert_eq!(x + 1, toolset.get_oldest_hyper_ratchet_version()); + if toolset.deregister_oldest_stacked_ratchet(x).is_ok() { + assert_eq!(x + 1, toolset.get_oldest_stacked_ratchet_version()); } else { - assert_eq!(toolset.len(), MAX_HYPER_RATCHETS_IN_MEMORY); + assert_eq!(toolset.len(), MAX_RATCHETS_IN_MEMORY - 1); assert_eq!( - toolset.get_oldest_hyper_ratchet_version(), - COUNT - MAX_HYPER_RATCHETS_IN_MEMORY as u32 + toolset + .get_oldest_stacked_ratchet_version() + .saturating_sub(1), + COUNT - MAX_RATCHETS_IN_MEMORY as u32 ); } } @@ -453,19 +456,21 @@ mod tests { .0, ) .unwrap(); - assert_eq!(toolset.len(), MAX_HYPER_RATCHETS_IN_MEMORY + 1); + assert_eq!(toolset.len(), MAX_RATCHETS_IN_MEMORY); assert_eq!( - toolset.get_oldest_hyper_ratchet_version(), - toolset.get_most_recent_hyper_ratchet_version() - MAX_HYPER_RATCHETS_IN_MEMORY as u32 + toolset + .get_oldest_stacked_ratchet_version() + .saturating_sub(1), + toolset.get_most_recent_stacked_ratchet_version() - MAX_RATCHETS_IN_MEMORY as u32 ); toolset - .deregister_oldest_hyper_ratchet( - toolset.get_most_recent_hyper_ratchet_version() - - MAX_HYPER_RATCHETS_IN_MEMORY as u32, + .deregister_oldest_stacked_ratchet( + toolset.get_most_recent_stacked_ratchet_version() + - (MAX_RATCHETS_IN_MEMORY - 1) as u32, // Add one since the max count is only for when it's full and temporarily fills the full buffer ) .unwrap(); - assert_eq!(toolset.len(), MAX_HYPER_RATCHETS_IN_MEMORY); + assert_eq!(toolset.len(), MAX_RATCHETS_IN_MEMORY - 1); } fn gen( @@ -476,18 +481,15 @@ mod tests { bob_psks: &[Vec], alice_psks: &[Vec], ) -> (R, R) { - let count = sec.value() as usize + 1; let mut alice = R::Constructor::new_alice( - ConstructorOpts::new_vec_init(Some(algorithm), count), + ConstructorOpts::new_vec_init(Some(algorithm), sec), cid, version, - Some(sec), ) .unwrap(); - let bob = R::Constructor::new_bob( + let mut bob = R::Constructor::new_bob( cid, - version, - ConstructorOpts::new_vec_init(Some(algorithm), count), + ConstructorOpts::new_vec_init(Some(algorithm), sec), alice.stage0_alice().unwrap(), bob_psks, ) @@ -525,7 +527,7 @@ mod tests { ) { toolset_wrapping_vers::(enx, kem, sig); #[cfg(feature = "fcm")] - toolset_wrapping_vers::(enx, kem, sig); + toolset_wrapping_vers::(enx, kem, sig); } fn toolset_wrapping_vers( @@ -545,7 +547,7 @@ mod tests { &PRE_SHARED_KEYS, ); let mut toolset = Toolset::new_debug(cid, hr.0, vers, vers); - let r = toolset.get_hyper_ratchet(vers).unwrap(); + let r = toolset.get_stacked_ratchet(vers).unwrap(); assert_eq!(r.version(), vers); const COUNT: usize = 100; @@ -569,24 +571,27 @@ mod tests { .0, ) .unwrap(); - let ratchet = toolset.get_hyper_ratchet(cur_vers).unwrap(); + let ratchet = toolset.get_stacked_ratchet(cur_vers).unwrap(); assert_eq!(ratchet.version(), cur_vers); cur_vers = cur_vers.wrapping_add(1); insofar += 1; } - assert_eq!(toolset.get_oldest_hyper_ratchet().unwrap().version(), vers); + assert_eq!( + toolset.get_oldest_stacked_ratchet().unwrap().version(), + vers + ); let mut amt_culled = 0; for _ in 0..COUNT { - if toolset.len() == MAX_HYPER_RATCHETS_IN_MEMORY { + if toolset.len() == MAX_RATCHETS_IN_MEMORY { continue; } toolset - .deregister_oldest_hyper_ratchet(vers.wrapping_add(amt_culled)) + .deregister_oldest_stacked_ratchet(vers.wrapping_add(amt_culled)) .unwrap(); amt_culled += 1; assert_eq!( - toolset.get_oldest_hyper_ratchet().unwrap().version(), + toolset.get_oldest_stacked_ratchet().unwrap().version(), vers.wrapping_add(amt_culled) ); } @@ -626,7 +631,7 @@ mod tests { |decrypted, plaintext, _, _| debug_assert_eq!(decrypted, plaintext), ); #[cfg(feature = "fcm")] - scrambler_transmission_spectrum::( + scrambler_transmission_spectrum::( enx, kem, sig, @@ -684,7 +689,7 @@ mod tests { scrambler_transmission_spectrum::(enx, kem, sig, tx_type, verifier); #[cfg(feature = "fcm")] - scrambler_transmission_spectrum::( + scrambler_transmission_spectrum::( enx, kem, sig, tx_type, verifier, ); } @@ -736,7 +741,7 @@ mod tests { ObjectId::zero(), 0, transfer_type.clone(), - |_vec, _drill, _target_cid, _, buffer| { + |_vec, _entropy_bank, _target_cid, _, buffer| { for x in 0..HEADER_SIZE_BYTES { buffer.put_u8(x as u8) } @@ -909,7 +914,7 @@ mod tests { let source = PathBuf::from("../resources/TheBridge.pdf"); let (group_sender_tx, mut group_sender_rx) = channel(1); let (_stop_tx, stop_rx) = tokio::sync::oneshot::channel(); - let (bytes, _num_groups, _mxbpg) = scramble_encrypt_source::<_, _, HEADER_LEN>( + let (bytes, _num_groups, _mxbpg) = scramble_encrypt_source::<_, _, HEADER_LEN, _>( source, None, ObjectId::zero(), @@ -997,7 +1002,7 @@ mod tests { KemAlgorithm::Kyber, SigAlgorithm::Falcon1024 )] - fn test_drill_encrypt_decrypt_basic( + fn test_entropy_bank_encrypt_decrypt_basic( #[case] enx: EncryptionAlgorithm, #[case] kem: KemAlgorithm, #[case] sig: SigAlgorithm, @@ -1017,12 +1022,12 @@ mod tests { KemAlgorithm::Kyber, SigAlgorithm::None )] - fn test_drill_encrypt_decrypt_basic_bad_psks( + fn test_entropy_bank_encrypt_decrypt_basic_bad_psks( #[case] enx: EncryptionAlgorithm, #[case] kem: KemAlgorithm, #[case] sig: SigAlgorithm, ) { - citadel_logging::setup_log_no_panic_hook(); + citadel_logging::should_panic_test(); test_harness_with_psks( enx + kem + sig, &PRE_SHARED_KEYS, @@ -1056,41 +1061,7 @@ mod tests { KemAlgorithm::Kyber, SigAlgorithm::Falcon1024 )] - fn test_drill_encrypt_decrypt_scrambler( - #[case] enx: EncryptionAlgorithm, - #[case] kem: KemAlgorithm, - #[case] sig: SigAlgorithm, - ) { - citadel_logging::setup_log(); - test_harness(enx + kem + sig, |alice, bob, _, data| { - let encrypted = alice.encrypt_scrambler(data).unwrap(); - let decrypted = bob.decrypt_scrambler(encrypted).unwrap(); - assert_eq!(decrypted, data); - }); - } - - #[rstest] - #[case( - EncryptionAlgorithm::AES_GCM_256, - KemAlgorithm::Kyber, - SigAlgorithm::None - )] - #[case( - EncryptionAlgorithm::Ascon80pq, - KemAlgorithm::Kyber, - SigAlgorithm::None - )] - #[case( - EncryptionAlgorithm::ChaCha20Poly_1305, - KemAlgorithm::Kyber, - SigAlgorithm::None - )] - #[case( - EncryptionAlgorithm::Kyber, - KemAlgorithm::Kyber, - SigAlgorithm::Falcon1024 - )] - fn test_drill_local_encrypt_decrypt( + fn test_entropy_bank_local_encrypt_decrypt( #[case] enx: EncryptionAlgorithm, #[case] kem: KemAlgorithm, #[case] sig: SigAlgorithm, diff --git a/citadel_logging/src/lib.rs b/citadel_logging/src/lib.rs index 9d197f0a3..bc0a5ac33 100644 --- a/citadel_logging/src/lib.rs +++ b/citadel_logging/src/lib.rs @@ -60,7 +60,6 @@ //! By default, `setup_log()` installs a panic hook that logs the panic //! information before exiting. If you need to use a custom panic hook, //! use `setup_log_no_panic_hook()` instead. - pub use tracing::{self, debug, error, info, instrument, trace, warn}; use tracing_subscriber::fmt::format::FmtSpan; use tracing_subscriber::fmt::SubscriberBuilder; @@ -89,7 +88,7 @@ use tracing_subscriber::EnvFilter; /// the process exits with status code 1. pub fn setup_log() { std::panic::set_hook(Box::new(|info| { - error!(target: "citadel", "Panic occurred: {}", info); + error!(target: "citadel", "Panic occurred: {info}"); std::process::exit(1); })); @@ -119,7 +118,14 @@ pub fn setup_log_no_panic_hook() { .with_line_number(true) // Include line numbers in log output .with_file(true) // Include file names in log output .with_span_events(FmtSpan::NONE) // Don't log span lifecycle events - .with_env_filter(EnvFilter::from_default_env()) // Use RUST_LOG env var + .with_env_filter(EnvFilter::from_default_env()) + .without_time() // Use RUST_LOG env var .finish() .try_init(); } + +/// Disables the panic hook installed by `setup_log()`. +pub fn should_panic_test() { + let _ = std::panic::take_hook(); + setup_log_no_panic_hook(); +} diff --git a/citadel_pqcrypto/src/constructor_opts.rs b/citadel_pqcrypto/src/constructor_opts.rs index 883089599..844e0958b 100644 --- a/citadel_pqcrypto/src/constructor_opts.rs +++ b/citadel_pqcrypto/src/constructor_opts.rs @@ -44,16 +44,36 @@ //! - [`citadel_pqcrypto::key_store`] - Secure key storage functionality use citadel_types::crypto::CryptoParameters; +use citadel_types::prelude::SecurityLevel; use serde::{Deserialize, Serialize}; -/// WARNING! `previous_shared_secret` should never leave a node; it should only be extracted from the previous PQC when bob is constructing his PQC +/// WARNING! this struct, especially the `chain`, should never leave a node; it should only be extracted from the previous PQC when bob is constructing his PQC #[derive(Clone, Default)] pub struct ConstructorOpts { pub cryptography: Option, pub chain: Option, } +pub trait ImpliedSecurityLevel { + fn implied_security_level(&self) -> SecurityLevel; +} + +impl ImpliedSecurityLevel for Vec { + fn implied_security_level(&self) -> SecurityLevel { + assert!( + !self.is_empty(), + "Security level cannot be derived from an empty vector" + ); + assert!( + self.len() < u8::MAX as usize, + "Security level does not fit in u8" + ); + SecurityLevel::from(self.len().saturating_sub(1) as u8) + } +} + impl ConstructorOpts { + /// Starts off a f(0) chain with a single layer of ratcheting pub fn new_init(cryptography: Option>) -> Self { Self { cryptography: cryptography.map(|r| r.into()), @@ -63,13 +83,15 @@ impl ConstructorOpts { pub fn new_vec_init( cryptography: Option>, - count: usize, + security_level: SecurityLevel, ) -> Vec { + let count = security_level.value() as usize + 1; let settings = cryptography.map(|r| r.into()).unwrap_or_default(); (0..count).map(|_| Self::new_init(Some(settings))).collect() } - pub fn new_from_previous( + /// Generates a new f(n) -> f(n +1) chain + pub fn new_ratcheted( cryptography: Option>, previous_shared_secret: RecursiveChain, ) -> Self { diff --git a/citadel_pqcrypto/src/lib.rs b/citadel_pqcrypto/src/lib.rs index 6281edb77..2246280d2 100644 --- a/citadel_pqcrypto/src/lib.rs +++ b/citadel_pqcrypto/src/lib.rs @@ -294,10 +294,10 @@ impl PostQuantumContainer { /// Creates a new [PostQuantumContainer] for Bob. This will panic if the algorithm is /// invalid - pub fn new_bob( + pub fn new_bob>( opts: ConstructorOpts, tx_params: AliceToBobTransferParameters, - psks: &[Vec], + psks: &[T], ) -> Result { let pq_node = PQNode::Bob; let params = opts.cryptography.unwrap_or_default(); @@ -331,14 +331,14 @@ impl PostQuantumContainer { } /// `psks`: Pre-shared keys - fn generate_recursive_keystore( + fn generate_recursive_keystore>( pq_node: PQNode, params: CryptoParameters, sig: Option, ss: Arc>>, previous_chain: Option<&RecursiveChain>, kex: PostQuantumMetaKex, - psks: &[Vec], + psks: &[T], ) -> Result<(RecursiveChain, KeyStore), Error> { let (chain, alice_key, bob_key) = if let Some(prev) = previous_chain { // prev = C_n @@ -351,7 +351,7 @@ impl PostQuantumContainer { .chain .iter() .chain(ss.iter()) - .chain(psks.iter().flatten()) + .chain(psks.iter().flat_map(|r| r.as_ref())) .copied() .collect::>()[..], ); @@ -414,7 +414,7 @@ impl PostQuantumContainer { let mut hasher_temp = sha3::Sha3_512::new(); hasher_temp.update( ss.iter() - .chain(psks.iter().flatten()) + .chain(psks.iter().flat_map(|r| r.as_ref())) .copied() .collect::>(), ); @@ -491,7 +491,7 @@ impl PostQuantumContainer { } /// This should always be called after deserialization - fn load_symmetric_keys(&mut self, psks: &[Vec]) -> Result<(), Error> { + fn load_symmetric_keys>(&mut self, psks: &[T]) -> Result<(), Error> { let pq_node = self.node; let params = self.params; let sig = self.data.sig().cloned(); @@ -516,10 +516,10 @@ impl PostQuantumContainer { } /// Internally creates shared key after bob sends a response back to Alice - pub fn alice_on_receive_ciphertext( + pub fn alice_on_receive_ciphertext>( &mut self, params: BobToAliceTransferParameters, - psks: &[Vec], + psks: &[T], ) -> Result<(), Error> { self.data.alice_on_receive_ciphertext(params)?; let _ss = self.data.get_shared_secret()?; // call once to load internally diff --git a/citadel_pqcrypto/src/wire.rs b/citadel_pqcrypto/src/wire.rs index 9bff76fb2..d989f4e61 100644 --- a/citadel_pqcrypto/src/wire.rs +++ b/citadel_pqcrypto/src/wire.rs @@ -48,7 +48,7 @@ use serde::{Deserialize, Serialize}; use std::sync::Arc; use zeroize::{ZeroizeOnDrop, Zeroizing}; -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub enum AliceToBobTransferParameters { MixedAsymmetric { alice_pk: Arc>>, @@ -63,7 +63,7 @@ pub enum AliceToBobTransferParameters { }, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub enum BobToAliceTransferParameters { MixedAsymmetric { bob_ciphertext_signature: Arc>>, diff --git a/citadel_pqcrypto/tests/primary.rs b/citadel_pqcrypto/tests/primary.rs index 9eb2c45c2..161bf9492 100644 --- a/citadel_pqcrypto/tests/primary.rs +++ b/citadel_pqcrypto/tests/primary.rs @@ -22,12 +22,12 @@ mod tests { pub static ref PRE_SHARED_KEYS2: Vec> = vec!["World".into(), "Hello".into()]; } - fn gen( + fn gen, R: AsRef<[u8]>>( kem_algorithm: KemAlgorithm, encryption_algorithm: EncryptionAlgorithm, sig_alg: SigAlgorithm, - bob_psks: &[Vec], - alice_psks: &[Vec], + bob_psks: &[T], + alice_psks: &[R], ) -> (PostQuantumContainer, PostQuantumContainer) { log::trace!(target: "citadel", "Test algorithm {:?} w/ {:?}", kem_algorithm, encryption_algorithm); let mut alice_container = PostQuantumContainer::new_alice(ConstructorOpts::new_init(Some( @@ -52,7 +52,7 @@ mod tests { #[test] fn runit() { - run( + run::>( 0, EncryptionAlgorithm::AES_GCM_256, SigAlgorithm::None, @@ -60,7 +60,7 @@ mod tests { &PRE_SHARED_KEYS, ) .unwrap(); - run( + run::>( 0, EncryptionAlgorithm::ChaCha20Poly_1305, SigAlgorithm::None, @@ -68,7 +68,7 @@ mod tests { &PRE_SHARED_KEYS, ) .unwrap(); - run( + run::>( 0, EncryptionAlgorithm::Ascon80pq, SigAlgorithm::None, @@ -78,12 +78,12 @@ mod tests { .unwrap(); } - fn run( + fn run>( algorithm: u8, encryption_algorithm: EncryptionAlgorithm, signature_algorithm: SigAlgorithm, - bob_psk: &[Vec], - alice_psk: &[Vec], + bob_psk: &[T], + alice_psk: &[T], ) -> Result<(), Box> { let kem_algorithm = KemAlgorithm::from_u8(algorithm).unwrap(); log::trace!(target: "citadel", "Test: {:?} w/ {:?} w/ {:?}", kem_algorithm, encryption_algorithm, signature_algorithm); @@ -388,7 +388,7 @@ mod tests { citadel_logging::setup_log(); for algorithm in KemAlgorithm::list() { log::trace!(target: "citadel", "About to test {:?}", algorithm); - run( + run::>( algorithm.as_u8(), EncryptionAlgorithm::AES_GCM_256, SigAlgorithm::None, @@ -396,7 +396,7 @@ mod tests { &PRE_SHARED_KEYS, ) .unwrap(); - run( + run::>( algorithm.as_u8(), EncryptionAlgorithm::ChaCha20Poly_1305, SigAlgorithm::None, @@ -404,7 +404,7 @@ mod tests { &PRE_SHARED_KEYS, ) .unwrap(); - run( + run::>( algorithm.as_u8(), EncryptionAlgorithm::Ascon80pq, SigAlgorithm::None, @@ -413,7 +413,7 @@ mod tests { ) .unwrap(); if algorithm == KemAlgorithm::Kyber { - run( + run::>( algorithm.as_u8(), EncryptionAlgorithm::Kyber, SigAlgorithm::Falcon1024, @@ -428,7 +428,7 @@ mod tests { #[test] fn test_kyber() { citadel_logging::setup_log(); - run( + run::>( KemAlgorithm::Kyber.as_u8(), EncryptionAlgorithm::Kyber, SigAlgorithm::Falcon1024, @@ -438,18 +438,18 @@ mod tests { .unwrap() } - #[should_panic] #[test] + #[should_panic(expected = "EncryptionFailure")] fn test_kyber_bad_psks() { - citadel_logging::setup_log_no_panic_hook(); - run( + citadel_logging::should_panic_test(); + run::>( KemAlgorithm::Kyber.as_u8(), EncryptionAlgorithm::AES_GCM_256, SigAlgorithm::Falcon1024, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS2, ) - .unwrap() + .unwrap(); } #[test] diff --git a/citadel_proto/backup/constants.rs b/citadel_proto/backup/constants.rs index 584bdaafd..fbe5bfd48 100644 --- a/citadel_proto/backup/constants.rs +++ b/citadel_proto/backup/constants.rs @@ -44,7 +44,7 @@ pub const KEEP_ALIVE_INTERVAL_MS: u64 = 15000; /// The keep alive max interval pub const KEEP_ALIVE_TIMEOUT_NS: i64 = (KEEP_ALIVE_INTERVAL_MS * 3 * 1_000_000) as i64; // 1ms = 1 million ns -/// Timeout for the drill update subroutine +/// Timeout for the entropy_bank update subroutine pub const DRILL_UPDATE_TIMEOUT_NS: i64 = KEEP_ALIVE_TIMEOUT_NS; /// For setting up the GroupReceivers pub const GROUP_TIMEOUT_MS: usize = KEEP_ALIVE_INTERVAL_MS as usize; @@ -73,15 +73,15 @@ pub const MULTIPORT_START: u16 = 25000; pub const MULTIPORT_END: u16 = 1 + MULTIPORT_START; pub const PRIMARY_PORT: u16 = 25021; pub const DEFAULT_PQC_ALGORITHM: u8 = citadel_pqcrypto::algorithm_dictionary::FIRESABER; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_LOW_BASE: u64 = 1 * 240 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_MEDIUM_BASE: u64 = 1 * 240 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_HIGH_BASE: u64 = 1 * 240 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_ULTRA_BASE: u64 = 1 * 240 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_DIVINE_BASE: u64 = 1 * 240 * 1_000_000_000; /// For ensuring that the hole-punching process begin at about the same time (required) diff --git a/citadel_proto/src/auth.rs b/citadel_proto/src/auth.rs index 9172d270a..583e980d1 100644 --- a/citadel_proto/src/auth.rs +++ b/citadel_proto/src/auth.rs @@ -82,7 +82,7 @@ impl AuthenticationRequest { } } - pub fn implicated_cid(&self) -> Option { + pub fn session_cid(&self) -> Option { match self { AuthenticationRequest::Credentialed { id, .. } => { if let UserIdentifier::ID(cid) = id { diff --git a/citadel_proto/src/constants.rs b/citadel_proto/src/constants.rs index 81da28e8a..a28c4fe76 100644 --- a/citadel_proto/src/constants.rs +++ b/citadel_proto/src/constants.rs @@ -106,7 +106,7 @@ pub const KEEP_ALIVE_INTERVAL_MS: u64 = 60000 * 15; // every 15 minutes /// The keep alive max interval pub const KEEP_ALIVE_TIMEOUT_NS: i64 = (KEEP_ALIVE_INTERVAL_MS * 3 * 1_000_000) as i64; // 1ms = 1 million ns -/// Timeout for the drill update subroutine +/// Timeout for the entropy_bank update subroutine pub const DRILL_UPDATE_TIMEOUT_NS: i64 = KEEP_ALIVE_TIMEOUT_NS; /// For setting up the GroupReceivers pub const GROUP_TIMEOUT_MS: usize = KEEP_ALIVE_INTERVAL_MS as usize; @@ -133,15 +133,15 @@ pub const MULTIPORT_START: u16 = 25000; //pub const MULTIPORT_END: u16 = citadel_crypt::entropy_bank::PORT_RANGE as u16 + MULTIPORT_START; pub const MULTIPORT_END: u16 = 1 + MULTIPORT_START; pub const PRIMARY_PORT: u16 = 25021; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_LOW_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_MEDIUM_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_HIGH_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_ULTRA_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per drill update (nanoseconds per update) +/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) pub const DRILL_UPDATE_FREQUENCY_DIVINE_BASE: u64 = 480 * 1_000_000_000; /// For ensuring that the hole-punching process begin at about the same time (required) /// this is applied to the ping. If the ping is 200ms, the a multiplier of 2.0 will mean that in 200*2.0 = 400ms, diff --git a/citadel_proto/src/kernel/kernel_communicator.rs b/citadel_proto/src/kernel/kernel_communicator.rs index dcd24e6d9..276f1ebf6 100644 --- a/citadel_proto/src/kernel/kernel_communicator.rs +++ b/citadel_proto/src/kernel/kernel_communicator.rs @@ -54,7 +54,7 @@ pub(crate) struct CallbackNotifier { #[derive(Debug, Hash, Copy, Clone, Eq, PartialEq)] pub struct CallbackKey { pub ticket: Ticket, - pub implicated_cid: Option, + pub session_cid: Option, } fn search_for_value<'a>( @@ -64,18 +64,18 @@ fn search_for_value<'a>( let expected_ticket = callback_key_received.ticket; for (key, notifier) in map.iter_mut() { let ticket = key.ticket; - let cid_opt = key.implicated_cid; + let cid_opt = key.session_cid; // If we locally expect a cid, then, we require the same cid to be present in the received callback_key // If we locally do not expect a cid, then, we don't need to check the cid if let Some(cid_expected) = cid_opt { - if let Some(cid_received) = callback_key_received.implicated_cid { + if let Some(cid_received) = callback_key_received.session_cid { if expected_ticket == key.ticket && cid_expected == cid_received { return Some(( notifier, CallbackKey { ticket, - implicated_cid: Some(cid_expected), + session_cid: Some(cid_expected), }, )); } else { @@ -93,7 +93,7 @@ fn search_for_value<'a>( notifier, CallbackKey { ticket, - implicated_cid: None, + session_cid: None, }, )); } else { @@ -107,17 +107,17 @@ fn search_for_value<'a>( } impl CallbackKey { - pub fn new(ticket: Ticket, implicated_cid: u64) -> Self { + pub fn new(ticket: Ticket, session_cid: u64) -> Self { Self { ticket, - implicated_cid: Some(implicated_cid), + session_cid: Some(session_cid), } } pub fn ticket_only(ticket: Ticket) -> Self { Self { ticket, - implicated_cid: None, + session_cid: None, } } } diff --git a/citadel_proto/src/kernel/kernel_executor.rs b/citadel_proto/src/kernel/kernel_executor.rs index 7e06ca834..20d3865ee 100644 --- a/citadel_proto/src/kernel/kernel_executor.rs +++ b/citadel_proto/src/kernel/kernel_executor.rs @@ -40,10 +40,10 @@ use std::pin::Pin; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::runtime::Handle; -use futures::TryStreamExt; - use citadel_user::account_manager::AccountManager; +use futures::TryStreamExt; use crate::error::NetworkError; use crate::kernel::kernel_communicator::KernelAsyncCallbackHandler; @@ -56,13 +56,13 @@ use crate::proto::packet_processor::includes::Duration; use crate::proto::remote::NodeRemote; /// Creates a [KernelExecutor] -pub struct KernelExecutor { - server_remote: Option, +pub struct KernelExecutor, R: Ratchet> { + server_remote: Option>, server_to_kernel_rx: Option>, shutdown_alerter_rx: Option>, callback_handler: Option, context: Option, - account_manager: AccountManager, + account_manager: AccountManager, kernel_executor_settings: KernelExecutorSettings, kernel: K, } @@ -74,11 +74,11 @@ pub type LocalSet = (); type KernelContext = (Handle, Pin>, Option); -impl KernelExecutor { +impl, R: Ratchet> KernelExecutor { /// Creates a new [KernelExecutor]. Panics if the server cannot start /// - underlying_proto: The proto to use for client to server communications - pub async fn new(args: KernelExecutorArguments) -> Result { - let KernelExecutorArguments:: { + pub async fn new(args: KernelExecutorArguments) -> Result { + let KernelExecutorArguments:: { rt, hypernode_type, account_manager, @@ -93,7 +93,7 @@ impl KernelExecutor { let (server_shutdown_alerter_tx, server_shutdown_alerter_rx) = citadel_io::tokio::sync::oneshot::channel(); // After this gets called, the server starts running and we get a remote - let (remote, future, localset_opt, callback_handler) = Node::init( + let (remote, future, localset_opt, callback_handler) = Node::::init( hypernode_type, server_to_kernel_tx, account_manager.clone(), @@ -171,7 +171,7 @@ impl KernelExecutor { async fn kernel_inner_loop( kernel: &mut K, mut server_to_kernel_rx: UnboundedReceiver, - citadel_server_remote: NodeRemote, + citadel_server_remote: NodeRemote, shutdown: citadel_io::tokio::sync::oneshot::Receiver<()>, callback_handler: KernelAsyncCallbackHandler, kernel_settings: KernelExecutorSettings, @@ -235,7 +235,7 @@ impl KernelExecutor { exec_res.and(stop_res.map(|_| ())) } - pub fn account_manager(&self) -> &AccountManager { + pub fn account_manager(&self) -> &AccountManager { &self.account_manager } } diff --git a/citadel_proto/src/kernel/kernel_trait.rs b/citadel_proto/src/kernel/kernel_trait.rs index 85ff0b17c..bd47907ef 100644 --- a/citadel_proto/src/kernel/kernel_trait.rs +++ b/citadel_proto/src/kernel/kernel_trait.rs @@ -32,14 +32,15 @@ use crate::error::NetworkError; use crate::proto::node_result::NodeResult; use crate::proto::remote::NodeRemote; use auto_impl::auto_impl; +use citadel_crypt::stacked_ratchet::Ratchet; /// The [`NetKernel`] is the thread-safe interface between the single-threaded OR multi-threaded async /// protocol and your network application #[async_trait] #[auto_impl(Box, &mut)] -pub trait NetKernel: Send + Sync { +pub trait NetKernel: Send + Sync { /// when the kernel executes, it will be given a handle to the server - fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError>; + fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError>; /// After the server remote is passed to the kernel, this function will be called once to allow the application to make any initial calls async fn on_start(&self) -> Result<(), NetworkError>; /// When the server processes a valid entry, the value is sent here. Each call to 'on_node_event_received' is done diff --git a/citadel_proto/src/kernel/mod.rs b/citadel_proto/src/kernel/mod.rs index 2169625d3..d31f9095b 100644 --- a/citadel_proto/src/kernel/mod.rs +++ b/citadel_proto/src/kernel/mod.rs @@ -51,6 +51,10 @@ //! - `HdpServer`: Low-level protocol implementation //! - `AccountManager`: User account management +use crate::error::NetworkError; +use crate::macros::ContextRequirements; +use crate::prelude::{PreSharedKey, ServerUnderlyingProtocol}; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::macros::support::Future; use citadel_io::tokio::runtime::Handle; use citadel_user::account_manager::AccountManager; @@ -58,10 +62,6 @@ use citadel_wire::exports::ClientConfig; use citadel_wire::hypernode_type::NodeType; use std::sync::Arc; -use crate::error::NetworkError; -use crate::macros::ContextRequirements; -use crate::prelude::{PreSharedKey, ServerUnderlyingProtocol}; - /// for handling easy asynchronous callbacks pub mod kernel_communicator; /// The mid-level entity in this crate which uses a multithreaded tokio runtime @@ -88,10 +88,10 @@ impl KernelExecutorSettings { } } -pub struct KernelExecutorArguments { +pub struct KernelExecutorArguments { pub rt: Handle, pub hypernode_type: NodeType, - pub account_manager: AccountManager, + pub account_manager: AccountManager, pub kernel: K, pub underlying_proto: ServerUnderlyingProtocol, pub client_config: Option>, diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index 13365eef9..8b71ff112 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -111,7 +111,7 @@ pub mod macros { impl SyncContextRequirements for T {} pub type WeakBorrowType = std::rc::Weak>; - pub type SessionBorrow<'a> = std::cell::RefMut<'a, CitadelSessionInner>; + pub type SessionBorrow<'a, R> = std::cell::RefMut<'a, CitadelSessionInner>; pub struct WeakBorrow { pub inner: std::rc::Weak>, @@ -152,47 +152,90 @@ pub mod macros { } macro_rules! define_outer_struct_wrapper { - ($struct_name:ident, $inner:ty) => { - #[derive(Clone)] - pub struct $struct_name { - pub inner: std::rc::Rc>, - } + // Version with generic parameters + ($struct_name:ident, $inner:ident, <$($generic:ident $(: $bound:path)?),*>, <$($use_generic:ident),*>) => { + #[derive(Clone)] + pub struct $struct_name<$($generic $(: $bound)?),*> { + pub inner: std::rc::Rc>>, + } - impl $struct_name { - #[allow(dead_code)] - pub fn as_weak(&self) -> crate::macros::WeakBorrow<$inner> { - crate::macros::WeakBorrow { - inner: std::rc::Rc::downgrade(&self.inner), - } + impl<$($generic $(: $bound)?),*> $struct_name<$($use_generic),*> { + #[allow(dead_code)] + pub fn as_weak(&self) -> crate::macros::WeakBorrow<$inner<$($use_generic),*>> { + crate::macros::WeakBorrow { + inner: std::rc::Rc::downgrade(&self.inner), } + } - #[allow(dead_code)] - pub fn upgrade_weak( - this: &crate::macros::WeakBorrow<$inner>, - ) -> Option<$struct_name> { - this.inner.upgrade().map(|inner| Self { inner }) - } + #[allow(dead_code)] + pub fn upgrade_weak( + this: &crate::macros::WeakBorrow<$inner<$($use_generic),*>>, + ) -> Option<$struct_name<$($use_generic),*>> { + this.inner.upgrade().map(|inner| Self { inner }) + } + + #[allow(dead_code)] + pub fn strong_count(&self) -> usize { + std::rc::Rc::strong_count(&self.inner) + } - #[allow(dead_code)] - pub fn strong_count(&self) -> usize { - std::rc::Rc::strong_count(&self.inner) + #[allow(dead_code)] + pub fn weak_count(&self) -> usize { + std::rc::Rc::weak_count(&self.inner) + } + } + + impl<$($generic $(: $bound)?),*> From<$inner<$($use_generic),*>> for $struct_name<$($use_generic),*> { + fn from(inner: $inner<$($use_generic),*>) -> Self { + Self { + inner: create_inner!(inner), } + } + } + }; - #[allow(dead_code)] - pub fn weak_count(&self) -> usize { - std::rc::Rc::weak_count(&self.inner) + // Simple version without generic parameters + ($struct_name:ident, $inner:ty) => { + #[derive(Clone)] + pub struct $struct_name { + pub inner: std::rc::Rc>, + } + + impl $struct_name { + #[allow(dead_code)] + pub fn as_weak(&self) -> crate::macros::WeakBorrow<$inner> { + crate::macros::WeakBorrow { + inner: std::rc::Rc::downgrade(&self.inner), } } - impl From<$inner> for $struct_name { - fn from(inner: $inner) -> Self { - Self { - inner: create_inner!(inner), - } + #[allow(dead_code)] + pub fn upgrade_weak( + this: &crate::macros::WeakBorrow<$inner>, + ) -> Option<$struct_name> { + this.inner.upgrade().map(|inner| Self { inner }) + } + + #[allow(dead_code)] + pub fn strong_count(&self) -> usize { + std::rc::Rc::strong_count(&self.inner) + } + + #[allow(dead_code)] + pub fn weak_count(&self) -> usize { + std::rc::Rc::weak_count(&self.inner) + } + } + + impl From<$inner> for $struct_name { + fn from(inner: $inner) -> Self { + Self { + inner: create_inner!(inner), } } - }; - } + } + }; +} macro_rules! create_inner { ($item:expr) => { @@ -261,7 +304,7 @@ pub mod macros { impl SyncContextRequirements for T {} pub type WeakBorrowType = std::sync::Weak>; - pub type SessionBorrow<'a> = citadel_io::RwLockWriteGuard<'a, CitadelSessionInner>; + pub type SessionBorrow<'a, R> = citadel_io::RwLockWriteGuard<'a, CitadelSessionInner>; pub struct WeakBorrow { pub inner: std::sync::Weak>, @@ -304,47 +347,90 @@ pub mod macros { } macro_rules! define_outer_struct_wrapper { - ($struct_name:ident, $inner:ty) => { - #[derive(Clone)] - pub struct $struct_name { - pub inner: std::sync::Arc>, - } + // Version with generic parameters + ($struct_name:ident, $inner:ident, <$($generic:ident $(: $bound:path)?),*>, <$($use_generic:ident),*>) => { + #[derive(Clone)] + pub struct $struct_name<$($generic $(: $bound)?),*> { + pub inner: std::sync::Arc>>, + } - impl $struct_name { - #[allow(dead_code)] - pub fn as_weak(&self) -> crate::macros::WeakBorrow<$inner> { - crate::macros::WeakBorrow { - inner: std::sync::Arc::downgrade(&self.inner), - } + impl<$($generic $(: $bound)?),*> $struct_name<$($use_generic),*> { + #[allow(dead_code)] + pub fn as_weak(&self) -> crate::macros::WeakBorrow<$inner<$($use_generic),*>> { + crate::macros::WeakBorrow { + inner: std::sync::Arc::downgrade(&self.inner), } + } - #[allow(dead_code)] - pub fn upgrade_weak( - this: &crate::macros::WeakBorrow<$inner>, - ) -> Option<$struct_name> { - this.inner.upgrade().map(|inner| Self { inner }) - } + #[allow(dead_code)] + pub fn upgrade_weak( + this: &crate::macros::WeakBorrow<$inner<$($use_generic),*>>, + ) -> Option<$struct_name<$($use_generic),*>> { + this.inner.upgrade().map(|inner| Self { inner }) + } + + #[allow(dead_code)] + pub fn strong_count(&self) -> usize { + std::sync::Arc::strong_count(&self.inner) + } - #[allow(dead_code)] - pub fn strong_count(&self) -> usize { - std::sync::Arc::strong_count(&self.inner) + #[allow(dead_code)] + pub fn weak_count(&self) -> usize { + std::sync::Arc::weak_count(&self.inner) + } + } + + impl<$($generic $(: $bound)?),*> From<$inner<$($use_generic),*>> for $struct_name<$($use_generic),*> { + fn from(inner: $inner<$($use_generic),*>) -> Self { + Self { + inner: create_inner!(inner), } + } + } + }; - #[allow(dead_code)] - pub fn weak_count(&self) -> usize { - std::sync::Arc::weak_count(&self.inner) + // Simple version without generic parameters + ($struct_name:ident, $inner:ty) => { + #[derive(Clone)] + pub struct $struct_name { + pub inner: std::sync::Arc>, + } + + impl $struct_name { + #[allow(dead_code)] + pub fn as_weak(&self) -> crate::macros::WeakBorrow<$inner> { + crate::macros::WeakBorrow { + inner: std::sync::Arc::downgrade(&self.inner), } } - impl From<$inner> for $struct_name { - fn from(inner: $inner) -> Self { - Self { - inner: create_inner!(inner), - } + #[allow(dead_code)] + pub fn upgrade_weak( + this: &crate::macros::WeakBorrow<$inner>, + ) -> Option<$struct_name> { + this.inner.upgrade().map(|inner| Self { inner }) + } + + #[allow(dead_code)] + pub fn strong_count(&self) -> usize { + std::sync::Arc::strong_count(&self.inner) + } + + #[allow(dead_code)] + pub fn weak_count(&self) -> usize { + std::sync::Arc::weak_count(&self.inner) + } + } + + impl From<$inner> for $struct_name { + fn from(inner: $inner) -> Self { + Self { + inner: create_inner!(inner), } } - }; - } + } + }; +} macro_rules! create_inner { ($item:expr) => { @@ -418,6 +504,8 @@ pub mod prelude { #[cfg(not(coverage))] pub use citadel_crypt::argon::autotuner::calculate_optimal_argon_params; pub use citadel_crypt::fcm::keys::FcmKeys; + pub use citadel_crypt::fcm::ratchet::ThinRatchet; + pub use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; pub use citadel_types::crypto::AlgorithmsExt; pub use citadel_types::crypto::SecBuffer; pub use citadel_user::account_manager::AccountManager; diff --git a/citadel_proto/src/proto/endpoint_crypto_accessor.rs b/citadel_proto/src/proto/endpoint_crypto_accessor.rs index 8ad7df408..cc5fc6d05 100644 --- a/citadel_proto/src/proto/endpoint_crypto_accessor.rs +++ b/citadel_proto/src/proto/endpoint_crypto_accessor.rs @@ -25,22 +25,19 @@ use crate::error::NetworkError; use crate::inner_arg::ExpectedInnerTargetMut; use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; use crate::proto::state_container::{StateContainer, StateContainerInner}; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; #[derive(Clone)] -pub enum EndpointCryptoAccessor { - P2P(u64, StateContainer), - C2S(StateContainer), +pub enum EndpointCryptoAccessor { + P2P(u64, StateContainer), + C2S(StateContainer), } -impl EndpointCryptoAccessor { +impl EndpointCryptoAccessor { // In P2P Mode, will return a state container pub fn borrow_hr(&self, vers: Option, access: F) -> Result where - F: for<'a> FnOnce( - &'a StackedRatchet, - &mut dyn ExpectedInnerTargetMut, - ) -> T, + F: for<'a> FnOnce(&'a R, &mut dyn ExpectedInnerTargetMut>) -> T, { match self { Self::P2P(peer_cid, state_container) => { @@ -48,7 +45,7 @@ impl EndpointCryptoAccessor { state_container .get_peer_session_crypto(*peer_cid) .ok_or(NetworkError::InternalError("Peer session crypto missing"))? - .get_hyper_ratchet(None) + .get_ratchet(None) .cloned() .map(|hr| access(&hr, &mut state_container)) .ok_or(NetworkError::InternalError("Ratchet does not exist")) @@ -58,7 +55,7 @@ impl EndpointCryptoAccessor { let mut state_container = inner_mut_state!(state_container); let hr = state_container .get_c2s_crypto() - .map(|r| r.get_hyper_ratchet(vers).cloned()) + .map(|r| r.get_ratchet(vers).cloned()) .ok_or(NetworkError::InternalError("C2S container does not exist"))? .ok_or(NetworkError::InternalError("Ratchet does not exist"))?; Ok(access(&hr, &mut state_container)) diff --git a/citadel_proto/src/proto/misc/dual_cell.rs b/citadel_proto/src/proto/misc/dual_cell.rs index 470bd9d56..c9da0d290 100644 --- a/citadel_proto/src/proto/misc/dual_cell.rs +++ b/citadel_proto/src/proto/misc/dual_cell.rs @@ -25,6 +25,7 @@ //! - `lock_holder.rs`: Resource locking use crate::macros::ContextRequirements; +use crate::proto::session::SessionState; use bytemuck::NoUninit; pub struct DualCell { @@ -45,7 +46,7 @@ impl DualCell { } #[cfg(feature = "multi-threaded")] { - let _ = self.inner.swap(new, atomic::Ordering::SeqCst); + let _ = self.inner.swap(new, atomic::Ordering::Relaxed); } } @@ -59,7 +60,7 @@ impl DualCell { } #[cfg(feature = "multi-threaded")] { - self.inner.load(atomic::Ordering::SeqCst) + self.inner.load(atomic::Ordering::Relaxed) } } } @@ -88,3 +89,9 @@ impl Clone for DualCell { } } } + +impl DualCell { + pub fn is_connected(&self) -> bool { + self.get() == SessionState::Connected + } +} diff --git a/citadel_proto/src/proto/mod.rs b/citadel_proto/src/proto/mod.rs index f0a9c5830..afa68aee5 100644 --- a/citadel_proto/src/proto/mod.rs +++ b/citadel_proto/src/proto/mod.rs @@ -37,6 +37,7 @@ use crate::proto::packet::HdpHeader; use crate::proto::session::CitadelSession; use crate::proto::state_container::StateContainerInner; use bytes::BytesMut; +use citadel_crypt::stacked_ratchet::Ratchet; /// For the custom BytesCodec that doesn't overflow pub(crate) mod codec; @@ -71,10 +72,10 @@ pub(crate) mod transfer_stats; pub(crate) mod validation; /// Returns the preferred primary stream for returning a response -pub(crate) fn get_preferred_primary_stream( +pub(crate) fn get_preferred_primary_stream( header: &HdpHeader, - session: &CitadelSession, - state_container: &StateContainerInner, + session: &CitadelSession, + state_container: &StateContainerInner, ) -> Option { if header.target_cid.get() != 0 { Some( diff --git a/citadel_proto/src/proto/node.rs b/citadel_proto/src/proto/node.rs index 857997057..5b5d08a4a 100644 --- a/citadel_proto/src/proto/node.rs +++ b/citadel_proto/src/proto/node.rs @@ -32,8 +32,7 @@ use std::net::ToSocketAddrs; use std::pin::Pin; use std::sync::Arc; -use futures::StreamExt; - +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::io::AsyncRead; use citadel_types::crypto::SecurityLevel; use citadel_user::account_manager::AccountManager; @@ -43,6 +42,7 @@ use citadel_wire::hypernode_type::NodeType; use citadel_wire::nat_identification::NatType; use citadel_wire::quic::{QuicEndpointConnector, QuicNode, QuicServer, SELF_SIGNED_DOMAIN}; use citadel_wire::tls::client_config_to_tls_connector; +use futures::StreamExt; use netbeam::time_tracker::TimeTracker; use crate::constants::{MAX_OUTGOING_UNPROCESSED_REQUESTS, TCP_CONN_TIMEOUT}; @@ -72,13 +72,13 @@ pub type TlsDomain = Option; // The outermost abstraction for the networking layer. We use Rc to allow ensure single-threaded performance // by default, but settings can be changed in crate::macros::*. -define_outer_struct_wrapper!(Node, NodeInner); +define_outer_struct_wrapper!(Node, NodeInner, , ); /// Inner device for the [`Node`] -pub struct NodeInner { +pub struct NodeInner { primary_socket: Option, /// Key: cid (to account for multiple clients from the same node) - session_manager: CitadelSessionManager, + session_manager: CitadelSessionManager, to_kernel: UnboundedSender, local_node_type: NodeType, // Applies only to listeners, not outgoing connections @@ -91,20 +91,20 @@ pub struct NodeInner { server_only_c2s_session_password: PreSharedKey, } -impl Node { +impl Node { /// Creates a new [`Node`] #[allow(clippy::too_many_arguments)] pub(crate) async fn init( local_node_type: NodeType, to_kernel: UnboundedSender, - account_manager: AccountManager, + account_manager: AccountManager, shutdown: citadel_io::tokio::sync::oneshot::Sender<()>, underlying_proto: ServerUnderlyingProtocol, client_config: Option>, stun_servers: Option>, server_only_c2s_session_password: Option, ) -> io::Result<( - NodeRemote, + NodeRemote, Pin>, Option, KernelAsyncCallbackHandler, @@ -168,13 +168,14 @@ impl Node { /// To handle the shutdown process, we need /// This will panic if called twice in succession without a proper server reload. /// Returns a handle to communicate with the [Node]. + #[allow(clippy::type_complexity)] #[allow(unused_results, unused_must_use)] fn load( - this: Node, - account_manager: AccountManager, + this: Node, + account_manager: AccountManager, shutdown: citadel_io::tokio::sync::oneshot::Sender<()>, ) -> ( - NodeRemote, + NodeRemote, Pin>, Option, KernelAsyncCallbackHandler, @@ -188,7 +189,7 @@ impl Node { let node_type = read.local_node_type; let (session_spawner_tx, session_spawner_rx) = unbounded(); - let session_spawner = CitadelSession::session_future_receiver(session_spawner_rx); + let session_spawner = CitadelSession::::session_future_receiver(session_spawner_rx); let (outbound_send_request_tx, outbound_send_request_rx) = BoundedSender::new(MAX_OUTGOING_UNPROCESSED_REQUESTS); // for the Hdp remote @@ -430,8 +431,8 @@ impl Node { /// to allow for TCP hole-punching (we need the same port to cover port-restricted NATS, worst-case scenario) /// The remote is usually the central server. Then the P2P listener binds to it to allow NATs to keep the hole punched /// It is expected that the listener_underlying_proto is QUIC here since this is called for p2p connections! - pub(crate) async fn create_session_transport_init( - remote: R, + pub(crate) async fn create_session_transport_init( + remote: T, default_client_config: &Arc, ) -> io::Result { // We start by creating a client to server connection @@ -445,9 +446,9 @@ impl Node { /// Important: Assumes UDP NAT traversal has concluded. This should ONLY be used for p2p /// This takes the local socket AND QuicNode instance #[allow(dead_code)] - pub async fn create_p2p_quic_connect_socket( + pub async fn create_p2p_quic_connect_socket( quic_endpoint: Endpoint, - remote: R, + remote: T, tls_domain: TlsDomain, timeout: Option, secure_client_config: Arc, @@ -507,8 +508,8 @@ impl Node { } /// Only for client to server conns - pub async fn create_c2s_connect_socket( - remote: R, + pub async fn create_c2s_connect_socket( + remote: T, timeout: Option, default_client_config: &Arc, ) -> io::Result<(GenericNetworkStream, Option)> { @@ -602,8 +603,8 @@ impl Node { } } - async fn read_first_packet( - stream: R, + async fn read_first_packet( + stream: Read, timeout: Option, ) -> std::io::Result { let (_stream, ret) = citadel_io::tokio::time::timeout( @@ -623,7 +624,7 @@ impl Node { /// will need to be created that is bound to the local primary port and connected to the adjacent hypernode's /// primary port. That socket will be created in the underlying HdpSessionManager during the connection process async fn listen_primary( - server: Node, + server: Node, _tt: TimeTracker, to_kernel: UnboundedSender, server_only_session_password: PreSharedKey, @@ -651,7 +652,7 @@ impl Node { async fn primary_session_creator_loop( to_kernel: UnboundedSender, local_nat_type: NatType, - session_manager: CitadelSessionManager, + session_manager: CitadelSessionManager, mut socket: DualListener, server_session_password: PreSharedKey, session_spawner: UnboundedSender>>, @@ -707,7 +708,7 @@ impl Node { } async fn outbound_kernel_request_handler( - this: Node, + this: Node, to_kernel_tx: UnboundedSender, mut outbound_send_request_rx: BoundedReceiver<(NodeRequest, Ticket)>, session_spawner: UnboundedSender>>, @@ -760,12 +761,12 @@ impl Node { while let Some((outbound_request, ticket_id)) = outbound_send_request_rx.recv().await { match outbound_request { NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand { - implicated_cid, + session_cid, command: cmd, }) => { if let Err(err) = session_manager.process_outbound_broadcast_command( ticket_id, - implicated_cid, + session_cid, cmd, ) { send_error(ticket_id, err)?; @@ -842,11 +843,8 @@ impl Node { } } - NodeRequest::DisconnectFromHypernode(DisconnectFromHypernode { - implicated_cid, - }) => { - if let Err(err) = session_manager.initiate_disconnect(implicated_cid, ticket_id) - { + NodeRequest::DisconnectFromHypernode(DisconnectFromHypernode { session_cid }) => { + if let Err(err) = session_manager.initiate_disconnect(session_cid, ticket_id) { send_error(ticket_id, err)?; } } @@ -854,19 +852,19 @@ impl Node { NodeRequest::ReKey(ReKey { v_conn_type: virtual_target, }) => { - if let Err(err) = - session_manager.initiate_update_drill_subroutine(virtual_target, ticket_id) + if let Err(err) = session_manager + .initiate_update_entropy_bank_subroutine(virtual_target, ticket_id) { send_error(ticket_id, err)?; } } NodeRequest::DeregisterFromHypernode(DeregisterFromHypernode { - implicated_cid, + session_cid, v_conn_type: virtual_connection_type, }) => { if let Err(err) = session_manager.initiate_deregistration_subroutine( - implicated_cid, + session_cid, virtual_connection_type, ticket_id, ) { @@ -876,12 +874,12 @@ impl Node { // TODO: Update this to include security levels (FCM conflicts though) NodeRequest::PeerCommand(PeerCommand { - implicated_cid, + session_cid, command: peer_command, }) => { if let Err(err) = session_manager .dispatch_peer_command( - implicated_cid, + session_cid, ticket_id, peer_command, SecurityLevel::Standard, @@ -895,7 +893,7 @@ impl Node { NodeRequest::SendObject(SendObject { source: path, chunk_size, - implicated_cid, + session_cid, v_conn_type: virtual_target, transfer_type, }) => { @@ -903,7 +901,7 @@ impl Node { ticket_id, chunk_size, path, - implicated_cid, + session_cid, virtual_target, SecurityLevel::Standard, transfer_type, @@ -920,7 +918,7 @@ impl Node { }) => { if let Err(err) = session_manager.revfs_pull( ticket_id, - v_conn.get_implicated_cid(), + v_conn.get_session_cid(), v_conn, virtual_dir, delete_on_pull, @@ -937,7 +935,7 @@ impl Node { }) => { if let Err(err) = session_manager.revfs_delete( ticket_id, - v_conn.get_implicated_cid(), + v_conn.get_session_cid(), v_conn, virtual_dir, security_level, @@ -967,8 +965,8 @@ impl Node { } } -pub(crate) struct HdpServerRemoteInner { +pub(crate) struct CitadelNodeRemoteInner { pub callback_handler: KernelAsyncCallbackHandler, pub node_type: NodeType, - pub account_manager: AccountManager, + pub account_manager: AccountManager, } diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs index 230cc64a4..f2a7c77ad 100644 --- a/citadel_proto/src/proto/node_request.rs +++ b/citadel_proto/src/proto/node_request.rs @@ -46,12 +46,12 @@ pub struct RegisterToHypernode { } pub struct PeerCommand { - pub implicated_cid: u64, + pub session_cid: u64, pub command: PeerSignal, } pub struct DeregisterFromHypernode { - pub implicated_cid: u64, + pub session_cid: u64, pub v_conn_type: VirtualConnectionType, } @@ -72,7 +72,7 @@ pub struct ReKey { pub struct SendObject { pub source: Box, pub chunk_size: Option, - pub implicated_cid: u64, + pub session_cid: u64, pub v_conn_type: VirtualTargetType, pub transfer_type: TransferType, } @@ -91,12 +91,12 @@ pub struct DeleteObject { } pub struct GroupBroadcastCommand { - pub implicated_cid: u64, + pub session_cid: u64, pub command: GroupBroadcast, } pub struct DisconnectFromHypernode { - pub implicated_cid: u64, + pub session_cid: u64, } /// These are sent down the stack into the server. Most of the requests expect a ticket ID @@ -111,7 +111,7 @@ pub enum NodeRequest { DeregisterFromHypernode(DeregisterFromHypernode), /// Implicated CID, creds, connect mode, fcm keys, TCP/TLS only, keep alive timeout, security settings ConnectToHypernode(ConnectToHypernode), - /// Updates the drill for the given CID + /// Updates the entropy_bank for the given CID ReKey(ReKey), /// Sends or updates a file SendObject(SendObject), @@ -130,24 +130,23 @@ pub enum NodeRequest { } impl NodeRequest { - pub fn implicated_cid(&self) -> Option { + pub fn session_cid(&self) -> Option { match self { NodeRequest::RegisterToHypernode(_) => None, - NodeRequest::PeerCommand(PeerCommand { implicated_cid, .. }) => Some(*implicated_cid), + NodeRequest::PeerCommand(PeerCommand { session_cid, .. }) => Some(*session_cid), NodeRequest::DeregisterFromHypernode(DeregisterFromHypernode { - implicated_cid, - .. - }) => Some(*implicated_cid), - NodeRequest::ConnectToHypernode(connect) => connect.auth_request.implicated_cid(), - NodeRequest::ReKey(rk) => Some(rk.v_conn_type.get_implicated_cid()), - NodeRequest::SendObject(SendObject { implicated_cid, .. }) => Some(*implicated_cid), - NodeRequest::PullObject(pull) => Some(pull.v_conn.get_implicated_cid()), - NodeRequest::DeleteObject(del) => Some(del.v_conn.get_implicated_cid()), - NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand { - implicated_cid, .. - }) => Some(*implicated_cid), - NodeRequest::DisconnectFromHypernode(DisconnectFromHypernode { implicated_cid }) => { - Some(*implicated_cid) + session_cid, .. + }) => Some(*session_cid), + NodeRequest::ConnectToHypernode(connect) => connect.auth_request.session_cid(), + NodeRequest::ReKey(rk) => Some(rk.v_conn_type.get_session_cid()), + NodeRequest::SendObject(SendObject { session_cid, .. }) => Some(*session_cid), + NodeRequest::PullObject(pull) => Some(pull.v_conn.get_session_cid()), + NodeRequest::DeleteObject(del) => Some(del.v_conn.get_session_cid()), + NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand { session_cid, .. }) => { + Some(*session_cid) + } + NodeRequest::DisconnectFromHypernode(DisconnectFromHypernode { session_cid }) => { + Some(*session_cid) } NodeRequest::GetActiveSessions => None, NodeRequest::Shutdown => None, diff --git a/citadel_proto/src/proto/node_result.rs b/citadel_proto/src/proto/node_result.rs index 67dabc06d..c7303b333 100644 --- a/citadel_proto/src/proto/node_result.rs +++ b/citadel_proto/src/proto/node_result.rs @@ -34,14 +34,13 @@ use crate::proto::state_container::VirtualConnectionType; use crate::kernel::kernel_communicator::CallbackKey; use citadel_types::proto::SessionSecuritySettings; use citadel_user::backend::utils::ObjectTransferHandler; -use citadel_user::client_account::ClientNetworkAccount; use std::net::SocketAddr; use std::path::PathBuf; #[derive(Debug)] pub struct RegisterOkay { pub ticket: Ticket, - pub cnac: ClientNetworkAccount, + pub cid: u64, pub welcome_message: Vec, } @@ -53,7 +52,7 @@ pub struct RegisterFailure { #[derive(Debug)] pub struct DeRegistration { - pub implicated_cid: u64, + pub session_cid: u64, pub ticket_opt: Option, pub success: bool, } @@ -61,7 +60,7 @@ pub struct DeRegistration { #[derive(Debug)] pub struct ConnectSuccess { pub ticket: Ticket, - pub implicated_cid: u64, + pub session_cid: u64, pub remote_addr: SocketAddr, pub is_personal: bool, pub v_conn_type: VirtualConnectionType, @@ -83,7 +82,7 @@ pub struct ConnectFail { pub struct ReKeyResult { pub ticket: Ticket, pub status: ReKeyReturnType, - pub implicated_cid: u64, + pub session_cid: u64, } #[derive(Debug)] @@ -103,12 +102,12 @@ pub struct OutboundRequestRejected { pub struct ObjectTransferHandle { pub ticket: Ticket, pub handle: ObjectTransferHandler, - pub implicated_cid: u64, + pub session_cid: u64, } #[derive(Debug)] pub struct MailboxDelivery { - pub implicated_cid: u64, + pub session_cid: u64, pub ticket_opt: Option, pub items: MailboxTransfer, } @@ -117,19 +116,19 @@ pub struct MailboxDelivery { pub struct PeerEvent { pub event: PeerSignal, pub ticket: Ticket, - pub implicated_cid: u64, + pub session_cid: u64, } #[derive(Debug)] pub struct GroupChannelCreated { pub ticket: Ticket, pub channel: GroupChannel, - pub implicated_cid: u64, + pub session_cid: u64, } #[derive(Debug)] pub struct GroupEvent { - pub implicated_cid: u64, + pub session_cid: u64, pub ticket: Ticket, pub event: GroupBroadcast, } @@ -168,7 +167,7 @@ pub struct ReVFSResult { pub error_message: Option, pub data: Option, pub ticket: Ticket, - pub implicated_cid: u64, + pub session_cid: u64, } /// This type is for relaying results between the lower-level protocol and the higher-level kernel @@ -219,63 +218,63 @@ impl NodeResult { match self { NodeResult::RegisterOkay(RegisterOkay { ticket: t, - cnac, + cid, welcome_message: _, - }) => Some(CallbackKey::new(*t, cnac.get_cid())), + }) => Some(CallbackKey::new(*t, *cid)), NodeResult::RegisterFailure(RegisterFailure { ticket: t, error_message: _, }) => Some(CallbackKey::ticket_only(*t)), NodeResult::DeRegistration(DeRegistration { - implicated_cid, + session_cid, ticket_opt: t, .. - }) => Some(CallbackKey::new((*t)?, *implicated_cid)), + }) => Some(CallbackKey::new((*t)?, *session_cid)), NodeResult::ConnectSuccess(ConnectSuccess { ticket: t, - implicated_cid, + session_cid, .. - }) => Some(CallbackKey::new(*t, *implicated_cid)), + }) => Some(CallbackKey::new(*t, *session_cid)), NodeResult::ConnectFail(ConnectFail { ticket: t, cid_opt, error_message: _, }) => Some(CallbackKey { ticket: *t, - implicated_cid: *cid_opt, + session_cid: *cid_opt, }), NodeResult::OutboundRequestRejected(OutboundRequestRejected { ticket: t, message_opt: _, }) => Some(CallbackKey::ticket_only(*t)), NodeResult::ObjectTransferHandle(ObjectTransferHandle { - implicated_cid, + session_cid, ticket, .. - }) => Some(CallbackKey::new(*ticket, *implicated_cid)), + }) => Some(CallbackKey::new(*ticket, *session_cid)), NodeResult::MailboxDelivery(MailboxDelivery { - implicated_cid, + session_cid, ticket_opt: t, items: _, - }) => Some(CallbackKey::new((*t)?, *implicated_cid)), + }) => Some(CallbackKey::new((*t)?, *session_cid)), NodeResult::PeerEvent(PeerEvent { event: _, ticket: t, - implicated_cid, - }) => Some(CallbackKey::new(*t, *implicated_cid)), + session_cid, + }) => Some(CallbackKey::new(*t, *session_cid)), NodeResult::GroupEvent(GroupEvent { - implicated_cid, + session_cid, ticket: t, event: _, - }) => Some(CallbackKey::new(*t, *implicated_cid)), + }) => Some(CallbackKey::new(*t, *session_cid)), NodeResult::PeerChannelCreated(PeerChannelCreated { ticket: t, channel, .. - }) => Some(CallbackKey::new(*t, channel.get_implicated_cid())), + }) => Some(CallbackKey::new(*t, channel.get_session_cid())), NodeResult::GroupChannelCreated(GroupChannelCreated { ticket: t, channel: _, - implicated_cid, - }) => Some(CallbackKey::new(*t, *implicated_cid)), + session_cid, + }) => Some(CallbackKey::new(*t, *session_cid)), NodeResult::Disconnect(Disconnect { ticket: t, cid_opt, @@ -284,7 +283,7 @@ impl NodeResult { message: _, }) => Some(CallbackKey { ticket: *t, - implicated_cid: *cid_opt, + session_cid: *cid_opt, }), NodeResult::InternalServerError(InternalServerError { ticket_opt: t, @@ -292,7 +291,7 @@ impl NodeResult { message: _, }) => Some(CallbackKey { ticket: (*t)?, - implicated_cid: *cid_opt, + session_cid: *cid_opt, }), NodeResult::SessionList(SessionList { ticket: t, @@ -301,14 +300,14 @@ impl NodeResult { NodeResult::Shutdown => None, NodeResult::ReKeyResult(ReKeyResult { ticket, - implicated_cid, + session_cid, .. - }) => Some(CallbackKey::new(*ticket, *implicated_cid)), + }) => Some(CallbackKey::new(*ticket, *session_cid)), NodeResult::ReVFS(ReVFSResult { ticket, - implicated_cid, + session_cid, .. - }) => Some(CallbackKey::new(*ticket, *implicated_cid)), + }) => Some(CallbackKey::new(*ticket, *session_cid)), } } } diff --git a/citadel_proto/src/proto/packet.rs b/citadel_proto/src/proto/packet.rs index c07a29bca..b86c8005d 100644 --- a/citadel_proto/src/proto/packet.rs +++ b/citadel_proto/src/proto/packet.rs @@ -91,7 +91,7 @@ pub(crate) mod packet_flags { pub(crate) const FINAL: u8 = 1; } - pub(crate) mod do_drill_update { + pub(crate) mod do_stacked_ratchet_update { pub(crate) const STAGE0: u8 = 0; pub(crate) const STAGE1: u8 = 1; @@ -167,7 +167,7 @@ pub(crate) mod packet_sizes { pub(crate) const GROUP_HEADER_BASE_LEN: usize = HDP_HEADER_BYTE_LEN + 1; pub(crate) const GROUP_HEADER_ACK_LEN: usize = HDP_HEADER_BYTE_LEN + 1 + 1 + 4 + 4; - pub(crate) mod do_drill_update { + pub(crate) mod do_entropy_bank_update { use crate::constants::HDP_HEADER_BYTE_LEN; pub(crate) const STAGE1: usize = HDP_HEADER_BYTE_LEN + HDP_HEADER_BYTE_LEN; @@ -195,8 +195,8 @@ pub struct HdpHeader { pub wave_id: U32, /// Multiple clients may be connected from the same node. NOTE: This can also be equal to the ticket id pub session_cid: U64, - /// The drill version applied to encrypt the data - pub drill_version: U32, + /// The entropy_bank version applied to encrypt the data + pub entropy_bank_version: U32, /// Before a packet is sent outbound, the local time is placed into the packet header pub timestamp: I64, /// The target_cid (0 if hyperLAN server) diff --git a/citadel_proto/src/proto/packet_crafter.rs b/citadel_proto/src/proto/packet_crafter.rs index 29a0e058a..7fa3e8ea4 100644 --- a/citadel_proto/src/proto/packet_crafter.rs +++ b/citadel_proto/src/proto/packet_crafter.rs @@ -70,7 +70,7 @@ use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualTargetType; use citadel_crypt::scramble::crypt_splitter::oneshot_unencrypted_group_unified; use citadel_crypt::secure_buffer::sec_packet::SecureMessagePacket; -use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::prelude::ObjectId; /// A secure packet container that provides zero-copy buffer management and @@ -170,8 +170,8 @@ impl From for SecureMessagePacket { /// time_tracker, /// )?; /// ``` -pub struct GroupTransmitter { - pub hyper_ratchet_container: RatchetPacketCrafterContainer, +pub struct GroupTransmitter { + pub ratchet_container: RatchetPacketCrafterContainer, to_primary_stream: OutboundPrimaryStreamSender, // Handles the encryption and scrambling asynchronously. Also manages missing packets pub(crate) group_transmitter: GroupSenderDevice, @@ -193,7 +193,7 @@ pub struct GroupTransmitter { /// Fcm may be present, in which case, the innermost encryption pass goes through the fcm ratchet to ensure /// Google can't see the information. The fcm constructor may not be present either, since a concurrent update may /// be occurring -pub struct RatchetPacketCrafterContainer { +pub struct RatchetPacketCrafterContainer { pub base: R, pub base_constructor: Option, } @@ -208,12 +208,12 @@ impl RatchetPacketCrafterContainer { } } -impl GroupTransmitter { +impl GroupTransmitter { /// Scrambled packets will use this pub fn new_from_group_sender( to_primary_stream: OutboundPrimaryStreamSender, group_sender: GroupSenderDevice, - hyper_ratchet: RatchetPacketCrafterContainer, + stacked_ratchet: RatchetPacketCrafterContainer, object_id: ObjectId, ticket: Ticket, security_level: SecurityLevel, @@ -223,7 +223,7 @@ impl GroupTransmitter { let group_id = cfg.group_id; let bytes_encrypted = cfg.plaintext_length as usize; Self { - hyper_ratchet_container: hyper_ratchet, + ratchet_container: stacked_ratchet, // This must be false is_message: false, group_transmitter: group_sender, @@ -243,15 +243,15 @@ impl GroupTransmitter { pub fn new_message( to_primary_stream: OutboundPrimaryStreamSender, object_id: ObjectId, - hyper_ratchet: RatchetPacketCrafterContainer, + stacked_ratchet: RatchetPacketCrafterContainer, input_packet: SecureProtocolPacket, security_level: SecurityLevel, group_id: u64, ticket: Ticket, time_tracker: TimeTracker, ) -> Option { - // Gets the latest drill version by default for this operation - log::trace!(target: "citadel", "Will use StackedRatchet v{} to encrypt group {}", hyper_ratchet.base.version(), group_id); + // Gets the latest entropy_bank version by default for this operation + log::trace!(target: "citadel", "Will use R v{} to encrypt group {}", stacked_ratchet.base.version(), group_id); let plaintext_len = input_packet.inner.message_len(); //the number of bytes that will be encrypted // + 1 byte source port offset (needed for sending across port-address-translation networks) @@ -259,7 +259,7 @@ impl GroupTransmitter { let is_empty = plaintext_len == 0; const HDP_HEADER_EXTENDED_BYTE_LEN: usize = HDP_HEADER_BYTE_LEN + 2; - //let res = encrypt_group_unified(input_packet.into_buffer(), &hyper_ratchet.base, HDP_HEADER_EXTENDED_BYTE_LEN, target_cid, object_id, group_id, craft_wave_payload_packet_into); + //let res = encrypt_group_unified(input_packet.into_buffer(), &stacked_ratchet.base, HDP_HEADER_EXTENDED_BYTE_LEN, target_cid, object_id, group_id, craft_wave_payload_packet_into); let res = oneshot_unencrypted_group_unified( input_packet.into(), HDP_HEADER_EXTENDED_BYTE_LEN, @@ -272,7 +272,7 @@ impl GroupTransmitter { Ok(group_transmitter) => { let group_config: GroupReceiverConfig = group_transmitter.get_receiver_config(); Some(Self { - hyper_ratchet_container: hyper_ratchet, + ratchet_container: stacked_ratchet, is_message: true, object_id, to_primary_stream, @@ -354,15 +354,15 @@ pub(crate) mod group { use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualTargetType; use crate::proto::validation::group::{GroupHeader, GroupHeaderAck, WaveAck}; - use citadel_crypt::endpoint_crypto_container::KemTransferStatus; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::proto::ObjectId; use citadel_user::serialization::SyncIO; use std::ops::RangeInclusive; /// Crafts a group header packet for a given group transmitter and virtual target - pub(super) fn craft_group_header_packet( - processor: &mut GroupTransmitter, + pub(super) fn craft_group_header_packet( + processor: &mut GroupTransmitter, virtual_target: VirtualTargetType, ) -> BytesMut { let target_cid = virtual_target.get_target_cid(); @@ -377,8 +377,8 @@ pub(crate) mod group { context_info: U128::new(processor.ticket.0), group: U64::new(processor.group_id), wave_id: U32::new(0), - session_cid: U64::new(processor.hyper_ratchet_container.base.get_cid()), - drill_version: U32::new(processor.hyper_ratchet_container.base.version()), + session_cid: U64::new(processor.ratchet_container.base.get_cid()), + entropy_bank_version: U32::new(processor.ratchet_container.base.version()), timestamp: I64::new(processor.time_tracker.get_global_time_ns()), target_cid: U64::new(target_cid), }; @@ -393,9 +393,9 @@ pub(crate) mod group { .unwrap(); // both the header and payload are now written. Just have to extend the kem info let kem = processor - .hyper_ratchet_container + .ratchet_container .base_constructor - .as_ref() + .as_mut() .map(|res| res.stage0_alice().unwrap()); let expected_len = kem.serialized_size().unwrap(); packet @@ -416,7 +416,7 @@ pub(crate) mod group { packet.put_u128(processor.object_id.0); processor - .hyper_ratchet_container + .ratchet_container .base .protect_message_packet( Some(processor.security_level), @@ -430,8 +430,8 @@ pub(crate) mod group { /// Crafts a group header acknowledgement packet for a given group transmitter and virtual target #[allow(clippy::too_many_arguments)] - pub(crate) fn craft_group_header_ack( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_group_header_ack( + stacked_ratchet: &R, group_id: u64, target_cid: u64, object_id: ObjectId, @@ -439,7 +439,7 @@ pub(crate) mod group { initial_wave_window: Option>, fast_msg: bool, timestamp: i64, - transfer: KemTransferStatus, + transfer: KemTransferStatus, security_level: SecurityLevel, ) -> BytesMut { let header = HdpHeader { @@ -451,8 +451,8 @@ pub(crate) mod group { context_info: U128::new(ticket.0), group: U64::new(group_id), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -470,7 +470,7 @@ pub(crate) mod group { header_ack.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -479,7 +479,7 @@ pub(crate) mod group { /// Crafts a wave payload packet for a given group transmitter and virtual target pub(crate) fn craft_wave_payload_packet_into( coords: &PacketVector, - scramble_drill: &EntropyBank, + scramble_entropy_bank: &EntropyBank, object_id: ObjectId, target_cid: u64, mut buffer: &mut BytesMut, @@ -493,8 +493,8 @@ pub(crate) mod group { context_info: U128::new(object_id.0), group: U64::new(coords.group_id), wave_id: U32::new(coords.wave_id), - session_cid: U64::new(scramble_drill.get_cid()), - drill_version: U32::new(scramble_drill.get_version()), + session_cid: U64::new(scramble_entropy_bank.get_cid()), + entropy_bank_version: U32::new(scramble_entropy_bank.get_version()), timestamp: I64::new(0), // Irrelevant; supplied by the wave header anyways target_cid: U64::new(target_cid), }; @@ -503,16 +503,16 @@ pub(crate) mod group { header.inscribe_into(&mut buffer); let src_port = coords.local_port; let remote_port = coords.remote_port; - debug_assert!(src_port <= scramble_drill.get_multiport_width() as u16); - debug_assert!(remote_port <= scramble_drill.get_multiport_width() as u16); + debug_assert!(src_port <= scramble_entropy_bank.get_multiport_width() as u16); + debug_assert!(remote_port <= scramble_entropy_bank.get_multiport_width() as u16); buffer.put_u8(src_port as u8); buffer.put_u8(remote_port as u8); } /// Crafts a wave acknowledgement packet for a given group transmitter and virtual target #[allow(clippy::too_many_arguments)] - pub(crate) fn craft_wave_ack( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_wave_ack( + stacked_ratchet: &R, object_id: ObjectId, target_cid: u64, group_id: u64, @@ -530,8 +530,8 @@ pub(crate) mod group { context_info: U128::new(object_id.0), group: U64::new(group_id), wave_id: U32::new(wave_id), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -542,7 +542,7 @@ pub(crate) mod group { header.inscribe_into(&mut packet); wave_ack.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -556,7 +556,7 @@ pub(crate) mod do_connect { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use crate::proto::peer::peer_layer::MailboxTransfer; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::user::MutualPeer; use citadel_user::auth::proposed_credentials::ProposedCredentials; @@ -573,8 +573,8 @@ pub(crate) mod do_connect { /// Alice receives the nonce from Bob. She must now inscribe her username/password #[allow(unused_results)] - pub(crate) fn craft_stage0_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_stage0_packet( + stacked_ratchet: &R, proposed_credentials: ProposedCredentials, timestamp: i64, security_level: SecurityLevel, @@ -590,8 +590,8 @@ pub(crate) mod do_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -608,7 +608,7 @@ pub(crate) mod do_connect { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -637,8 +637,8 @@ pub(crate) mod do_connect { } #[allow(clippy::too_many_arguments)] - pub(crate) fn craft_final_status_packet>( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_final_status_packet, R: Ratchet>( + stacked_ratchet: &R, success: bool, mailbox: Option, post_login_object: citadel_user::external_services::ServicesObject, @@ -672,8 +672,8 @@ pub(crate) mod do_connect { context_info: U128::new(0), group: U64::new(is_filesystem as u64), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -683,7 +683,7 @@ pub(crate) mod do_connect { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -692,8 +692,8 @@ pub(crate) mod do_connect { /// Crafts a do-connect success acknowledgement packet for a given timestamp and security level #[allow(unused_results)] - pub(crate) fn craft_success_ack( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_success_ack( + stacked_ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { @@ -706,8 +706,8 @@ pub(crate) mod do_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -715,7 +715,7 @@ pub(crate) mod do_connect { let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); header.inscribe_into(&mut packet); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -724,17 +724,16 @@ pub(crate) mod do_connect { } pub(crate) mod keep_alive { - use bytes::BytesMut; - use zerocopy::{I64, U128, U32, U64}; - use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use bytes::BytesMut; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; + use zerocopy::{I64, U128, U32, U64}; /// Crafts a keep-alive packet for a given timestamp and security level - pub(crate) fn craft_keep_alive_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_keep_alive_packet( + stacked_ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { @@ -747,14 +746,14 @@ pub(crate) mod keep_alive { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -767,8 +766,8 @@ pub(crate) mod do_register { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; - use citadel_crypt::stacked_ratchet::constructor::{AliceToBobTransfer, BobToAliceTransfer}; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_user::auth::proposed_credentials::ProposedCredentials; use citadel_user::serialization::SyncIO; @@ -776,18 +775,20 @@ pub(crate) mod do_register { /// Crafts a do-register stage 0 packet for a given transfer and passwordless flag #[derive(Serialize, Deserialize)] - pub(crate) struct DoRegisterStage0 { - pub(crate) transfer: AliceToBobTransfer, + pub(crate) struct DoRegisterStage0 { + #[serde(bound = "")] + pub(crate) transfer: + >::AliceToBobWireTransfer, pub(crate) passwordless: bool, } - /// At this stage, the drill does not exist. There is no verifying such packets. The payload contains Alice's public key. + /// At this stage, the entropy_bank does not exist. There is no verifying such packets. The payload contains Alice's public key. /// Since this is sent over TCP, the size of the packet can be up to ~64k bytes /// We also use the NID in place of the CID because the CID only exists AFTER registration completes - pub(crate) fn craft_stage0( + pub(crate) fn craft_stage0( algorithm: u8, timestamp: i64, - transfer: AliceToBobTransfer, + transfer: >::AliceToBobWireTransfer, passwordless: bool, proposed_cid: u64, ) -> BytesMut { @@ -801,7 +802,7 @@ pub(crate) mod do_register { group: U64::new(0), wave_id: U32::new(0), session_cid: U64::new(proposed_cid), - drill_version: U32::new(0), + entropy_bank_version: U32::new(0), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -809,7 +810,7 @@ pub(crate) mod do_register { let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); packet.put(header.as_packet()); - DoRegisterStage0 { + DoRegisterStage0:: { transfer, passwordless, } @@ -820,10 +821,10 @@ pub(crate) mod do_register { } /// Crafts a do-register stage 1 packet for a given transfer and proposed CID - pub(crate) fn craft_stage1( + pub(crate) fn craft_stage1( algorithm: u8, timestamp: i64, - transfer: BobToAliceTransfer, + transfer: >::BobToAliceWireTransfer, proposed_cid: u64, ) -> BytesMut { let header = HdpHeader { @@ -836,7 +837,7 @@ pub(crate) mod do_register { group: U64::new(0), wave_id: U32::new(0), session_cid: U64::new(proposed_cid), - drill_version: U32::new(0), + entropy_bank_version: U32::new(0), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -844,7 +845,7 @@ pub(crate) mod do_register { let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); header.inscribe_into(&mut packet); - transfer.serialize_into(&mut packet).unwrap(); + SyncIO::serialize_into_buf(&transfer, &mut packet).unwrap(); packet } @@ -857,8 +858,8 @@ pub(crate) mod do_register { /// Alice sends this. The stage 3 packet contains the encrypted username, password, and full name of the registering client #[allow(unused_results)] - pub(crate) fn craft_stage2( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_stage2( + stacked_ratchet: &R, algorithm: u8, timestamp: i64, credentials: &ProposedCredentials, @@ -873,8 +874,8 @@ pub(crate) mod do_register { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(0), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(0), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -887,7 +888,7 @@ pub(crate) mod do_register { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -895,8 +896,8 @@ pub(crate) mod do_register { } /// Crafts a do-register success packet for a given success message and timestamp - pub(crate) fn craft_success>( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_success, R: Ratchet>( + stacked_ratchet: &R, algorithm: u8, timestamp: i64, success_message: T, @@ -913,8 +914,8 @@ pub(crate) mod do_register { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(0), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(0), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -923,7 +924,7 @@ pub(crate) mod do_register { header.inscribe_into(&mut packet); packet.put(success_message); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -949,7 +950,7 @@ pub(crate) mod do_register { group: U64::new(0), wave_id: U32::new(0), session_cid: U64::new(proposed_cid), - drill_version: U32::new(0), + entropy_bank_version: U32::new(0), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -969,13 +970,13 @@ pub(crate) mod do_disconnect { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use crate::proto::remote::Ticket; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; /// Crafts a do-disconnect stage 0 packet for a given ticket, timestamp, and security level #[allow(unused_results)] - pub(crate) fn craft_stage0( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_stage0( + stacked_ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, @@ -989,14 +990,14 @@ pub(crate) mod do_disconnect { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1005,8 +1006,8 @@ pub(crate) mod do_disconnect { /// Crafts a do-disconnect final packet for a given ticket, timestamp, and security level #[allow(unused_results)] - pub(crate) fn craft_final( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_final( + stacked_ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, @@ -1020,39 +1021,38 @@ pub(crate) mod do_disconnect { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet } } -pub(crate) mod do_drill_update { +pub(crate) mod do_entropy_bank_update { use bytes::BytesMut; use zerocopy::{I64, U128, U32, U64}; use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, packet_sizes, HdpHeader}; - use citadel_crypt::endpoint_crypto_container::KemTransferStatus; - use citadel_crypt::stacked_ratchet::constructor::AliceToBobTransfer; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_user::serialization::SyncIO; use serde::{Deserialize, Serialize}; - /// Crafts a do-drill update stage 0 packet for a given transfer, timestamp, target CID, and security level + /// Crafts a do-entropy_bank update stage 0 packet for a given transfer, timestamp, target CID, and security level #[allow(unused_results)] - pub(crate) fn craft_stage0( - hyper_ratchet: &StackedRatchet, - transfer: AliceToBobTransfer, + pub(crate) fn craft_stage0( + stacked_ratchet: &R, + transfer: >::AliceToBobWireTransfer, timestamp: i64, target_cid: u64, security_level: SecurityLevel, @@ -1060,14 +1060,14 @@ pub(crate) mod do_drill_update { let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_drill_update::STAGE0, + cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE0, algorithm: 0, security_level: security_level.value(), context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1076,22 +1076,23 @@ pub(crate) mod do_drill_update { header.inscribe_into(&mut packet); transfer.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet } - /// Crafts a do-drill update stage 1 packet for a given update status, timestamp, target CID, and security level + /// Crafts a do-entropy_bank update stage 1 packet for a given update status, timestamp, target CID, and security level #[derive(Serialize, Deserialize)] - pub(crate) struct Stage1UpdatePacket { - pub(crate) update_status: KemTransferStatus, + pub(crate) struct Stage1UpdatePacket { + #[serde(bound = "")] + pub(crate) update_status: KemTransferStatus, } #[allow(unused_results)] - pub(crate) fn craft_stage1( - hyper_ratchet: &StackedRatchet, - update_status: KemTransferStatus, + pub(crate) fn craft_stage1( + stacked_ratchet: &R, + update_status: KemTransferStatus, timestamp: i64, target_cid: u64, security_level: SecurityLevel, @@ -1099,39 +1100,39 @@ pub(crate) mod do_drill_update { let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_drill_update::STAGE1, + cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE1, algorithm: 0, security_level: security_level.value(), context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; - let mut packet = BytesMut::with_capacity(packet_sizes::do_drill_update::STAGE1); + let mut packet = BytesMut::with_capacity(packet_sizes::do_entropy_bank_update::STAGE1); header.inscribe_into(&mut packet); let stage1_packet = Stage1UpdatePacket { update_status }; stage1_packet.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet } - /// Crafts a do-drill update truncate packet for a given truncate version, target CID, timestamp, and security level + /// Crafts a do-entropy_bank update truncate packet for a given truncate version, target CID, timestamp, and security level #[derive(Serialize, Deserialize)] pub(crate) struct TruncatePacket { pub(crate) truncate_version: Option, } #[allow(unused_results)] - pub(crate) fn craft_truncate( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_truncate( + stacked_ratchet: &R, truncate_version: Option, target_cid: u64, timestamp: i64, @@ -1140,39 +1141,39 @@ pub(crate) mod do_drill_update { let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_drill_update::TRUNCATE, + cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE, algorithm: 0, security_level: security_level.value(), context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; - let mut packet = BytesMut::with_capacity(packet_sizes::do_drill_update::STAGE1); + let mut packet = BytesMut::with_capacity(packet_sizes::do_entropy_bank_update::STAGE1); header.inscribe_into(&mut packet); // encrypt the nonce into the packet TruncatePacket { truncate_version } .serialize_into_buf(&mut packet) .unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet } - /// Crafts a do-drill update truncate acknowledgement packet for a given truncated version, target CID, timestamp, and security level + /// Crafts a do-entropy_bank update truncate acknowledgement packet for a given truncated version, target CID, timestamp, and security level #[derive(Serialize, Deserialize)] pub(crate) struct TruncateAckPacket { pub(crate) truncated_version: u32, } - pub(crate) fn craft_truncate_ack( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_truncate_ack( + stacked_ratchet: &R, truncated_version: u32, target_cid: u64, timestamp: i64, @@ -1181,25 +1182,25 @@ pub(crate) mod do_drill_update { let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_drill_update::TRUNCATE_ACK, + cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE_ACK, algorithm: 0, security_level: security_level.value(), context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; - let mut packet = BytesMut::with_capacity(packet_sizes::do_drill_update::STAGE1); + let mut packet = BytesMut::with_capacity(packet_sizes::do_entropy_bank_update::STAGE1); header.inscribe_into(&mut packet); TruncateAckPacket { truncated_version } .serialize_into_buf(&mut packet) .unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1207,38 +1208,37 @@ pub(crate) mod do_drill_update { } pub(crate) mod do_deregister { - use bytes::BytesMut; - use zerocopy::{I64, U128, U32, U64}; - use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use bytes::BytesMut; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; + use zerocopy::{I64, U128, U32, U64}; /// Crafts a do-deregister stage 0 packet for a given timestamp and security level - pub(crate) fn craft_stage0( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_stage0( + stacked_ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), cmd_primary: packet_flags::cmd::primary::DO_DEREGISTER, - cmd_aux: packet_flags::cmd::aux::do_drill_update::STAGE0, + cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE0, algorithm: 0, security_level: security_level.value(), context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1247,8 +1247,8 @@ pub(crate) mod do_deregister { /// Crafts a do-deregister final packet for a given success flag, timestamp, and security level #[allow(unused_results)] - pub(crate) fn craft_final( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_final( + stacked_ratchet: &R, success: bool, timestamp: i64, security_level: SecurityLevel, @@ -1268,15 +1268,15 @@ pub(crate) mod do_deregister { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1292,9 +1292,8 @@ pub(crate) mod pre_connect { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::packet_flags::payload_identifiers; use crate::proto::packet::{packet_flags, HdpHeader}; - use citadel_crypt::stacked_ratchet::constructor::{AliceToBobTransfer, BobToAliceTransfer}; - use citadel_crypt::stacked_ratchet::StackedRatchet; - use citadel_crypt::toolset::StaticAuxRatchet; + use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ConnectMode; use citadel_types::proto::SessionSecuritySettings; @@ -1306,8 +1305,9 @@ pub(crate) mod pre_connect { /// Crafts a pre-connect SYN packet for a given transfer, NAT type, UDP mode, timestamp, keep-alive timeout, security level, session security settings, peer-only connect protocol, and connect mode #[derive(Serialize, Deserialize)] - pub struct SynPacket { - pub transfer: AliceToBobTransfer, + pub struct SynPacket { + #[serde(bound = "")] + pub transfer: >::AliceToBobWireTransfer, pub session_security_settings: SessionSecuritySettings, pub peer_only_connect_protocol: ConnectProtocol, pub connect_mode: ConnectMode, @@ -1317,9 +1317,9 @@ pub(crate) mod pre_connect { } #[allow(clippy::too_many_arguments)] - pub(crate) fn craft_syn( - static_aux_hr: &StaticAuxRatchet, - transfer: AliceToBobTransfer, + pub(crate) fn craft_syn( + static_aux_hr: &R, + transfer: >::AliceToBobWireTransfer, nat_type: NatType, udp_mode: UdpMode, timestamp: i64, @@ -1339,7 +1339,7 @@ pub(crate) mod pre_connect { group: U64::new(0), wave_id: U32::new(0), session_cid: U64::new(static_aux_hr.get_cid()), - drill_version: U32::new(static_aux_hr.version()), + entropy_bank_version: U32::new(static_aux_hr.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -1347,7 +1347,7 @@ pub(crate) mod pre_connect { let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); header.inscribe_into(&mut packet); - SynPacket { + SynPacket:: { transfer, session_security_settings, peer_only_connect_protocol, @@ -1367,14 +1367,15 @@ pub(crate) mod pre_connect { /// Crafts a pre-connect SYN acknowledgement packet for a given transfer, NAT type, timestamp, and security level #[derive(Serialize, Deserialize)] - pub struct SynAckPacket { - pub transfer: BobToAliceTransfer, + pub struct SynAckPacket { + #[serde(bound = "")] + pub transfer: >::BobToAliceWireTransfer, pub nat_type: NatType, } - pub(crate) fn craft_syn_ack( - static_aux_hr: &StaticAuxRatchet, - transfer: BobToAliceTransfer, + pub(crate) fn craft_syn_ack( + static_aux_hr: &R, + transfer: >::BobToAliceWireTransfer, nat_type: NatType, timestamp: i64, security_level: SecurityLevel, @@ -1389,7 +1390,7 @@ pub(crate) mod pre_connect { group: U64::new(0), wave_id: U32::new(0), session_cid: U64::new(static_aux_hr.get_cid()), - drill_version: U32::new(static_aux_hr.version()), + entropy_bank_version: U32::new(static_aux_hr.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -1397,7 +1398,7 @@ pub(crate) mod pre_connect { let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); header.inscribe_into(&mut packet); - SynAckPacket { transfer, nat_type } + SynAckPacket:: { transfer, nat_type } .serialize_into_buf(&mut packet) .unwrap(); @@ -1415,8 +1416,8 @@ pub(crate) mod pre_connect { } // This gets sent from Alice to Bob - pub(crate) fn craft_stage0( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_stage0( + stacked_ratchet: &R, timestamp: i64, node_type: NodeType, security_level: SecurityLevel, @@ -1430,8 +1431,8 @@ pub(crate) mod pre_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -1443,7 +1444,7 @@ pub(crate) mod pre_connect { .serialize_into_buf(&mut packet) .unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1451,8 +1452,8 @@ pub(crate) mod pre_connect { } /// Crafts a pre-connect final packet for a given success flag, TCP-only flag, timestamp, and security level - pub(crate) fn craft_stage_final( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_stage_final( + stacked_ratchet: &R, success: bool, tcp_only: bool, timestamp: i64, @@ -1479,23 +1480,23 @@ pub(crate) mod pre_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet } /// Crafts a pre-connect begin connect packet for a given timestamp and security level - pub(crate) fn craft_begin_connect( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_begin_connect( + stacked_ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { @@ -1508,14 +1509,14 @@ pub(crate) mod pre_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1533,7 +1534,7 @@ pub(crate) mod pre_connect { group: prev_header.group, wave_id: prev_header.wave_id, session_cid: prev_header.session_cid, - drill_version: prev_header.drill_version, + entropy_bank_version: prev_header.entropy_bank_version, timestamp: prev_header.timestamp, target_cid: U64::new(0), }; @@ -1555,7 +1556,7 @@ pub(crate) mod peer_cmd { use crate::proto::remote::Ticket; use bytes::BytesMut; use citadel_crypt::scramble::crypt_splitter::AES_GCM_GHASH_OVERHEAD; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_user::serialization::SyncIO; use serde::Serialize; @@ -1568,8 +1569,8 @@ pub(crate) mod peer_cmd { */ /// Peer signals, unlike channels, DO NOT get a target_cid because they require the central server's participation to increase security between the /// two nodes - pub(crate) fn craft_peer_signal( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_peer_signal( + stacked_ratchet: &R, peer_command: T, ticket: Ticket, timestamp: i64, @@ -1584,8 +1585,8 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(C2S_ENCRYPTION_ONLY), }; @@ -1597,7 +1598,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); peer_command.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1606,8 +1607,8 @@ pub(crate) mod peer_cmd { /// Crafts a peer signal endpoint packet for a given peer command, ticket, timestamp, target CID, and security level #[allow(dead_code)] - pub(crate) fn craft_peer_signal_endpoint( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_peer_signal_endpoint( + stacked_ratchet: &R, peer_command: T, ticket: Ticket, timestamp: i64, @@ -1623,8 +1624,8 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1636,7 +1637,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); peer_command.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1645,8 +1646,8 @@ pub(crate) mod peer_cmd { /// Crafts a channel packet for a given payload, ticket, proxy target CID, timestamp, and security level #[allow(dead_code)] - pub(crate) fn craft_channel_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_channel_packet( + stacked_ratchet: &R, payload: ChannelPacket, ticket: Ticket, proxy_target_cid: u64, @@ -1662,8 +1663,8 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(proxy_target_cid), }; @@ -1674,7 +1675,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1683,8 +1684,8 @@ pub(crate) mod peer_cmd { /// Crafts a group message packet for a given payload, ticket, proxy target CID, timestamp, and security level #[allow(dead_code)] - pub(crate) fn craft_group_message_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_group_message_packet( + stacked_ratchet: &R, payload: &GroupBroadcast, ticket: Ticket, proxy_target_cid: u64, @@ -1700,8 +1701,8 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(proxy_target_cid), }; @@ -1712,7 +1713,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1725,7 +1726,7 @@ pub(crate) mod file { use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualTargetType; use bytes::BytesMut; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::prelude::TransferType; use citadel_types::proto::{ObjectId, VirtualObjectMetadata}; @@ -1741,8 +1742,8 @@ pub(crate) mod file { pub object_id: ObjectId, } - pub(crate) fn craft_file_error_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_file_error_packet( + stacked_ratchet: &R, ticket: Ticket, security_level: SecurityLevel, virtual_target: VirtualTargetType, @@ -1759,8 +1760,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(virtual_target.get_target_cid()), }; @@ -1774,7 +1775,7 @@ pub(crate) mod file { payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1790,8 +1791,8 @@ pub(crate) mod file { } #[allow(clippy::too_many_arguments)] - pub(crate) fn craft_file_header_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_file_header_packet( + stacked_ratchet: &R, group_start: u64, ticket: Ticket, security_level: SecurityLevel, @@ -1809,8 +1810,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(group_start), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(virtual_target.get_target_cid()), }; @@ -1825,7 +1826,7 @@ pub(crate) mod file { payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1842,8 +1843,8 @@ pub(crate) mod file { } #[allow(clippy::too_many_arguments)] - pub(crate) fn craft_file_header_ack_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_file_header_ack_packet( + stacked_ratchet: &R, success: bool, object_id: ObjectId, target_cid: u64, @@ -1862,8 +1863,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1880,7 +1881,7 @@ pub(crate) mod file { payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1895,8 +1896,8 @@ pub(crate) mod file { pub security_level: SecurityLevel, } - pub fn craft_revfs_pull( - hyper_ratchet: &StackedRatchet, + pub fn craft_revfs_pull( + stacked_ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -1913,8 +1914,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1929,7 +1930,7 @@ pub(crate) mod file { }; payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1942,8 +1943,8 @@ pub(crate) mod file { pub virtual_path: PathBuf, } - pub fn craft_revfs_delete( - hyper_ratchet: &StackedRatchet, + pub fn craft_revfs_delete( + stacked_ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -1959,8 +1960,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1971,7 +1972,7 @@ pub(crate) mod file { let payload = ReVFSDeletePacket { virtual_path }; payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1985,8 +1986,8 @@ pub(crate) mod file { pub error_msg: Option, } - pub fn craft_revfs_ack( - hyper_ratchet: &StackedRatchet, + pub fn craft_revfs_ack( + stacked_ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -2002,8 +2003,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -2015,7 +2016,7 @@ pub(crate) mod file { let payload = ReVFSAckPacket { success, error_msg }; payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -2029,8 +2030,8 @@ pub(crate) mod file { Error { error: String }, } - pub fn craft_revfs_pull_ack( - hyper_ratchet: &StackedRatchet, + pub fn craft_revfs_pull_ack( + stacked_ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -2046,8 +2047,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -2056,7 +2057,7 @@ pub(crate) mod file { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -2068,13 +2069,13 @@ pub(crate) mod udp { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use bytes::BytesMut; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use zerocopy::{U32, U64}; /// Crafts a UDP packet for a given command auxiliary, payload, target CID, and security level - pub(crate) fn craft_udp_packet( - hyper_ratchet: &StackedRatchet, + pub(crate) fn craft_udp_packet( + stacked_ratchet: &R, cmd_aux: u8, payload: BytesMut, target_cid: u64, @@ -2089,8 +2090,8 @@ pub(crate) mod udp { context_info: Default::default(), group: Default::default(), wave_id: Default::default(), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: Default::default(), target_cid: U64::new(target_cid), }; @@ -2099,7 +2100,7 @@ pub(crate) mod udp { header.inscribe_into(&mut packet); packet.extend_from_slice(&payload[..]); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -2111,13 +2112,13 @@ pub(crate) mod hole_punch { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use bytes::{BufMut, BytesMut}; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use zerocopy::{U32, U64}; /// Crafts a hole punch packet for a given plaintext, security level, target CID, and hyper ratchet - pub fn generate_packet( - hyper_ratchet: &StackedRatchet, + pub fn generate_packet( + stacked_ratchet: &R, plaintext: &[u8], security_level: SecurityLevel, target_cid: u64, @@ -2131,8 +2132,8 @@ pub(crate) mod hole_punch { context_info: Default::default(), group: Default::default(), wave_id: Default::default(), - session_cid: U64::new(hyper_ratchet.get_cid()), - drill_version: U32::new(hyper_ratchet.version()), + session_cid: U64::new(stacked_ratchet.get_cid()), + entropy_bank_version: U32::new(stacked_ratchet.version()), timestamp: Default::default(), target_cid: U64::new(target_cid), }; @@ -2141,7 +2142,7 @@ pub(crate) mod hole_punch { header.inscribe_into(&mut packet); packet.put(plaintext); - hyper_ratchet + stacked_ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -2149,10 +2150,10 @@ pub(crate) mod hole_punch { } /// Decrypts a hole punch packet for a given packet, hyper ratchet, security level, and target CID - pub fn decrypt_packet( - _hyper_ratchet: &StackedRatchet, + pub fn decrypt_packet( + stacked_ratchet: &R, packet: &[u8], - _security_level: SecurityLevel, + security_level: SecurityLevel, ) -> Option { if packet.len() < HDP_HEADER_BYTE_LEN { log::warn!(target: "citadel", "Bad hole-punch packet size. Len: {} | {:?}", packet.len(), packet); @@ -2160,10 +2161,10 @@ pub(crate) mod hole_punch { } let mut packet = BytesMut::from(packet); - let _header = packet.split_to(HDP_HEADER_BYTE_LEN); + let header = packet.split_to(HDP_HEADER_BYTE_LEN); - _hyper_ratchet - .validate_message_packet_in_place_split(Some(_security_level), &_header, &mut packet) + stacked_ratchet + .validate_message_packet_in_place_split(Some(security_level), &header, &mut packet) .ok()?; Some(packet) diff --git a/citadel_proto/src/proto/packet_processor/connect_packet.rs b/citadel_proto/src/proto/packet_processor/connect_packet.rs index 54c236f9d..1ec42437d 100644 --- a/citadel_proto/src/proto/packet_processor/connect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/connect_packet.rs @@ -31,14 +31,15 @@ //! - `VirtualConnection`: Manages established connections //! - `SessionSecuritySettings`: Configures connection security //! + use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{ConnectFail, ConnectSuccess, MailboxDelivery}; -use crate::proto::packet_processor::primary_group_packet::get_proper_hyper_ratchet; +use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::proto::ConnectMode; use citadel_user::backend::BackendType; use citadel_user::external_services::ServicesObject; -use std::sync::atomic::Ordering; /// This will optionally return an HdpPacket as a response if deemed necessary #[cfg_attr(feature = "localhost-testing", tracing::instrument( @@ -50,10 +51,10 @@ use std::sync::atomic::Ordering; fields(is_server = sess_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() ) ))] -pub async fn process_connect( - sess_ref: &CitadelSession, +pub async fn process_connect( + sess_ref: &CitadelSession, packet: HdpPacket, - header_drill_vers: u32, + header_entropy_bank_vers: u32, ) -> Result { let session = sess_ref.clone(); @@ -73,7 +74,7 @@ pub async fn process_connect( } let hr = return_if_none!( - get_proper_hyper_ratchet(header_drill_vers, &state_container, None), + get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, None), "Could not get proper HR [connect]" ); let cnac = return_if_none!(state_container.cnac.clone(), "CNAC missing"); @@ -82,7 +83,7 @@ pub async fn process_connect( let (header, payload, _, _) = packet.decompose(); - let (header, payload, hyper_ratchet) = return_if_none!( + let (header, payload, stacked_ratchet) = return_if_none!( validation::aead::validate(hr, &header, payload), "Unable to validate connect packet" ); @@ -108,7 +109,7 @@ pub async fn process_connect( session .file_transfer_compatible .set_once(local_uses_file_system && stage0_packet.uses_filesystem); - let cid = hyper_ratchet.get_cid(); + let cid = stacked_ratchet.get_cid(); let success_time = session.time_tracker.get_global_time_ns(); let addr = session.remote_peer; let is_personal = !session.is_server; @@ -170,7 +171,7 @@ pub async fn process_connect( let success_packet = packet_crafter::do_connect::craft_final_status_packet( - &hyper_ratchet, + &stacked_ratchet, true, mailbox_items, post_login_object.clone(), @@ -181,17 +182,14 @@ pub async fn process_connect( session.account_manager.get_backend_type(), ); - session.implicated_cid.set(Some(cid)); - session - .state - .store(SessionState::Connected, Ordering::Relaxed); + session.session_cid.set(Some(cid)); + session.state.set(SessionState::Connected); - let cxn_type = VirtualConnectionType::LocalGroupServer { - implicated_cid: cid, - }; + let cxn_type = + VirtualConnectionType::LocalGroupServer { session_cid: cid }; let channel_signal = NodeResult::ConnectSuccess(ConnectSuccess { ticket: kernel_ticket, - implicated_cid: cid, + session_cid: cid, remote_addr: addr, is_personal, v_conn_type: cxn_type, @@ -217,7 +215,7 @@ pub async fn process_connect( //session.state = SessionState::NeedsConnect; let packet = packet_crafter::do_connect::craft_final_status_packet( - &hyper_ratchet, + &stacked_ratchet, false, None, ServicesObject::default(), @@ -246,14 +244,12 @@ pub async fn process_connect( let message = String::from_utf8(payload.message.to_vec()) .unwrap_or_else(|_| "Invalid UTF-8 message".to_string()); log::error!(target: "citadel", "The server refused to login the user. Reason: {}", &message); - let cid = hyper_ratchet.get_cid(); + let cid = stacked_ratchet.get_cid(); state_container.connect_state.on_fail(); drop(state_container); - session.implicated_cid.set(None); - session - .state - .store(SessionState::NeedsConnect, Ordering::Relaxed); + session.session_cid.set(None); + session.state.set(SessionState::NeedsConnect); session.disable_dc_signal(); session.send_to_kernel(NodeResult::ConnectFail(ConnectFail { @@ -294,7 +290,7 @@ pub async fn process_connect( let message = String::from_utf8(payload.message.to_vec()) .unwrap_or_else(|_| String::from("Invalid message")); let kernel_ticket = session.kernel_ticket.get(); - let cid = hyper_ratchet.get_cid(); + let cid = stacked_ratchet.get_cid(); state_container.connect_state.on_success(); state_container.connect_state.on_connect_packet_received(); @@ -322,7 +318,7 @@ pub async fn process_connect( .expect("Should be set"); std::mem::drop(state_container); - session.implicated_cid.set(Some(cid)); // This makes is_provisional equal to false + session.session_cid.set(Some(cid)); // This makes is_provisional equal to false let addr = session.remote_peer; let is_personal = !session.is_server; @@ -339,9 +335,8 @@ pub async fn process_connect( let _post_login_object = payload.post_login_object.clone(); //session.post_quantum = pqc; - let cxn_type = VirtualConnectionType::LocalGroupServer { - implicated_cid: cid, - }; + let cxn_type = + VirtualConnectionType::LocalGroupServer { session_cid: cid }; let peers = payload.peers; let timestamp = session.time_tracker.get_global_time_ns(); @@ -349,12 +344,10 @@ pub async fn process_connect( let persistence_handler = session.account_manager.get_persistence_handler().clone(); //session.session_manager.clear_provisional_tracker(session.kernel_ticket); - session - .state - .store(SessionState::Connected, Ordering::Relaxed); + session.state.set(SessionState::Connected); let success_ack = packet_crafter::do_connect::craft_success_ack( - &hyper_ratchet, + &stacked_ratchet, timestamp, security_level, ); @@ -362,7 +355,7 @@ pub async fn process_connect( session.send_to_kernel(NodeResult::ConnectSuccess(ConnectSuccess { ticket: kernel_ticket, - implicated_cid: cid, + session_cid: cid, remote_addr: addr, is_personal, v_conn_type: cxn_type, @@ -376,7 +369,7 @@ pub async fn process_connect( if let Some(mailbox_delivery) = payload.mailbox { session.send_to_kernel(NodeResult::MailboxDelivery( MailboxDelivery { - implicated_cid: cid, + session_cid: cid, ticket_opt: None, items: mailbox_delivery, }, @@ -433,7 +426,7 @@ pub async fn process_connect( if use_ka { let ka = packet_crafter::keep_alive::craft_keep_alive_packet( - &hyper_ratchet, + &stacked_ratchet, timestamp, security_level, ); diff --git a/citadel_proto/src/proto/packet_processor/deregister_packet.rs b/citadel_proto/src/proto/packet_processor/deregister_packet.rs index 688f3ffc5..156b7fb54 100644 --- a/citadel_proto/src/proto/packet_processor/deregister_packet.rs +++ b/citadel_proto/src/proto/packet_processor/deregister_packet.rs @@ -35,8 +35,8 @@ //! use citadel_proto::proto::packet::HdpPacket; //! //! async fn handle_deregister(session: &CitadelSession, packet: HdpPacket) { -//! let header_drill_vers = 1; -//! match deregister_packet::process_deregister(session, packet, header_drill_vers).await { +//! let header_entropy_bank_vers = 1; +//! match deregister_packet::process_deregister(session, packet, header_entropy_bank_vers).await { //! Ok(result) => { //! // Handle successful deregistration //! } @@ -50,20 +50,27 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::DeRegistration; -use crate::proto::packet_processor::primary_group_packet::get_proper_hyper_ratchet; -use citadel_crypt::stacked_ratchet::StackedRatchet; -use std::sync::atomic::Ordering; +use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; +use citadel_crypt::stacked_ratchet::Ratchet; /// processes a deregister packet. The client must be connected to the HyperLAN Server in order to DeRegister -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub async fn process_deregister( - session_ref: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub async fn process_deregister( + session_ref: &CitadelSession, packet: HdpPacket, - header_drill_vers: u32, + header_entropy_bank_vers: u32, ) -> Result { let session = session_ref.clone(); - if session.state.load(Ordering::Relaxed) != SessionState::Connected { + if !session.state.is_connected() { log::error!(target: "citadel", "disconnect packet received, but session state is not connected. Must be connected to deregister. Dropping"); return Ok(PrimaryProcessorResult::Void); } @@ -73,28 +80,28 @@ pub async fn process_deregister( let hr = { let state_container = inner_state!(session.state_container); return_if_none!( - get_proper_hyper_ratchet(header_drill_vers, &state_container, None), + get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, None), "Could not get proper HR [deregister]" ) }; let timestamp = session.time_tracker.get_global_time_ns(); let (header, payload, _, _) = packet.decompose(); - let (header, _payload, hyper_ratchet) = return_if_none!( + let (header, _payload, stacked_ratchet) = return_if_none!( validation::aead::validate(hr, &header, payload), "Unable to validate dereg packet" ); let header = &header; - let implicated_cid = header.session_cid.get(); + let session_cid = header.session_cid.get(); let security_level = header.security_level.into(); match header.cmd_aux { packet_flags::cmd::aux::do_deregister::STAGE0 => { log::trace!(target: "citadel", "STAGE 0 DEREGISTER PACKET RECV"); deregister_client_from_self( - implicated_cid, + session_cid, session, - &hyper_ratchet, + &stacked_ratchet, timestamp, security_level, ) @@ -103,7 +110,7 @@ pub async fn process_deregister( packet_flags::cmd::aux::do_deregister::SUCCESS => { log::trace!(target: "citadel", "STAGE SUCCESS DEREGISTER PACKET RECV"); - deregister_from_hyperlan_server_as_client(implicated_cid, session).await + deregister_from_hyperlan_server_as_client(session_cid, session).await } packet_flags::cmd::aux::do_deregister::FAILURE => { @@ -112,12 +119,11 @@ pub async fn process_deregister( let ticket = state_container.deregister_state.current_ticket; // state_container.deregister_state.on_fail(); std::mem::drop(state_container); - let cid = - return_if_none!(session.implicated_cid.get(), "implicated CID not loaded"); + let cid = return_if_none!(session.session_cid.get(), "implicated CID not loaded"); session .kernel_tx .unbounded_send(NodeResult::DeRegistration(DeRegistration { - implicated_cid: cid, + session_cid: cid, ticket_opt: ticket, success: false, }))?; @@ -137,10 +143,10 @@ pub async fn process_deregister( to_concurrent_processor!(task) } -async fn deregister_client_from_self( - implicated_cid: u64, - session_ref: &CitadelSession, - hyper_ratchet: &StackedRatchet, +async fn deregister_client_from_self( + session_cid: u64, + session_ref: &CitadelSession, + ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> Result { @@ -154,11 +160,11 @@ async fn deregister_client_from_self( (acc_manager, ticket) }; - let (ret, success) = match acc_mgr.delete_client_by_cid(implicated_cid).await { + let (ret, success) = match acc_mgr.delete_client_by_cid(session_cid).await { Ok(_) => { - log::trace!(target: "citadel", "Successfully purged account {} locally!", implicated_cid); + log::trace!(target: "citadel", "Successfully purged account {} locally!", session_cid); let stage_success_packet = packet_crafter::do_deregister::craft_final( - hyper_ratchet, + ratchet, true, timestamp, security_level, @@ -171,9 +177,9 @@ async fn deregister_client_from_self( } Err(err) => { - log::error!(target: "citadel", "Unable to locally purge account {}. Please report this to the HyperLAN Server admin ({:?})", implicated_cid, err); + log::error!(target: "citadel", "Unable to locally purge account {}. Please report this to the HyperLAN Server admin ({:?})", session_cid, err); let stage_failure_packet = packet_crafter::do_deregister::craft_final( - hyper_ratchet, + ratchet, false, timestamp, security_level, @@ -188,15 +194,13 @@ async fn deregister_client_from_self( let session = session_ref; session.send_to_kernel(NodeResult::DeRegistration(DeRegistration { - implicated_cid, + session_cid, ticket_opt: ticket, success, }))?; // This ensures no further packets are processed - session - .state - .store(SessionState::NeedsRegister, Ordering::Relaxed); + session.state.set(SessionState::NeedsRegister); session.send_session_dc_signal( ticket, success, @@ -206,9 +210,9 @@ async fn deregister_client_from_self( Ok(ret) } -async fn deregister_from_hyperlan_server_as_client( - implicated_cid: u64, - session_ref: &CitadelSession, +async fn deregister_from_hyperlan_server_as_client( + session_cid: u64, + session_ref: &CitadelSession, ) -> Result { let session = session_ref; let (acc_manager, dereg_ticket) = { @@ -220,20 +224,20 @@ async fn deregister_from_hyperlan_server_as_client( (acc_manager, dereg_ticket) }; - let success = match acc_manager.delete_client_by_cid(implicated_cid).await { + let success = match acc_manager.delete_client_by_cid(session_cid).await { Ok(_) => { - log::trace!(target: "citadel", "Successfully purged account {} locally!", implicated_cid); + log::trace!(target: "citadel", "Successfully purged account {} locally!", session_cid); true } Err(err) => { - log::error!(target: "citadel", "Unable to locally purge account {}. Please report this to the HyperLAN Server admin. Reason: {:?}", implicated_cid, err); + log::error!(target: "citadel", "Unable to locally purge account {}. Please report this to the HyperLAN Server admin. Reason: {:?}", session_cid, err); false } }; session.send_to_kernel(NodeResult::DeRegistration(DeRegistration { - implicated_cid, + session_cid, ticket_opt: dereg_ticket, success: true, }))?; diff --git a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs index 0d9b59956..793d078e6 100644 --- a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs @@ -35,8 +35,8 @@ //! use citadel_proto::proto::packet::HdpPacket; //! //! async fn handle_disconnect(session: &CitadelSession, packet: HdpPacket) { -//! let header_drill_vers = 1; -//! match disconnect_packet::process_disconnect(session, packet, header_drill_vers).await { +//! let header_entropy_bank_vers = 1; +//! match disconnect_packet::process_disconnect(session, packet, header_entropy_bank_vers).await { //! Ok(result) => { //! // Handle successful disconnection //! } @@ -49,20 +49,28 @@ use super::includes::*; use crate::error::NetworkError; -use crate::proto::packet_processor::primary_group_packet::get_proper_hyper_ratchet; -use std::sync::atomic::Ordering; +use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; +use citadel_crypt::stacked_ratchet::Ratchet; pub const SUCCESS_DISCONNECT: &str = "Successfully Disconnected"; /// Stage 0: Alice sends Bob a DO_DISCONNECT request packet /// Stage 1: Bob sends Alice an FINAL, whereafter Alice may disconnect -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub async fn process_disconnect( - session: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub async fn process_disconnect( + session: &CitadelSession, packet: HdpPacket, - header_drill_vers: u32, + header_entropy_bank_vers: u32, ) -> Result { - if session.state.load(Ordering::Relaxed) != SessionState::Connected { + if !session.state.is_connected() { log::error!(target: "citadel", "disconnect packet received, but session state is not connected. Dropping"); return Ok(PrimaryProcessorResult::Void); } @@ -70,13 +78,13 @@ pub async fn process_disconnect( let hr = { let state_container = inner_state!(session.state_container); return_if_none!( - get_proper_hyper_ratchet(header_drill_vers, &state_container, None), + get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, None), "Could not get proper HR [disconnect]" ) }; let (header, payload, _, _) = packet.decompose(); - let (header, _, hyper_ratchet) = return_if_none!( + let (header, _, stacked_ratchet) = return_if_none!( validation::aead::validate(hr, &header, payload), "Unable to validate" ); @@ -88,7 +96,7 @@ pub async fn process_disconnect( packet_flags::cmd::aux::do_disconnect::STAGE0 => { log::trace!(target: "citadel", "STAGE 0 DISCONNECT PACKET RECEIVED"); let packet = packet_crafter::do_disconnect::craft_final( - &hyper_ratchet, + &stacked_ratchet, ticket, timestamp, security_level, @@ -106,9 +114,7 @@ pub async fn process_disconnect( packet_flags::cmd::aux::do_disconnect::FINAL => { trace!(target: "citadel", "STAGE 1 DISCONNECT PACKET RECEIVED (ticket: {})", ticket); session.kernel_ticket.set(ticket); - session - .state - .store(SessionState::Disconnected, Ordering::Relaxed); + session.state.set(SessionState::Disconnected); session.send_session_dc_signal(Some(ticket), true, SUCCESS_DISCONNECT); Ok(PrimaryProcessorResult::EndSession(SUCCESS_DISCONNECT)) } diff --git a/citadel_proto/src/proto/packet_processor/file_packet.rs b/citadel_proto/src/proto/packet_processor/file_packet.rs index a4b2beba3..7d6af7264 100644 --- a/citadel_proto/src/proto/packet_processor/file_packet.rs +++ b/citadel_proto/src/proto/packet_processor/file_packet.rs @@ -55,19 +55,27 @@ use crate::prelude::{InternalServerError, ReVFSResult, Ticket}; use crate::proto::packet_crafter::file::ReVFSPullAckPacket; use crate::proto::packet_processor::header_to_response_vconn_type; use crate::proto::packet_processor::primary_group_packet::{ - get_proper_hyper_ratchet, get_resp_target_cid_from_header, + get_orientation_safe_ratchet, get_resp_target_cid_from_header, }; use crate::proto::{get_preferred_primary_stream, send_with_error_logging}; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::proto::TransferType; -use std::sync::atomic::Ordering; -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub fn process_file_packet( - session: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub fn process_file_packet( + session: &CitadelSession, packet: HdpPacket, proxy_cid_info: Option<(u64, u64)>, ) -> Result { - if session.state.load(Ordering::Relaxed) != SessionState::Connected { + if !session.state.is_connected() { return Ok(PrimaryProcessorResult::Void); } @@ -78,8 +86,12 @@ pub fn process_file_packet( let header_bytes = &header[..]; let header = return_if_none!(Ref::new(header_bytes), "Unable to validate header layout") as Ref<&[u8], HdpHeader>; - let hyper_ratchet = return_if_none!( - get_proper_hyper_ratchet(header.drill_version.get(), &state_container, proxy_cid_info), + let stacked_ratchet = return_if_none!( + get_orientation_safe_ratchet( + header.entropy_bank_version.get(), + &state_container, + proxy_cid_info + ), "Unable to get proper HR" ); let security_level = header.security_level.into(); @@ -87,7 +99,7 @@ pub fn process_file_packet( let ts = session.time_tracker.get_global_time_ns(); // ALL FILE packets must be authenticated - match validation::group::validate(&hyper_ratchet, security_level, header_bytes, payload) { + match validation::group::validate(&stacked_ratchet, security_level, header_bytes, payload) { Some(payload) => { match header.cmd_aux { packet_flags::cmd::aux::file::FILE_ERROR => { @@ -112,7 +124,7 @@ pub fn process_file_packet( InternalServerError { ticket_opt: Some(ticket), message: payload.error_message, - cid_opt: session.implicated_cid.get(), + cid_opt: session.session_cid.get(), }, ))?; @@ -135,20 +147,19 @@ pub fn process_file_packet( log::trace!(target: "citadel", "Declared local encryption level on file header: {local_encryption_level:?}"); let (target_cid, v_target_flipped) = match v_target { VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => ( - implicated_cid, + session_cid, VirtualConnectionType::LocalGroupPeer { - implicated_cid: target_cid, - peer_cid: implicated_cid, + session_cid: target_cid, + peer_cid: session_cid, }, ), - VirtualConnectionType::LocalGroupServer { implicated_cid } => ( - 0, - VirtualConnectionType::LocalGroupServer { implicated_cid }, - ), + VirtualConnectionType::LocalGroupServer { session_cid } => { + (0, VirtualConnectionType::LocalGroupServer { session_cid }) + } _ => { log::error!(target: "citadel", "HyperWAN functionality not yet enabled"); @@ -166,7 +177,7 @@ pub fn process_file_packet( vfm, session.account_manager.get_persistence_handler(), session.state_container.clone(), - hyper_ratchet, + stacked_ratchet, target_cid, v_target_flipped, preferred_primary_stream, @@ -196,17 +207,17 @@ pub fn process_file_packet( let v_target = payload.virtual_target; let is_p2p = header.session_cid.get() != 0 && header.target_cid.get() != 0; - let implicated_cid = if is_p2p { + let session_cid = if is_p2p { header.target_cid.get() } else { header.session_cid.get() }; - //let implicated_cid = header.session_cid.get(); + //let session_cid = header.session_cid.get(); // conclude by passing this data into the state container if state_container .on_file_header_ack_received( success, - implicated_cid, + session_cid, header.context_info.get().into(), object_id, v_target, @@ -296,7 +307,7 @@ pub fn process_file_packet( // on top of spawning the file transfer subroutine prior to this, // we will also send a REVFS pull ack let response_packet = packet_crafter::file::craft_revfs_pull_ack( - &hyper_ratchet, + &stacked_ratchet, security_level, ticket, ts, @@ -339,7 +350,7 @@ pub fn process_file_packet( .err() .map(|e| e.into_string()); let response_packet = packet_crafter::file::craft_revfs_ack( - &hyper_ratchet, + &stacked_ratchet, security_level, ticket, ts, @@ -369,7 +380,7 @@ pub fn process_file_packet( error_message: payload.error_msg, data: None, ticket, - implicated_cid: hyper_ratchet.get_cid(), + session_cid: stacked_ratchet.get_cid(), }); session.send_to_kernel(response)?; @@ -399,7 +410,7 @@ pub fn process_file_packet( error_message: Some(error), data: None, ticket, - implicated_cid: hyper_ratchet.get_cid(), + session_cid: stacked_ratchet.get_cid(), }); session.send_to_kernel(error_signal)?; diff --git a/citadel_proto/src/proto/packet_processor/hole_punch.rs b/citadel_proto/src/proto/packet_processor/hole_punch.rs index 584a38a39..16919e17f 100644 --- a/citadel_proto/src/proto/packet_processor/hole_punch.rs +++ b/citadel_proto/src/proto/packet_processor/hole_punch.rs @@ -51,13 +51,22 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::packet_processor::primary_group_packet::{ - get_proper_hyper_ratchet, get_resp_target_cid_from_header, + get_orientation_safe_ratchet, get_resp_target_cid_from_header, }; +use citadel_crypt::stacked_ratchet::Ratchet; /// This will handle an inbound group packet -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub fn process_hole_punch( - session: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub fn process_hole_punch( + session: &CitadelSession, packet: HdpPacket, hr_version: u32, proxy_cid_info: Option<(u64, u64)>, @@ -65,7 +74,7 @@ pub fn process_hole_punch( let (header, payload, _, _) = packet.decompose(); let state_container = inner_state!(session.state_container); let hr = return_if_none!( - get_proper_hyper_ratchet(hr_version, &state_container, proxy_cid_info), + get_orientation_safe_ratchet(hr_version, &state_container, proxy_cid_info), "Unable to get proper HR" ); let header = header.as_ref(); diff --git a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs index abf39e06c..130f53075 100644 --- a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs +++ b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs @@ -34,8 +34,8 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; -use crate::proto::packet_processor::primary_group_packet::get_proper_hyper_ratchet; -use std::sync::atomic::Ordering; +use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; +use citadel_crypt::stacked_ratchet::Ratchet; /// This will handle a keep alive packet. It will automatically send a keep packet after it sleeps for a period of time #[allow(unused_results, unused_must_use)] @@ -48,14 +48,14 @@ use std::sync::atomic::Ordering; fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() ) ))] -pub async fn process_keep_alive( - session: &CitadelSession, +pub async fn process_keep_alive( + session: &CitadelSession, packet: HdpPacket, - header_drill_vers: u32, + header_entropy_bank_vers: u32, ) -> Result { let session = session.clone(); // TODO: keep alives for p2p conns - if session.state.load(Ordering::Relaxed) != SessionState::Connected { + if !session.state.is_connected() { log::warn!(target: "citadel", "Keep alive received, but session not connected. Dropping packet"); return Ok(PrimaryProcessorResult::Void); } @@ -66,14 +66,14 @@ pub async fn process_keep_alive( let hr = { let state_container = inner_state!(session.state_container); return_if_none!( - get_proper_hyper_ratchet(header_drill_vers, &state_container, None), + get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, None), "Could not get proper HR [KA]" ) }; let (header, payload, _, _) = packet.decompose(); - if let Some((header, _payload, _hyper_ratchet)) = + if let Some((header, _payload, _stacked_ratchet)) = validation::aead::validate(hr, &header, payload) { let current_timestamp_ns = session.time_tracker.get_global_time_ns(); @@ -117,7 +117,7 @@ pub async fn process_keep_alive( } } else { log::trace!(target: "citadel", "Invalid KEEP_ALIVE window; expired"); - //session.session_manager.clear_session(session.implicated_cid.unwrap()); + //session.session_manager.clear_session(session.session_cid.unwrap()); return Ok(PrimaryProcessorResult::EndSession( "Keep alive arrived too late", )); diff --git a/citadel_proto/src/proto/packet_processor/mod.rs b/citadel_proto/src/proto/packet_processor/mod.rs index 5f1649053..1cee95df1 100644 --- a/citadel_proto/src/proto/packet_processor/mod.rs +++ b/citadel_proto/src/proto/packet_processor/mod.rs @@ -176,12 +176,10 @@ pub(crate) fn header_to_response_vconn_type(header: &HdpHeader) -> VirtualConnec if target_cid != C2S_ENCRYPTION_ONLY { // the peer_cid and implicated cid must be flipped VirtualConnectionType::LocalGroupPeer { - implicated_cid: target_cid, + session_cid: target_cid, peer_cid: session_cid, } } else { - VirtualConnectionType::LocalGroupServer { - implicated_cid: session_cid, - } + VirtualConnectionType::LocalGroupServer { session_cid } } } diff --git a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs index efe8128c6..ac4b4d495 100644 --- a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs +++ b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs @@ -50,7 +50,7 @@ use crate::proto::node_result::{GroupChannelCreated, GroupEvent}; use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; use crate::proto::peer::group_channel::GroupBroadcastPayload; use crate::proto::remote::Ticket; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::proto::{ GroupMemberAlterMode, MemberState, MessageGroupKey, MessageGroupOptions, }; @@ -230,11 +230,11 @@ pub enum GroupBroadcast { ) ))] /// Process a group broadcast message -pub async fn process_group_broadcast( - session_ref: &CitadelSession, +pub async fn process_group_broadcast( + session_ref: &CitadelSession, header: Ref<&[u8], HdpHeader>, payload: &[u8], - sess_hyper_ratchet: &StackedRatchet, + sess_ratchet: &R, ) -> Result { let session = session_ref; let signal = return_if_none!( @@ -245,7 +245,7 @@ pub async fn process_group_broadcast( let ticket = header.context_info.get().into(); let security_level = header.security_level.into(); // since group broadcast packets never get proxied, the implicated cid is the local session cid - let implicated_cid = header.session_cid.get(); + let session_cid = header.session_cid.get(); log::trace!(target: "citadel", "[GROUP:{}] message: {:?}", session.is_server.if_true("server").if_false("client"), signal); match signal { GroupBroadcast::Create { @@ -257,7 +257,7 @@ pub async fn process_group_broadcast( .create_message_group_and_notify( timestamp, ticket, - implicated_cid, + session_cid, initial_peers, security_level, options, @@ -265,7 +265,7 @@ pub async fn process_group_broadcast( .await; let signal = GroupBroadcast::CreateResponse { key }; let return_packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -288,9 +288,9 @@ pub async fn process_group_broadcast( let peer_layer = &session.hypernode_peer_layer; let result = if peer_layer.message_group_exists(key).await { peer_layer - .add_pending_peers_to_group(key, vec![implicated_cid]) + .add_pending_peers_to_group(key, vec![session_cid]) .await; - peer_layer.request_join(implicated_cid, key).await + peer_layer.request_join(session_cid, key).await } else { None }; @@ -301,7 +301,7 @@ pub async fn process_group_broadcast( log::warn!(target: "citadel", "Group {:?} does not exist", key); let error = GroupBroadcast::GroupNonExists { key }; let return_packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &error, ticket, C2S_ENCRYPTION_ONLY, @@ -316,7 +316,7 @@ pub async fn process_group_broadcast( let success = GroupBroadcast::AcceptMembershipResponse { key, success: true }; let return_packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &success, ticket, C2S_ENCRYPTION_ONLY, @@ -341,7 +341,7 @@ pub async fn process_group_broadcast( let signal = GroupBroadcast::RequestJoinPending { result: res, key }; let return_packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -371,7 +371,7 @@ pub async fn process_group_broadcast( groups: message_groups, }; let return_packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -400,14 +400,14 @@ pub async fn process_group_broadcast( ), GroupBroadcast::End { key } => { - return_if_none!(permission_gate(implicated_cid, key), "Permission denied"); + return_if_none!(permission_gate(session_cid, key), "Permission denied"); let success = session .session_manager - .remove_message_group(implicated_cid, timestamp, ticket, key, security_level) + .remove_message_group(session_cid, timestamp, ticket, key, security_level) .await; let signal = GroupBroadcast::EndResponse { key, success }; let return_packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -452,7 +452,7 @@ pub async fn process_group_broadcast( let success = session .session_manager .broadcast_signal_to_group( - implicated_cid, + session_cid, timestamp, ticket, key, @@ -467,7 +467,7 @@ pub async fn process_group_broadcast( .unwrap_or(false); let resp = GroupBroadcast::MessageResponse { key, success }; let packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &resp, ticket, C2S_ENCRYPTION_ONLY, @@ -510,7 +510,7 @@ pub async fn process_group_broadcast( if !session .session_manager .broadcast_signal_to_group( - implicated_cid, + session_cid, timestamp, ticket, key, @@ -531,7 +531,7 @@ pub async fn process_group_broadcast( // tell the user who accepted the membership let signal = GroupBroadcast::AcceptMembershipResponse { key, success }; let packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -566,7 +566,7 @@ pub async fn process_group_broadcast( // tell the user who declined the membership let signal = GroupBroadcast::DeclineMembershipResponse { key, success }; let packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -587,7 +587,7 @@ pub async fn process_group_broadcast( } GroupBroadcast::AcceptMembershipResponse { key, success } => { - if success && (key.cid != implicated_cid) { + if success && (key.cid != session_cid) { create_group_channel(ticket, key, session) } else { forward_signal( @@ -612,11 +612,11 @@ pub async fn process_group_broadcast( .session_manager .kick_from_message_group( GroupMemberAlterMode::Leave, - implicated_cid, + session_cid, timestamp, ticket, key, - vec![implicated_cid], + vec![session_cid], security_level, ) .await @@ -625,12 +625,12 @@ pub async fn process_group_broadcast( let message = if success { format!( "Successfully removed peer {} from room {}:{}", - implicated_cid, key.cid, key.mgid + session_cid, key.cid, key.mgid ) } else { format!( "Unable to remove peer {} from room {}:{}", - implicated_cid, key.cid, key.mgid + session_cid, key.cid, key.mgid ) }; let signal = GroupBroadcast::LeaveRoomResponse { @@ -639,7 +639,7 @@ pub async fn process_group_broadcast( message, }; let packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -673,7 +673,7 @@ pub async fn process_group_broadcast( key, invitees: peers, } => { - return_if_none!(permission_gate(implicated_cid, key), "Permission denied"); + return_if_none!(permission_gate(session_cid, key), "Permission denied"); // the server receives this. It then sends an invitation // if peer is not online, leave some mail. If peer is online, // send invitation @@ -681,7 +681,7 @@ pub async fn process_group_broadcast( let sess_mgr = session.session_manager.clone(); let peer_layer = &session.hypernode_peer_layer; let peer_statuses = persistence_handler - .hyperlan_peers_are_mutuals(implicated_cid, &peers) + .hyperlan_peers_are_mutuals(session_cid, &peers) .await?; if peer_layer.message_group_exists(key).await { @@ -692,7 +692,7 @@ pub async fn process_group_broadcast( peers.iter().cloned().zip(peer_statuses.clone()), true, GroupBroadcast::Invitation { - sender: implicated_cid, + sender: session_cid, key, }, security_level, @@ -715,7 +715,7 @@ pub async fn process_group_broadcast( failed_to_invite_list: peers_failed, }; let packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -727,7 +727,7 @@ pub async fn process_group_broadcast( // Send error message let signal = GroupBroadcast::GroupNonExists { key }; let packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -755,12 +755,12 @@ pub async fn process_group_broadcast( key, kick_list: peers, } => { - return_if_none!(permission_gate(implicated_cid, key), "Permission denied"); + return_if_none!(permission_gate(session_cid, key), "Permission denied"); let success = session .session_manager .kick_from_message_group( GroupMemberAlterMode::Kick, - implicated_cid, + session_cid, timestamp, ticket, key, @@ -772,7 +772,7 @@ pub async fn process_group_broadcast( .unwrap_or(false); let resp = GroupBroadcast::KickResponse { key, success }; let packet = packet_crafter::peer_cmd::craft_group_message_packet( - sess_hyper_ratchet, + sess_ratchet, &resp, ticket, C2S_ENCRYPTION_ONLY, @@ -817,21 +817,21 @@ pub async fn process_group_broadcast( } /// Create a group channel -fn create_group_channel( +fn create_group_channel( ticket: Ticket, key: MessageGroupKey, - session: &CitadelSession, + session: &CitadelSession, ) -> Result { let channel = inner_mut_state!(session.state_container) .setup_group_channel_endpoints(key, ticket, session)?; - let implicated_cid = session - .implicated_cid + let session_cid = session + .session_cid .get() .ok_or_else(|| NetworkError::msg("Implicated CID not loaded"))?; session.send_to_kernel(NodeResult::GroupChannelCreated(GroupChannelCreated { ticket, channel, - implicated_cid, + session_cid, }))?; Ok(PrimaryProcessorResult::Void) } @@ -851,13 +851,13 @@ impl From for GroupBroadcastPayload { } /// Forward a signal to the kernel or a group channel -fn forward_signal( - session: &CitadelSession, +fn forward_signal( + session: &CitadelSession, ticket: Ticket, key: Option, broadcast: GroupBroadcast, ) -> Result { - let implicated_cid = return_if_none!(session.implicated_cid.get(), "Implicated CID not loaded"); + let session_cid = return_if_none!(session.session_cid.get(), "Implicated CID not loaded"); if let Some(key) = key { // send to the dedicated channel @@ -876,7 +876,7 @@ fn forward_signal( // send to kernel session .send_to_kernel(NodeResult::GroupEvent(GroupEvent { - implicated_cid, + session_cid, ticket, event: broadcast, })) @@ -885,8 +885,8 @@ fn forward_signal( } /// Permission gate for group operations -fn permission_gate(implicated_cid: u64, key: MessageGroupKey) -> Option<()> { - if implicated_cid != key.cid { +fn permission_gate(session_cid: u64, key: MessageGroupKey) -> Option<()> { + if session_cid != key.cid { None } else { Some(()) diff --git a/citadel_proto/src/proto/packet_processor/peer/mod.rs b/citadel_proto/src/proto/packet_processor/peer/mod.rs index a5bc8c40e..6e9bbdb7e 100644 --- a/citadel_proto/src/proto/packet_processor/peer/mod.rs +++ b/citadel_proto/src/proto/packet_processor/peer/mod.rs @@ -29,22 +29,23 @@ use crate::error::NetworkError; use crate::prelude::{ConnectFail, NodeResult, Ticket}; use crate::proto::session::CitadelSession; +use citadel_crypt::stacked_ratchet::Ratchet; pub mod group_broadcast; pub mod peer_cmd_packet; pub mod server; pub mod signal_handler_interface; -pub(crate) fn send_dc_signal_peer>( - session: &CitadelSession, +pub(crate) fn send_dc_signal_peer, R: Ratchet>( + session: &CitadelSession, ticket: Ticket, err: T, ) -> Result<(), NetworkError> { - let implicated_cid = session.implicated_cid.get().expect("Should exist"); + let session_cid = session.session_cid.get().expect("Should exist"); session .send_to_kernel(NodeResult::ConnectFail(ConnectFail { ticket, - cid_opt: Some(implicated_cid), + cid_opt: Some(session_cid), error_message: err.into(), })) .map_err(|err| NetworkError::Generic(err.to_string()))?; diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index 88527a52f..a9d11ece3 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -66,12 +66,9 @@ use std::sync::atomic::Ordering; use bytes::BytesMut; -use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; +use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, PeerSessionCrypto}; use citadel_crypt::prelude::ConstructorOpts; -use citadel_crypt::stacked_ratchet::constructor::{ - AliceToBobTransfer, BobToAliceTransfer, BobToAliceTransferType, StackedRatchetConstructor, -}; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_crypt::toolset::Toolset; use citadel_types::proto::UdpMode; use citadel_user::backend::BackendType; @@ -87,13 +84,13 @@ use crate::proto::packet_processor::preconnect_packet::{ calculate_sync_time, generate_hole_punch_crypt_container, }; use crate::proto::packet_processor::primary_group_packet::{ - get_proper_hyper_ratchet, get_resp_target_cid, + get_orientation_safe_ratchet, get_resp_target_cid, }; use crate::proto::peer::hole_punch_compat_sink_stream::ReliableOrderedCompatStream; use crate::proto::peer::p2p_conn_handler::attempt_simultaneous_hole_punch; use crate::proto::peer::peer_crypt::{KeyExchangeProcess, PeerNatInfo}; use crate::proto::peer::peer_layer::{ - HyperNodePeerLayerInner, NodeConnectionType, PeerConnectionType, PeerResponse, PeerSignal, + CitadelNodePeerLayerInner, NodeConnectionType, PeerConnectionType, PeerResponse, PeerSignal, }; use crate::proto::remote::Ticket; use crate::proto::session_manager::CitadelSessionManager; @@ -103,36 +100,48 @@ use netbeam::sync::network_endpoint::NetworkEndpoint; #[allow(unused_results)] /// Insofar, there is no use of endpoint-to-endpoint encryption for PEER_CMD packets because they are mediated between the /// HyperLAN client and the HyperLAN Server -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session_orig.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub async fn process_peer_cmd( - session_orig: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session_orig.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub async fn process_peer_cmd( + session_orig: &CitadelSession, aux_cmd: u8, packet: HdpPacket, - header_drill_version: u32, + header_entropy_bank_version: u32, endpoint_cid_info: Option<(u64, u64)>, ) -> Result { // ALL PEER_CMD packets require that the current session contain a CNAC (not anymore since switching to async) let session = session_orig.clone(); let (header, payload, _peer_addr, _) = packet.decompose(); - let (implicated_cid, sess_hyper_ratchet, payload, security_level) = { + let (session_cid, sess_stacked_ratchet, payload, security_level) = { // Some PEER_CMD packets get encrypted using the endpoint crypto log::trace!(target: "citadel", "RECV PEER CMD packet (proxy: {})", endpoint_cid_info.is_some()); let state_container = inner_state!(session.state_container); - let implicated_cid = return_if_none!(session.implicated_cid.get()); - let sess_hyper_ratchet = return_if_none!( - get_proper_hyper_ratchet(header_drill_version, &state_container, endpoint_cid_info), + let session_cid = return_if_none!(session.session_cid.get()); + let sess_stacked_ratchet = return_if_none!( + get_orientation_safe_ratchet( + header_entropy_bank_version, + &state_container, + endpoint_cid_info + ), "Unable to obtain peer HR (P_CMD_PKT)" ); let (header, payload) = return_if_none!( - validation::aead::validate_custom(&sess_hyper_ratchet, &header, payload), + validation::aead::validate_custom(&sess_stacked_ratchet, &header, payload), "Unable to validate peer CMD packet" ); let security_level = header.security_level.into(); log::trace!(target: "citadel", "PEER CMD packet authenticated"); - (implicated_cid, sess_hyper_ratchet, payload, security_level) + (session_cid, sess_stacked_ratchet, payload, security_level) }; let task = async move { @@ -146,7 +155,7 @@ pub async fn process_peer_cmd( session, header, &payload[..], - &sess_hyper_ratchet, + &sess_stacked_ratchet, ) .await } @@ -168,7 +177,7 @@ pub async fn process_peer_cmd( // below line is confusing. The logic is answered in the server block for PeerSignal::Disconnect let target = resp .as_ref() - .map(|_| vconn.get_original_implicated_cid()) + .map(|_| vconn.get_original_session_cid()) .unwrap_or_else(|| vconn.get_original_target_cid()); let state_container = inner_state!(session.state_container); if let Some(v_conn) = @@ -220,7 +229,7 @@ pub async fn process_peer_cmd( session.send_to_kernel(NodeResult::PeerEvent(PeerEvent { event: signal, ticket, - implicated_cid, + session_cid, }))?; return Ok(PrimaryProcessorResult::Void); } @@ -236,13 +245,13 @@ pub async fn process_peer_cmd( PeerSignal::DeregistrationSuccess { peer_conn_type } => { let peer_cid = peer_conn_type.get_original_target_cid(); - log::trace!(target: "citadel", "[Deregistration] about to remove peer {} from {} at the endpoint", peer_cid, implicated_cid); + log::trace!(target: "citadel", "[Deregistration] about to remove peer {} from {} at the endpoint", peer_cid, session_cid); let acc_mgr = &session.account_manager; let kernel_tx = &session.kernel_tx; if (acc_mgr .get_persistence_handler() - .deregister_p2p_as_client(implicated_cid, peer_cid) + .deregister_p2p_as_client(session_cid, peer_cid) .await?) .is_none() { @@ -254,7 +263,7 @@ pub async fn process_peer_cmd( peer_conn_type: *peer_conn_type, }, ticket, - implicated_cid, + session_cid, }))?; return Ok(PrimaryProcessorResult::Void); } @@ -269,7 +278,7 @@ pub async fn process_peer_cmd( let to_kernel = session.kernel_tx.clone(); let account_manager = session.account_manager.clone(); - let peer_cid = vconn.get_original_implicated_cid(); + let peer_cid = vconn.get_original_session_cid(); let this_cid = vconn.get_original_target_cid(); match account_manager @@ -293,7 +302,7 @@ pub async fn process_peer_cmd( ))), }, ticket, - implicated_cid, + session_cid, }))?; } @@ -306,7 +315,7 @@ pub async fn process_peer_cmd( peer_connection_type: vconn.reverse(), }, ticket, - implicated_cid, + session_cid, }))?; } } @@ -328,7 +337,7 @@ pub async fn process_peer_cmd( if accepted { return match conn { PeerConnectionType::LocalGroupPeer { - implicated_cid: original_implicated_cid, + session_cid: original_session_cid, peer_cid: original_target_cid, } => { // this implies this node is receiving an accept_request. As such, we need to NOT @@ -336,37 +345,32 @@ pub async fn process_peer_cmd( // establish a working [PeerChannel] system that has a custom post-quantum key and toolset // unique to the session. //let mut state_container = inner_mut!(session.state_container); - //let peer_cid = conn.get_original_implicated_cid(); + //let peer_cid = conn.get_original_session_cid(); - let alice_constructor = - return_if_none!(StackedRatchetConstructor::new_alice( + let alice_constructor = return_if_none!( + >::new_alice( ConstructorOpts::new_vec_init( Some(endpoint_security_settings.crypto_params), - (endpoint_security_settings - .security_level - .value() - + 1) - as usize + endpoint_security_settings.security_level ), conn.get_original_target_cid(), 0, - Some(endpoint_security_settings.security_level) - )); + ) + ); let transfer = return_if_none!( alice_constructor.stage0_alice(), "AliceConstructor None" ); //log::trace!(target: "citadel", "0. Len: {}, {:?}", alice_pub_key.len(), &alice_pub_key[..10]); - let msg_bytes = - return_if_none!(transfer.serialize_to_vec()); + let msg_bytes = return_if_none!( + SyncIO::serialize_to_vector(&transfer).ok() + ); let mut state_container = inner_mut_state!(session.state_container); let session_password = state_container - .get_session_password( - conn.get_original_implicated_cid(), - ) + .get_session_password(conn.get_original_session_cid()) .cloned(); if session_password.is_none() { log::error!(target: "citadel", "The session password locally is set to None. This is a development issue, please report"); @@ -384,17 +388,17 @@ pub async fn process_peer_cmd( Some(alice_constructor); state_container.peer_kem_states.insert( - *original_implicated_cid, + *original_session_cid, peer_kem_state_container, ); drop(state_container); // finally, prepare the signal and send outbound - // signal: PeerSignal, pqc: &Rc, drill: &EntropyBank, ticket: Ticket, timestamp: i64 + // signal: PeerSignal, pqc: &Rc, entropy_bank: &EntropyBank, ticket: Ticket, timestamp: i64 let signal = PeerSignal::Kex { peer_conn_type: PeerConnectionType::LocalGroupPeer { - implicated_cid: *original_target_cid, - peer_cid: *original_implicated_cid, + session_cid: *original_target_cid, + peer_cid: *original_session_cid, }, kex_payload: KeyExchangeProcess::Stage0( msg_bytes, @@ -405,7 +409,7 @@ pub async fn process_peer_cmd( let stage0_peer_kem = packet_crafter::peer_cmd::craft_peer_signal( - &sess_hyper_ratchet, + &sess_stacked_ratchet, signal, ticket, timestamp, @@ -430,7 +434,7 @@ pub async fn process_peer_cmd( peer_connection_type: conn.reverse(), }, ticket, - implicated_cid, + session_cid, }))?; } } @@ -449,9 +453,9 @@ pub async fn process_peer_cmd( // We generate bob's pqc, as well as a nonce //let mut state_container = inner_mut!(session.state_container); //let this_cid = conn.get_original_target_cid(); - let peer_cid = conn.get_original_implicated_cid(); + let peer_cid = conn.get_original_session_cid(); let transfer_deser = return_if_none!( - AliceToBobTransfer::deserialize_from(transfer) + SyncIO::deserialize_from_vector(transfer).ok() ); let mut state_container = @@ -465,19 +469,17 @@ pub async fn process_peer_cmd( let session_password = session_password.unwrap_or_default(); - let bob_constructor = - return_if_none!(StackedRatchetConstructor::new_bob( + let mut bob_constructor = return_if_none!( + >::new_bob( conn.get_original_target_cid(), - 0, ConstructorOpts::new_vec_init( Some(session_security_settings.crypto_params), - (session_security_settings.security_level.value() - + 1) - as usize + session_security_settings.security_level, ), transfer_deser, session_password.as_ref(), - )); + ) + ); let transfer = return_if_none!(bob_constructor.stage0_bob()); let bob_transfer = return_if_none!(transfer.serialize_to_vector().ok()); @@ -508,7 +510,7 @@ pub async fn process_peer_cmd( drop(state_container); let stage1_kem = packet_crafter::peer_cmd::craft_peer_signal( - &sess_hyper_ratchet, + &sess_stacked_ratchet, signal, ticket, timestamp, @@ -541,7 +543,7 @@ pub async fn process_peer_cmd( ) = { let mut state_container = inner_mut_state!(session.state_container); - let peer_cid = conn.get_original_implicated_cid(); + let peer_cid = conn.get_original_session_cid(); let this_cid = conn.get_original_target_cid(); let mut kem_state = return_if_none!(state_container .peer_kem_states @@ -553,15 +555,15 @@ pub async fn process_peer_cmd( let mut alice_constructor = return_if_none!(kem_state.constructor.take()); let deser = return_if_none!( - BobToAliceTransfer::deserialize_from(transfer), + SyncIO::deserialize_from_vector(transfer).ok(), "bad deser" ); if let Err(err) = alice_constructor.stage1_alice( - BobToAliceTransferType::Default(deser), + deser, kem_state.session_password.as_ref(), ) { - log::warn!(target: "citadel", "Failed to complete key exchange for {implicated_cid} | Wrong session passwords? Err: {err:?}"); + log::warn!(target: "citadel", "Failed to complete key exchange for {session_cid} | Wrong session passwords? Err: {err:?}"); send_dc_signal_peer( session, ticket, @@ -575,7 +577,7 @@ pub async fn process_peer_cmd( }; let error_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_hyper_ratchet, + &sess_stacked_ratchet, error_signal, ticket, timestamp, @@ -585,19 +587,19 @@ pub async fn process_peer_cmd( error_packet, )); } - let hyper_ratchet = return_if_none!( + let stacked_ratchet = return_if_none!( alice_constructor.finish_with_custom_cid(this_cid) ); - let endpoint_hyper_ratchet = hyper_ratchet.clone(); + let endpoint_stacked_ratchet = stacked_ratchet.clone(); // now, create a new toolset and encrypt it // NOTE: when this toolset gets transmitted, it retains this_cid // As such, the other end MUST change the CID internally for BOTH - // toolset AND the single drill - let toolset = Toolset::new(this_cid, hyper_ratchet); + // toolset AND the single entropy_bank + let toolset = Toolset::new(this_cid, stacked_ratchet); // now, register the loaded PQC + toolset into the virtual conn let peer_crypto = PeerSessionCrypto::new(toolset, true); let vconn_type = VirtualConnectionType::LocalGroupPeer { - implicated_cid: this_cid, + session_cid: this_cid, peer_cid, }; let (needs_turn, bob_predicted_socket_addr) = bob_nat_info @@ -643,13 +645,13 @@ pub async fn process_peer_cmd( }; let endpoint_security_level = - endpoint_hyper_ratchet.get_default_security_level(); + endpoint_stacked_ratchet.get_default_security_level(); let hole_punch_compat_stream = - ReliableOrderedCompatStream::new( + ReliableOrderedCompatStream::::new( return_if_none!(session.to_primary_stream.clone()), &mut state_container, peer_cid, - endpoint_hyper_ratchet.clone(), + endpoint_stacked_ratchet.clone(), endpoint_security_level, ); let ticket_for_chan = state_container @@ -659,7 +661,7 @@ pub async fn process_peer_cmd( let stun_servers = session.stun_servers.clone(); let encrypted_config_container = generate_hole_punch_crypt_container( - endpoint_hyper_ratchet, + endpoint_stacked_ratchet, SecurityLevel::Standard, peer_cid, stun_servers, @@ -668,7 +670,7 @@ pub async fn process_peer_cmd( // we need to use the session pqc since this signal needs to get processed by the center node let stage2_kem_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_hyper_ratchet, + &sess_stacked_ratchet, signal, ticket, timestamp, @@ -706,7 +708,7 @@ pub async fn process_peer_cmd( log::warn!(target: "citadel", "This p2p connection requires TURN-like routing"); session.send_to_kernel(channel_signal)?; } else { - let implicated_cid = session.implicated_cid.clone(); + let session_cid = session.session_cid.clone(); let kernel_tx = session.kernel_tx.clone(); // must send packet before registering app, otherwise, registration will fail let app = NetworkEndpoint::register( @@ -723,7 +725,7 @@ pub async fn process_peer_cmd( ticket, session.clone(), bob_nat_info.clone(), - implicated_cid, + session_cid, kernel_tx, channel_signal, sync_instant, @@ -747,14 +749,14 @@ pub async fn process_peer_cmd( // NEW UPDATE: now that we know the other side successfully created its toolset, // calculate sync time then begin the hole punch subroutine log::trace!(target: "citadel", "RECV STAGE 2 PEER KEM"); - let peer_cid = conn.get_original_implicated_cid(); + let peer_cid = conn.get_original_session_cid(); let this_cid = conn.get_original_target_cid(); //let security_level = session.security_level; let ( hole_punch_compat_stream, channel, udp_rx_opt, - endpoint_hyper_ratchet, + endpoint_stacked_ratchet, ticket_for_chan, needs_turn, ) = { @@ -770,18 +772,20 @@ pub async fn process_peer_cmd( let bob_constructor = return_if_none!(kem.constructor.take()); let udp_rx_opt = kem.udp_channel_sender.rx.take(); - let endpoint_hyper_ratchet = return_if_none!( + let endpoint_stacked_ratchet = return_if_none!( bob_constructor.finish_with_custom_cid(this_cid) ); let endpoint_security_level = - endpoint_hyper_ratchet.get_default_security_level(); - let toolset = - Toolset::new(this_cid, endpoint_hyper_ratchet.clone()); + endpoint_stacked_ratchet.get_default_security_level(); + let toolset = Toolset::new( + this_cid, + endpoint_stacked_ratchet.clone(), + ); let peer_crypto = PeerSessionCrypto::new(toolset, false); // create an endpoint vconn let vconn_type = VirtualConnectionType::LocalGroupPeer { - implicated_cid: this_cid, + session_cid: this_cid, peer_cid, }; let (needs_turn, alice_predicted_socket_addr) = @@ -816,18 +820,18 @@ pub async fn process_peer_cmd( .outgoing_peer_connect_attempts .remove(&peer_cid); let hole_punch_compat_stream = - ReliableOrderedCompatStream::new( + ReliableOrderedCompatStream::::new( return_if_none!(session.to_primary_stream.clone()), &mut state_container, peer_cid, - endpoint_hyper_ratchet.clone(), + endpoint_stacked_ratchet.clone(), endpoint_security_level, ); ( hole_punch_compat_stream, channel, udp_rx_opt, - endpoint_hyper_ratchet, + endpoint_stacked_ratchet, ticket_for_chan, needs_turn, ) @@ -859,7 +863,7 @@ pub async fn process_peer_cmd( let stun_servers = session.stun_servers.clone(); let encrypted_config_container = generate_hole_punch_crypt_container( - endpoint_hyper_ratchet, + endpoint_stacked_ratchet, SecurityLevel::Standard, peer_cid, stun_servers, @@ -870,8 +874,8 @@ pub async fn process_peer_cmd( as u64); let sync_instant = Instant::now() + diff; - // session: HdpSession, expected_peer_cid: u64, peer_endpoint_addr: SocketAddr, implicated_cid: Arc>>, kernel_tx: UnboundedSender, sync_time: Instant - let implicated_cid = session.implicated_cid.clone(); + // session: HdpSession, expected_peer_cid: u64, peer_endpoint_addr: SocketAddr, session_cid: Arc>>, kernel_tx: UnboundedSender, sync_time: Instant + let session_cid = session.session_cid.clone(); let kernel_tx = session.kernel_tx.clone(); let client_config = session.client_config.clone(); @@ -880,7 +884,7 @@ pub async fn process_peer_cmd( ticket, session.clone(), alice_nat_info.clone(), - implicated_cid, + session_cid, kernel_tx.clone(), channel_signal, sync_instant, @@ -919,7 +923,7 @@ pub async fn process_peer_cmd( .unbounded_send(NodeResult::PeerEvent(PeerEvent { event: signal, ticket, - implicated_cid, + session_cid, }))?; Ok(PrimaryProcessorResult::Void) } else { @@ -927,7 +931,7 @@ pub async fn process_peer_cmd( session, signal, ticket, - sess_hyper_ratchet, + sess_stacked_ratchet, header, timestamp, security_level, @@ -948,11 +952,11 @@ pub async fn process_peer_cmd( to_concurrent_processor!(task) } -async fn process_signal_command_as_server( - sess_ref: &CitadelSession, +async fn process_signal_command_as_server( + sess_ref: &CitadelSession, signal: PeerSignal, ticket: Ticket, - sess_hyper_ratchet: StackedRatchet, + sess_stacked_ratchet: R, header: Ref<&[u8], HdpHeader>, timestamp: i64, security_level: SecurityLevel, @@ -998,16 +1002,16 @@ async fn process_signal_command_as_server( peer_conn_type: conn, kex_payload: kep, }; - if sess_hyper_ratchet.get_cid() == conn.get_original_target_cid() { + if sess_stacked_ratchet.get_cid() == conn.get_original_target_cid() { log::error!(target: "citadel", "Error (equivalent CIDs)"); return Ok(PrimaryProcessorResult::Void); } let peer_cid = conn.get_original_target_cid(); - let res = sess_mgr.send_signal_to_peer_direct(peer_cid, move |peer_hyper_ratchet| { + let res = sess_mgr.send_signal_to_peer_direct(peer_cid, move |peer_stacked_ratchet| { packet_crafter::peer_cmd::craft_peer_signal( - peer_hyper_ratchet, + peer_stacked_ratchet, signal_to, ticket, timestamp, @@ -1018,7 +1022,7 @@ async fn process_signal_command_as_server( if let Err(err) = res { reply_to_sender_err( err, - &sess_hyper_ratchet, + &sess_stacked_ratchet, ticket, timestamp, security_level, @@ -1039,10 +1043,10 @@ async fn process_signal_command_as_server( // check to see if the client is connected, and if not, send to HypernodePeerLayer match peer_conn_type { PeerConnectionType::LocalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, peer_cid: target_cid, } => { - let implicated_cid = header.session_cid.get(); + let session_cid = header.session_cid.get(); const TIMEOUT: Duration = Duration::from_secs(60 * 60); // 1 hour // if the peer response is some, then HyperLAN Client B responded if let Some(peer_response) = peer_response { @@ -1052,11 +1056,11 @@ async fn process_signal_command_as_server( username, peer_response, ticket, - implicated_cid, + session_cid, target_cid, timestamp, session, - &sess_hyper_ratchet, + &sess_stacked_ratchet, security_level, ) .await @@ -1077,16 +1081,16 @@ async fn process_signal_command_as_server( // the signal is going to be routed from HyperLAN client A to HyperLAN client B (initiation phase). No FCM // NOTE: we MUST redefine peer_conn_type since it may be overwritten if only a username is given let peer_conn_type = PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, }; let mut peer_layer = session.hypernode_peer_layer.inner.write().await; if let Some(ticket_new) = - peer_layer.check_simultaneous_register(implicated_cid, target_cid) + peer_layer.check_simultaneous_register(session_cid, target_cid) { - log::info!(target: "citadel", "Simultaneous register detected! Simulating implicated_cid={} sent an accept_register to target={}", implicated_cid, target_cid); - peer_layer.insert_mapped_ticket(implicated_cid, ticket_new, ticket); + log::info!(target: "citadel", "Simultaneous register detected! Simulating session_cid={} sent an accept_register to target={}", session_cid, target_cid); + peer_layer.insert_mapped_ticket(session_cid, ticket_new, ticket); drop(peer_layer); // route signal to peer let _ = @@ -1095,11 +1099,11 @@ async fn process_signal_command_as_server( username.clone(), PeerResponse::Accept(Some(username)), ticket_new, - implicated_cid, + session_cid, target_cid, timestamp, session, - &sess_hyper_ratchet, + &sess_stacked_ratchet, security_level, ) .await?; @@ -1112,8 +1116,8 @@ async fn process_signal_command_as_server( // TODO: get rid of multiple username fields // we have to flip the ordering for here alone since the endpoint handler for this signal expects do let peer_conn_type = PeerConnectionType::LocalGroupPeer { - implicated_cid: target_cid, - peer_cid: implicated_cid, + session_cid: target_cid, + peer_cid: session_cid, }; let cmd = PeerSignal::PostRegister { peer_conn_type, @@ -1124,7 +1128,7 @@ async fn process_signal_command_as_server( }; let rebound_accept = packet_crafter::peer_cmd::craft_peer_signal( - &sess_hyper_ratchet, + &sess_stacked_ratchet, cmd, ticket, timestamp, @@ -1144,13 +1148,13 @@ async fn process_signal_command_as_server( invitee_response: None, }, TIMEOUT, - implicated_cid, + session_cid, target_cid, timestamp, ticket, &to_primary_stream, &sess_mgr, - &sess_hyper_ratchet, + &sess_stacked_ratchet, security_level, &mut peer_layer, ) @@ -1160,7 +1164,7 @@ async fn process_signal_command_as_server( } PeerConnectionType::ExternalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, interserver_cid: _icid, peer_cid: _target_cid, } => { @@ -1175,7 +1179,7 @@ async fn process_signal_command_as_server( // then, delete the cid entry from the CNAC and save to the local FS match peer_conn_type { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => { let mut peer_layer_lock = session.hypernode_peer_layer.inner.write().await; @@ -1185,7 +1189,7 @@ async fn process_signal_command_as_server( let mut register_event = false; let dereg_result = if peer_layer_lock - .check_simultaneous_deregister(implicated_cid, target_cid) + .check_simultaneous_deregister(session_cid, target_cid) .is_some() { // if the other peer is simultaneously deregistering, mark as Ok(()) @@ -1195,7 +1199,7 @@ async fn process_signal_command_as_server( register_event = true; account_manager .get_persistence_handler() - .deregister_p2p_as_server(implicated_cid, target_cid) + .deregister_p2p_as_server(session_cid, target_cid) .await }; @@ -1205,7 +1209,7 @@ async fn process_signal_command_as_server( log::trace!(target: "citadel", "Registering dereg event"); peer_layer_lock .insert_tracked_posting( - implicated_cid, + session_cid, Duration::from_secs(60 * 60), ticket, PeerSignal::DeregistrationSuccess { peer_conn_type }, @@ -1230,7 +1234,7 @@ async fn process_signal_command_as_server( // now, send a success packet to the client let success_cmd = PeerSignal::DeregistrationSuccess { peer_conn_type }; let rebound_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_hyper_ratchet, + &sess_stacked_ratchet, success_cmd, ticket, timestamp, @@ -1246,12 +1250,12 @@ async fn process_signal_command_as_server( ticket, error: err.into_string(), peer_connection_type: PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, }, }; let error_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_hyper_ratchet, + &sess_stacked_ratchet, error_signal, ticket, timestamp, @@ -1263,7 +1267,7 @@ async fn process_signal_command_as_server( } PeerConnectionType::ExternalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, interserver_cid: _icid, peer_cid: _target_cid, } => { @@ -1283,7 +1287,7 @@ async fn process_signal_command_as_server( } => { match peer_conn_type { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => { // TODO: Change timeouts. Create a better timeout system, in general @@ -1295,11 +1299,11 @@ async fn process_signal_command_as_server( peer_response, endpoint_security_level, udp_enabled, - implicated_cid, + session_cid, target_cid, timestamp, sess_ref, - &sess_hyper_ratchet, + &sess_stacked_ratchet, security_level, ) .await @@ -1309,11 +1313,11 @@ async fn process_signal_command_as_server( let sess_mgr = session.session_manager.clone(); let mut peer_layer = session.hypernode_peer_layer.inner.write().await; if let Some(ticket_new) = - peer_layer.check_simultaneous_connect(implicated_cid, target_cid) + peer_layer.check_simultaneous_connect(session_cid, target_cid) { - log::trace!(target: "citadel", "Simultaneous connect detected! Simulating implicated_cid={} sent an accept_connect to target={}", implicated_cid, target_cid); + log::trace!(target: "citadel", "Simultaneous connect detected! Simulating session_cid={} sent an accept_connect to target={}", session_cid, target_cid); log::trace!(target: "citadel", "Simultaneous connect: first_ticket: {} | sender expected ticket: {}", ticket_new, ticket); - peer_layer.insert_mapped_ticket(implicated_cid, ticket_new, ticket); + peer_layer.insert_mapped_ticket(session_cid, ticket_new, ticket); // NOTE: Packet will rebound to sender, then, sender will locally send // packet to the peer who first attempted a connect request drop(peer_layer); @@ -1324,11 +1328,11 @@ async fn process_signal_command_as_server( PeerResponse::Accept(None), endpoint_security_level, udp_enabled, - implicated_cid, + session_cid, target_cid, timestamp, sess_ref, - &sess_hyper_ratchet, + &sess_stacked_ratchet, security_level, ) .await?; @@ -1344,13 +1348,13 @@ async fn process_signal_command_as_server( session_password, }, TIMEOUT, - implicated_cid, + session_cid, target_cid, timestamp, ticket, &to_primary_stream, &sess_mgr, - &sess_hyper_ratchet, + &sess_stacked_ratchet, security_level, &mut peer_layer, ) @@ -1360,7 +1364,7 @@ async fn process_signal_command_as_server( } PeerConnectionType::ExternalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, interserver_cid: _icid, peer_cid: _target_cid, } => { @@ -1376,7 +1380,7 @@ async fn process_signal_command_as_server( } => { match peer_conn_type { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => { let state_container = inner_state!(session.state_container); @@ -1410,25 +1414,25 @@ async fn process_signal_command_as_server( .map(|v_conn| v_conn.is_active.store(false, Ordering::SeqCst)); let resp = Some(resp.unwrap_or(PeerResponse::Disconnected(format!( - "Peer {implicated_cid} closed the virtual connection to {target_cid}" + "Peer {session_cid} closed the virtual connection to {target_cid}" )))); let signal_to_peer = PeerSignal::Disconnect { peer_conn_type: PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, }, disconnect_response: resp, }; - // now, remove target CID's v_conn to `implicated_cid` + // now, remove target CID's v_conn to `session_cid` std::mem::drop(state_container); let _ = session_manager.disconnect_virtual_conn( - implicated_cid, + session_cid, target_cid, - move |peer_hyper_ratchet| { + move |peer_stacked_ratchet| { // send signal to peer packet_crafter::peer_cmd::craft_peer_signal( - peer_hyper_ratchet, + peer_stacked_ratchet, signal_to_peer, ticket, timestamp, @@ -1442,7 +1446,7 @@ async fn process_signal_command_as_server( let rebound_signal = PeerSignal::Disconnect { peer_conn_type: PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, }, disconnect_response: Some(PeerResponse::Disconnected( @@ -1452,13 +1456,13 @@ async fn process_signal_command_as_server( reply_to_sender( rebound_signal, - &sess_hyper_ratchet, + &sess_stacked_ratchet, ticket, timestamp, security_level, ) } else { - //reply_to_sender_err(format!("{} is not connected to {}", implicated_cid, target_cid), &sess_hyper_ratchet, ticket, timestamp, security_level) + //reply_to_sender_err(format!("{} is not connected to {}", session_cid, target_cid), &sess_stacked_ratchet, ticket, timestamp, security_level) // connection may already be dc'ed from another dc attempt. Just say nothing Ok(PrimaryProcessorResult::Void) } @@ -1477,7 +1481,7 @@ async fn process_signal_command_as_server( limit, } => { match hypernode_conn_type { - NodeConnectionType::LocalGroupPeerToLocalGroupServer(_implicated_cid) => { + NodeConnectionType::LocalGroupPeerToLocalGroupServer(_session_cid) => { let account_manager = session.account_manager.clone(); let session_manager = session.session_manager.clone(); @@ -1514,14 +1518,14 @@ async fn process_signal_command_as_server( log::trace!(target: "citadel", "[GetRegisteredPeers] Done getting list"); reply_to_sender( rebound_signal, - &sess_hyper_ratchet, + &sess_stacked_ratchet, ticket, timestamp, security_level, ) } - NodeConnectionType::LocalGroupPeerToExternalGroupServer(_implicated_cid, _icid) => { + NodeConnectionType::LocalGroupPeerToExternalGroupServer(_session_cid, _icid) => { log::error!(target: "citadel", "HyperWAN functionality not implemented"); Ok(PrimaryProcessorResult::Void) } @@ -1532,14 +1536,13 @@ async fn process_signal_command_as_server( v_conn_type: hypernode_conn_type, response: _resp_opt, } => match hypernode_conn_type { - NodeConnectionType::LocalGroupPeerToLocalGroupServer(implicated_cid) => { + NodeConnectionType::LocalGroupPeerToLocalGroupServer(session_cid) => { let account_manager = session.account_manager.clone(); let session_manager = session.session_manager.clone(); log::trace!(target: "citadel", "[GetMutuals] Getting list"); - let rebound_signal = if let Some(mutuals) = account_manager - .get_hyperlan_peer_list(implicated_cid) - .await? + let rebound_signal = if let Some(mutuals) = + account_manager.get_hyperlan_peer_list(session_cid).await? { let online_status = session_manager.check_online_status(&mutuals); let peer_info = account_manager.get_peer_info_from_cids(&mutuals).await; @@ -1563,21 +1566,21 @@ async fn process_signal_command_as_server( log::trace!(target: "citadel", "[GetMutuals] Done getting list"); reply_to_sender( rebound_signal, - &sess_hyper_ratchet, + &sess_stacked_ratchet, ticket, timestamp, security_level, ) } - NodeConnectionType::LocalGroupPeerToExternalGroupServer(_implicated_cid, _icid) => { + NodeConnectionType::LocalGroupPeerToExternalGroupServer(_session_cid, _icid) => { log::error!(target: "citadel", "HyperWAN functionality not implemented"); Ok(PrimaryProcessorResult::Void) } }, PeerSignal::BroadcastConnected { - implicated_cid: _cid, + session_cid: _cid, group_broadcast: _hypernode_conn_type, } => Ok(PrimaryProcessorResult::Void), @@ -1608,7 +1611,7 @@ async fn process_signal_command_as_server( .unbounded_send(NodeResult::PeerEvent(PeerEvent { event: signal.clone(), ticket, - implicated_cid: sess_hyper_ratchet.get_cid(), + session_cid: sess_stacked_ratchet.get_cid(), }))?; let peer_cid = peer_connection_type.get_original_target_cid(); @@ -1621,9 +1624,9 @@ async fn process_signal_command_as_server( let res = inner!(session.session_manager).send_signal_to_peer_direct( peer_cid, - move |peer_hyper_ratchet| { + move |peer_stacked_ratchet| { packet_crafter::peer_cmd::craft_peer_signal( - peer_hyper_ratchet, + peer_stacked_ratchet, signal, ticket, timestamp, @@ -1635,7 +1638,7 @@ async fn process_signal_command_as_server( if let Err(err) = res { reply_to_sender_err( err, - &sess_hyper_ratchet, + &sess_stacked_ratchet, ticket, timestamp, security_level, @@ -1652,7 +1655,7 @@ async fn process_signal_command_as_server( .unbounded_send(NodeResult::PeerEvent(PeerEvent { event: signal, ticket, - implicated_cid: sess_hyper_ratchet.get_cid(), + session_cid: sess_stacked_ratchet.get_cid(), }))?; Ok(PrimaryProcessorResult::Void) } @@ -1670,15 +1673,15 @@ async fn process_signal_command_as_server( #[inline] /// This just makes the repeated operation above cleaner. By itself does not send anything; must return the result of this closure directly -fn reply_to_sender( +fn reply_to_sender( signal: PeerSignal, - hyper_ratchet: &StackedRatchet, + stacked_ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, ) -> Result { let packet = packet_crafter::peer_cmd::craft_peer_signal( - hyper_ratchet, + stacked_ratchet, signal, ticket, timestamp, @@ -1687,9 +1690,9 @@ fn reply_to_sender( Ok(PrimaryProcessorResult::ReplyToSender(packet)) } -fn reply_to_sender_err( +fn reply_to_sender_err( err: E, - hyper_ratchet: &StackedRatchet, + stacked_ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, @@ -1698,7 +1701,7 @@ fn reply_to_sender_err( Ok(PrimaryProcessorResult::ReplyToSender( construct_error_signal( err, - hyper_ratchet, + stacked_ratchet, ticket, timestamp, security_level, @@ -1707,9 +1710,9 @@ fn reply_to_sender_err( )) } -fn construct_error_signal( +fn construct_error_signal( err: E, - hyper_ratchet: &StackedRatchet, + stacked_ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, @@ -1719,12 +1722,12 @@ fn construct_error_signal( ticket, error: err.to_string(), peer_connection_type: PeerConnectionType::LocalGroupPeer { - implicated_cid: hyper_ratchet.get_cid(), + session_cid: stacked_ratchet.get_cid(), peer_cid, }, }; packet_crafter::peer_cmd::craft_peer_signal( - hyper_ratchet, + stacked_ratchet, err_signal, ticket, timestamp, @@ -1733,38 +1736,38 @@ fn construct_error_signal( } #[allow(clippy::too_many_arguments)] -pub(crate) async fn route_signal_and_register_ticket_forwards( +pub(crate) async fn route_signal_and_register_ticket_forwards( signal: PeerSignal, timeout: Duration, - implicated_cid: u64, + session_cid: u64, target_cid: u64, timestamp: i64, ticket: Ticket, to_primary_stream: &OutboundPrimaryStreamSender, - sess_mgr: &CitadelSessionManager, - sess_hyper_ratchet: &StackedRatchet, + sess_mgr: &CitadelSessionManager, + sess_stacked_ratchet: &R, security_level: SecurityLevel, - peer_layer: &mut HyperNodePeerLayerInner, + peer_layer: &mut CitadelNodePeerLayerInner, ) -> Result { - let sess_hyper_ratchet_2 = sess_hyper_ratchet.clone(); + let sess_stacked_ratchet_2 = sess_stacked_ratchet.clone(); let to_primary_stream = to_primary_stream.clone(); // Give the target_cid 10 seconds to respond - let res = sess_mgr.route_signal_primary(peer_layer, implicated_cid, target_cid, ticket, signal.clone(), move |peer_hyper_ratchet| { - packet_crafter::peer_cmd::craft_peer_signal(peer_hyper_ratchet, signal.clone(), ticket, timestamp, security_level) + let res = sess_mgr.route_signal_primary(peer_layer, session_cid, target_cid, ticket, signal.clone(), move |peer_stacked_ratchet| { + packet_crafter::peer_cmd::craft_peer_signal(peer_stacked_ratchet, signal.clone(), ticket, timestamp, security_level) }, timeout, move |stale_signal| { // on timeout, run this // TODO: Use latest ratchet, otherwise, may expire - log::warn!(target: "citadel", "Running timeout closure. Sending error message to {}", implicated_cid); - let error_packet = packet_crafter::peer_cmd::craft_peer_signal(&sess_hyper_ratchet_2, stale_signal, ticket, timestamp, security_level); + log::warn!(target: "citadel", "Running timeout closure. Sending error message to {}", session_cid); + let error_packet = packet_crafter::peer_cmd::craft_peer_signal(&sess_stacked_ratchet_2, stale_signal, ticket, timestamp, security_level); let _ = to_primary_stream.unbounded_send(error_packet); }).await; - // Then, we tell the implicated_cid's node that we have handled the message. However, the peer has yet to respond + // Then, we tell the session_cid's node that we have handled the message. However, the peer has yet to respond if let Err(err) = res { reply_to_sender_err( err, - sess_hyper_ratchet, + sess_stacked_ratchet, ticket, timestamp, security_level, @@ -1774,7 +1777,7 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( let received_signal = PeerSignal::SignalReceived { ticket }; reply_to_sender( received_signal, - sess_hyper_ratchet, + sess_stacked_ratchet, ticket, timestamp, security_level, @@ -1784,30 +1787,30 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( // returns (true, status) if the process was a success, or (false, success) otherwise #[allow(clippy::too_many_arguments)] -pub(crate) async fn route_signal_response( +pub(crate) async fn route_signal_response( signal: PeerSignal, - implicated_cid: u64, + session_cid: u64, target_cid: u64, timestamp: i64, ticket: Ticket, - session: CitadelSession, - sess_hyper_ratchet: &StackedRatchet, - on_route_finished: impl FnOnce(&CitadelSession, &CitadelSession, PeerSignal), + session: CitadelSession, + sess_stacked_ratchet: &R, + on_route_finished: impl FnOnce(&CitadelSession, &CitadelSession, PeerSignal), security_level: SecurityLevel, ) -> Result { - trace!(target: "citadel", "Routing signal {:?} | impl: {} | target: {}", signal, implicated_cid, target_cid); + trace!(target: "citadel", "Routing signal {:?} | impl: {} | target: {}", signal, session_cid, target_cid); let sess_ref = &session; let res = session .session_manager .route_signal_response_primary( - implicated_cid, + session_cid, target_cid, ticket, sess_ref, - move |peer_hyper_ratchet| { + move |peer_stacked_ratchet| { packet_crafter::peer_cmd::craft_peer_signal( - peer_hyper_ratchet, + peer_stacked_ratchet, signal, ticket, timestamp, @@ -1819,7 +1822,7 @@ pub(crate) async fn route_signal_response( let received_signal = PeerSignal::SignalReceived { ticket }; let ret = reply_to_sender( received_signal, - sess_hyper_ratchet, + sess_stacked_ratchet, ticket, timestamp, security_level, @@ -1839,7 +1842,7 @@ pub(crate) async fn route_signal_response( log::warn!(target: "citadel", "Unable to route signal! {:?}", err); reply_to_sender_err( err, - sess_hyper_ratchet, + sess_stacked_ratchet, ticket, timestamp, security_level, diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs index 6e98fbc52..1768117ff 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs @@ -36,23 +36,30 @@ use crate::proto::packet_processor::peer::peer_cmd_packet::route_signal_response use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::{SessionSecuritySettings, UdpMode}; -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session.is_server, implicated_cid = implicated_cid, target_cid = target_cid)))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session.is_server, session_cid = session_cid, target_cid = target_cid) +))] #[allow(clippy::too_many_arguments)] -pub(crate) async fn handle_response_phase_post_connect( +pub(crate) async fn handle_response_phase_post_connect( peer_conn_type: PeerConnectionType, ticket: Ticket, peer_response: PeerResponse, endpoint_security_level: SessionSecuritySettings, udp_enabled: UdpMode, - implicated_cid: u64, + session_cid: u64, target_cid: u64, timestamp: i64, - session: &CitadelSession, - sess_hyper_ratchet: &StackedRatchet, + session: &CitadelSession, + sess_stacked_ratchet: &R, security_level: SecurityLevel, ) -> Result { // the signal is going to be routed from HyperLAN Client B to HyperLAN client A (response phase) @@ -63,7 +70,7 @@ pub(crate) async fn handle_response_phase_post_connect( session_security_settings: endpoint_security_level, udp_mode: udp_enabled, session_password: None, - }, implicated_cid, target_cid, timestamp, ticket,session.clone(), sess_hyper_ratchet, + }, session_cid, target_cid, timestamp, ticket, session.clone(), sess_stacked_ratchet, |this_sess, peer_sess, _original_tracked_posting| { // when the route finishes, we need to update both sessions to allow high-level message-passing // In other words, forge a virtual connection @@ -77,18 +84,18 @@ pub(crate) async fn handle_response_phase_post_connect( // The UDP senders may not exist (e.g., TCP only mode) let this_udp_sender = this_sess_state_container.udp_primary_outbound_tx.clone(); let peer_udp_sender = peer_sess_state_container.udp_primary_outbound_tx.clone(); - // rel to this local sess, the key = target_cid, then (implicated_cid, target_cid) + // rel to this local sess, the key = target_cid, then (session_cid, target_cid) let virtual_conn_relative_to_this = VirtualConnectionType::LocalGroupPeer { - implicated_cid, - peer_cid: target_cid + session_cid, + peer_cid: target_cid, }; let virtual_conn_relative_to_peer = VirtualConnectionType::LocalGroupPeer { - implicated_cid: target_cid, - peer_cid: implicated_cid + session_cid: target_cid, + peer_cid: session_cid, }; this_sess_state_container.insert_new_virtual_connection_as_server(target_cid, virtual_conn_relative_to_this, peer_udp_sender, peer_tcp_sender); - peer_sess_state_container.insert_new_virtual_connection_as_server(implicated_cid, virtual_conn_relative_to_peer, this_udp_sender, this_tcp_sender); - log::trace!(target: "citadel", "Virtual connection between {} <-> {} forged", implicated_cid, target_cid); + peer_sess_state_container.insert_new_virtual_connection_as_server(session_cid, virtual_conn_relative_to_peer, this_udp_sender, this_tcp_sender); + log::trace!(target: "citadel", "Virtual connection between {} <-> {} forged", session_cid, target_cid); // TODO: Ensure that, upon disconnect, the corresponding entry gets dropped in the connection table of not the dropped peer } } diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs index 805110ea6..8ddfe5674 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs @@ -35,21 +35,28 @@ use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::peer::peer_layer::Username; use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session.is_server, implicated_cid = implicated_cid, target_cid = target_cid)))] +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session.is_server, session_cid = session_cid, target_cid = target_cid) +))] #[allow(clippy::too_many_arguments)] -pub async fn handle_response_phase_post_register( +pub async fn handle_response_phase_post_register( peer_conn_type: PeerConnectionType, username: Username, peer_response: PeerResponse, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, target_cid: u64, timestamp: i64, - session: &CitadelSession, - sess_hyper_ratchet: &StackedRatchet, + session: &CitadelSession, + sess_stacked_ratchet: &R, security_level: SecurityLevel, ) -> Result { let decline = matches!(&peer_response, PeerResponse::Decline); @@ -60,12 +67,12 @@ pub async fn handle_response_phase_post_register( invitee_username: None, ticket_opt: Some(ticket), invitee_response: Some(peer_response), - }, implicated_cid, target_cid, timestamp, ticket, session.clone(), sess_hyper_ratchet, + }, session_cid, target_cid, timestamp, ticket, session.clone(), sess_stacked_ratchet, |this_sess, _peer_sess, _original_tracked_posting| { if !decline { let account_manager = this_sess.account_manager.clone(); let task = async move { - if let Err(err) = account_manager.register_hyperlan_p2p_as_server(implicated_cid, target_cid).await { + if let Err(err) = account_manager.register_hyperlan_p2p_as_server(session_cid, target_cid).await { // TODO: route error log::error!(target: "citadel", "Unable to register hyperlan p2p at server: {:?}", err); } diff --git a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs index 9998fe506..f75169497 100644 --- a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs @@ -35,8 +35,8 @@ //! use citadel_proto::proto::packet::HdpPacket; //! //! async fn handle_preconnect(session: &CitadelSession, packet: HdpPacket) { -//! let header_drill_vers = 1; -//! match preconnect_packet::process_preconnect(session, packet, header_drill_vers).await { +//! let header_entropy_bank_vers = 1; +//! match preconnect_packet::process_preconnect(session, packet, header_entropy_bank_vers).await { //! Ok(result) => { //! // Handle successful preconnect processing //! } @@ -47,7 +47,8 @@ //! } //! ``` -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::endpoint_crypto_container::AssociatedSecurityLevel; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; use citadel_wire::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; use netbeam::sync::RelativeNodeType; @@ -65,12 +66,11 @@ use citadel_types::proto::UdpMode; use super::includes::*; use crate::proto::node_result::ConnectFail; -use crate::proto::packet_processor::primary_group_packet::get_proper_hyper_ratchet; +use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; use crate::proto::state_subcontainers::preconnect_state_container::UdpChannelSender; use citadel_wire::exports::Connection; use citadel_wire::udp_traversal::udp_hole_puncher::EndpointHolePunchExt; use netbeam::sync::network_endpoint::NetworkEndpoint; -use std::sync::atomic::Ordering; /// Handles preconnect packets. Handles the NAT traversal #[cfg_attr(feature = "localhost-testing", tracing::instrument( @@ -82,10 +82,10 @@ use std::sync::atomic::Ordering; fields(is_server = session_orig.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() ) ))] -pub async fn process_preconnect( - session_orig: &CitadelSession, +pub async fn process_preconnect( + session_orig: &CitadelSession, packet: HdpPacket, - header_drill_vers: u32, + header_entropy_bank_vers: u32, ) -> Result { let session = session_orig.clone(); @@ -148,13 +148,13 @@ pub async fn process_preconnect( udp_mode, kat, nat_type, - new_hyper_ratchet, + new_stacked_ratchet, )) => { session.adjacent_nat_type.set_once(Some(nat_type)); state_container.pre_connect_state.generated_ratchet = - Some(new_hyper_ratchet); + Some(new_stacked_ratchet); // since the SYN's been validated, the CNACs toolset has been updated - let new_session_sec_lvl = transfer.security_level; + let new_session_sec_lvl = transfer.security_level(); log::trace!(target: "citadel", "Synchronizing toolsets. UDP mode: {:?}. Session security level: {:?}", udp_mode, new_session_sec_lvl); // TODO: Rate limiting to prevent SYN flooding @@ -206,9 +206,9 @@ pub async fn process_preconnect( inner_state!(session.state_container).cnac.clone(), "SESS Cnac not loaded" )); - let implicated_cid = header.session_cid.get(); + let session_cid = header.session_cid.get(); - let (stream, new_hyper_ratchet) = { + let (stream, new_stacked_ratchet) = { let mut state_container = inner_mut_state!(session.state_container); if state_container.pre_connect_state.last_stage == packet_flags::cmd::aux::do_preconnect::SYN_ACK @@ -218,8 +218,8 @@ pub async fn process_preconnect( state_container.pre_connect_state.constructor.take(), "Alice constructor not loaded" ); - let implicated_cid = header.session_cid.get(); - if let Some((new_hyper_ratchet, nat_type)) = + let session_cid = header.session_cid.get(); + if let Some((new_stacked_ratchet, nat_type)) = validation::pre_connect::validate_syn_ack( &session.session_password, cnac, @@ -227,21 +227,17 @@ pub async fn process_preconnect( packet, ) { - // The toolset, at this point, has already been updated. The CNAC can be used to - //let ref drill = cnac.get_drill_blocking(None)?; session.adjacent_nat_type.set_once(Some(nat_type)); state_container.pre_connect_state.generated_ratchet = - Some(new_hyper_ratchet.clone()); + Some(new_stacked_ratchet.clone()); let local_node_type = session.local_node_type; let timestamp = session.time_tracker.get_global_time_ns(); - //let local_bind_addr = session.local_bind_addr.ip(); - //let local_bind_addr = session.implicated_user_p2p_internal_listener_addr.clone()?; if state_container.udp_mode == UdpMode::Disabled { let stage0_preconnect_packet = packet_crafter::pre_connect::craft_stage0( - &new_hyper_ratchet, + &new_stacked_ratchet, timestamp, local_node_type, security_level, @@ -263,17 +259,17 @@ pub async fn process_preconnect( quic_conn, session.local_bind_addr, )), - &new_hyper_ratchet, + &new_stacked_ratchet, session, security_level, - implicated_cid, + session_cid, &mut state_container, ); } let stage0_preconnect_packet = packet_crafter::pre_connect::craft_stage0( - &new_hyper_ratchet, + &new_stacked_ratchet, timestamp, local_node_type, security_level, @@ -284,15 +280,14 @@ pub async fn process_preconnect( ); to_primary_stream.unbounded_send(stage0_preconnect_packet)?; - //let hole_puncher = SingleUDPHolePuncher::new_initiator(session.local_nat_type.clone(), generate_hole_punch_crypt_container(new_hyper_ratchet.clone(), SecurityLevel::Standard), nat_type, local_bind_addr, server_external_addr, server_internal_addr).ok()?; - let stream = ReliableOrderedCompatStream::new( + let stream = ReliableOrderedCompatStream::::new( to_primary_stream, &mut state_container, C2S_ENCRYPTION_ONLY, - new_hyper_ratchet.clone(), + new_stacked_ratchet.clone(), security_level, ); - (stream, new_hyper_ratchet) + (stream, new_stacked_ratchet) } else { log::error!(target: "citadel", "Invalid SYN_ACK"); return Ok(PrimaryProcessorResult::Void); @@ -310,7 +305,7 @@ pub async fn process_preconnect( let stun_servers = session.stun_servers.clone(); let res = conn .begin_udp_hole_punch(generate_hole_punch_crypt_container( - new_hyper_ratchet.clone(), + new_stacked_ratchet.clone(), SecurityLevel::Standard, C2S_ENCRYPTION_ONLY, stun_servers, @@ -322,10 +317,10 @@ pub async fn process_preconnect( log::trace!(target: "citadel", "Initiator finished NAT traversal ..."); send_success_as_initiator( Some(get_raw_udp_interface(ret)), - &new_hyper_ratchet, + &new_stacked_ratchet, session, security_level, - implicated_cid, + session_cid, &mut inner_mut_state!(session.state_container), ) } @@ -334,10 +329,10 @@ pub async fn process_preconnect( log::warn!(target: "citadel", "Hole punch attempt failed {:?}", err.to_string()); send_success_as_initiator( None, - &new_hyper_ratchet, + &new_stacked_ratchet, session, security_level, - implicated_cid, + session_cid, &mut inner_mut_state!(session.state_container), ) } @@ -347,13 +342,13 @@ pub async fn process_preconnect( packet_flags::cmd::aux::do_preconnect::STAGE0 => { log::trace!(target: "citadel", "RECV STAGE 0 PRE_CONNECT PACKET"); - let implicated_cid = header.session_cid.get(); - let (hyper_ratchet, stream) = { + let session_cid = header.session_cid.get(); + let (stacked_ratchet, stream) = { let mut state_container = inner_mut_state!(session.state_container); // At this point, the user's static-key identity has been verified. We can now check the online status to ensure no double-logins - let hyper_ratchet = return_if_none!( - get_proper_hyper_ratchet( - header.drill_version.get(), + let stacked_ratchet = return_if_none!( + get_orientation_safe_ratchet( + header.entropy_bank_version.get(), &state_container, None ), @@ -363,7 +358,7 @@ pub async fn process_preconnect( if state_container.pre_connect_state.last_stage == packet_flags::cmd::aux::do_preconnect::SYN_ACK { - if validation::pre_connect::validate_stage0(&hyper_ratchet, packet) + if validation::pre_connect::validate_stage0(&stacked_ratchet, packet) .is_some() { let timestamp = session.time_tracker.get_global_time_ns(); @@ -376,7 +371,7 @@ pub async fn process_preconnect( // We have to modify the state to ensure that this node can receive a DO_CONNECT packet state_container.pre_connect_state.success = true; let packet = packet_crafter::pre_connect::craft_begin_connect( - &hyper_ratchet, + &stacked_ratchet, timestamp, security_level, ); @@ -389,14 +384,14 @@ pub async fn process_preconnect( "Primary stream not loaded" ); - let stream = ReliableOrderedCompatStream::new( + let stream = ReliableOrderedCompatStream::::new( to_primary_stream, &mut state_container, C2S_ENCRYPTION_ONLY, - hyper_ratchet.clone(), + stacked_ratchet.clone(), security_level, ); - (hyper_ratchet, stream) + (stacked_ratchet, stream) } else { log::error!(target: "citadel", "Unable to validate stage 0 packet"); return Ok(PrimaryProcessorResult::Void); @@ -414,7 +409,7 @@ pub async fn process_preconnect( let stun_servers = session.stun_servers.clone(); let res = conn .begin_udp_hole_punch(generate_hole_punch_crypt_container( - hyper_ratchet.clone(), + stacked_ratchet.clone(), SecurityLevel::Standard, C2S_ENCRYPTION_ONLY, stun_servers, @@ -425,7 +420,7 @@ pub async fn process_preconnect( Ok(ret) => handle_success_as_receiver( Some(get_raw_udp_interface(ret)), session, - implicated_cid, + session_cid, &mut inner_mut_state!(session.state_container), ), @@ -455,13 +450,13 @@ pub async fn process_preconnect( let timestamp = session.time_tracker.get_global_time_ns(); let mut state_container = inner_mut_state!(session.state_container); let hr = return_if_none!( - get_proper_hyper_ratchet(header_drill_vers, &state_container, None), + get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, None), "Could not get proper HR [preconnect0]" ); let cnac = &(return_if_none!(state_container.cnac.clone(), "Sess CNAC not loaded")); let tcp_only = header.algorithm == payload_identifiers::do_preconnect::TCP_ONLY; let (header, packet, ..) = packet.decompose(); - if let Some((header, _, hyper_ratchet)) = + if let Some((header, _, stacked_ratchet)) = validation::aead::validate(hr, &header, packet) { state_container.pre_connect_state.success = true; @@ -473,7 +468,7 @@ pub async fn process_preconnect( if tcp_only { log::warn!(target: "citadel", "Received signal to fall-back to TCP only mode"); let begin_connect = packet_crafter::pre_connect::craft_begin_connect( - &hyper_ratchet, + &stacked_ratchet, timestamp, security_level, ); @@ -513,7 +508,7 @@ pub async fn process_preconnect( )) } else { let begin_connect = packet_crafter::pre_connect::craft_begin_connect( - &hyper_ratchet, + &stacked_ratchet, timestamp, security_level, ); @@ -530,7 +525,7 @@ pub async fn process_preconnect( log::trace!(target: "citadel", "RECV STAGE BEGIN_CONNECT PRE_CONNECT PACKET"); let mut state_container = inner_mut_state!(session.state_container); let hr = return_if_none!( - get_proper_hyper_ratchet(header_drill_vers, &state_container, None), + get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, None), "Could not get proper HR [preconnect1]" ); @@ -538,13 +533,13 @@ pub async fn process_preconnect( == packet_flags::cmd::aux::do_preconnect::SUCCESS { let (header, payload, _, _) = packet.decompose(); - if let Some((_, _, hyper_ratchet)) = + if let Some((_, _, stacked_ratchet)) = validation::aead::validate(hr, &header, payload) { state_container.pre_connect_state.success = true; std::mem::drop(state_container); // now, begin stage 0 connect - begin_connect_process(session, &hyper_ratchet, security_level) + begin_connect_process(session, &stacked_ratchet, security_level) } else { log::error!(target: "citadel", "Unable to validate success_ack packet. Dropping"); Ok(PrimaryProcessorResult::Void) @@ -580,9 +575,9 @@ pub async fn process_preconnect( to_concurrent_processor!(task) } -fn begin_connect_process( - session: &CitadelSession, - hyper_ratchet: &StackedRatchet, +fn begin_connect_process( + session: &CitadelSession, + stacked_ratchet: &R, security_level: SecurityLevel, ) -> Result { // at this point, the session keys have already been re-established. We just need to begin the login stage @@ -594,7 +589,7 @@ fn begin_connect_process( ); let stage0_connect_packet = crate::proto::packet_crafter::do_connect::craft_stage0_packet( - hyper_ratchet, + stacked_ratchet, proposed_credentials, timestamp, security_level, @@ -604,9 +599,7 @@ fn begin_connect_process( // we now store the pqc temporarily in the state container //session.post_quantum = Some(new_pqc); std::mem::drop(state_container); - session - .state - .store(SessionState::ConnectionProcess, Ordering::Relaxed); + session.state.set(SessionState::ConnectionProcess); log::trace!(target: "citadel", "Successfully sent stage0 connect packet outbound"); @@ -614,18 +607,18 @@ fn begin_connect_process( Ok(PrimaryProcessorResult::ReplyToSender(stage0_connect_packet)) } -fn send_success_as_initiator( +fn send_success_as_initiator( udp_splittable: Option, - hyper_ratchet: &StackedRatchet, - session: &CitadelSession, + stacked_ratchet: &R, + session: &CitadelSession, security_level: SecurityLevel, - implicated_cid: u64, - state_container: &mut StateContainerInner, + session_cid: u64, + state_container: &mut StateContainerInner, ) -> Result { - let _ = handle_success_as_receiver(udp_splittable, session, implicated_cid, state_container)?; + let _ = handle_success_as_receiver(udp_splittable, session, session_cid, state_container)?; let success_packet = packet_crafter::pre_connect::craft_stage_final( - hyper_ratchet, + stacked_ratchet, true, false, session.time_tracker.get_global_time_ns(), @@ -634,11 +627,11 @@ fn send_success_as_initiator( Ok(PrimaryProcessorResult::ReplyToSender(success_packet)) } -fn handle_success_as_receiver( +fn handle_success_as_receiver( udp_splittable: Option, - session: &CitadelSession, - implicated_cid: u64, - state_container: &mut StateContainerInner, + session: &CitadelSession, + session_cid: u64, + state_container: &mut StateContainerInner, ) -> Result { let tcp_loaded_alerter_rx = state_container.setup_tcp_alert_if_udp_c2s(); @@ -661,7 +654,7 @@ fn handle_success_as_receiver( if state_container.udp_mode == UdpMode::Enabled { CitadelSession::udp_socket_loader( session.clone(), - VirtualTargetType::LocalGroupServer { implicated_cid }, + VirtualTargetType::LocalGroupServer { session_cid }, udp_splittable, peer_addr, session.kernel_ticket.get(), @@ -675,18 +668,18 @@ fn handle_success_as_receiver( Ok(PrimaryProcessorResult::Void) } -pub(crate) fn generate_hole_punch_crypt_container( - hyper_ratchet: StackedRatchet, +pub(crate) fn generate_hole_punch_crypt_container( + stacked_ratchet: R, security_level: SecurityLevel, target_cid: u64, stun_servers: Option>, ) -> HolePunchConfigContainer { - let hyper_ratchet_cloned = hyper_ratchet.clone(); + let stacked_ratchet_cloned = stacked_ratchet.clone(); HolePunchConfigContainer::new( move |plaintext| { packet_crafter::hole_punch::generate_packet( - &hyper_ratchet, + &stacked_ratchet, plaintext, security_level, target_cid, @@ -694,7 +687,7 @@ pub(crate) fn generate_hole_punch_crypt_container( }, move |packet| { packet_crafter::hole_punch::decrypt_packet( - &hyper_ratchet_cloned, + &stacked_ratchet_cloned, packet, security_level, ) diff --git a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs index b6253e41d..4bf2a145c 100644 --- a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs +++ b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs @@ -64,15 +64,12 @@ use crate::proto::validation::group::{GroupHeader, GroupHeaderAck, WaveAck}; use citadel_crypt::endpoint_crypto_container::{ EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, }; -use citadel_crypt::fcm::fcm_ratchet::ThinRatchet; use citadel_crypt::misc::CryptError; -use citadel_crypt::stacked_ratchet::constructor::{AliceToBobTransferType, ConstructorType}; -use citadel_crypt::stacked_ratchet::{Ratchet, RatchetType, StackedRatchet}; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecrecyMode; use citadel_types::prelude::ObjectId; use citadel_types::proto::UdpMode; use std::ops::Deref; -use std::sync::atomic::Ordering; /// This will handle an inbound primary group packet /// NOTE: Since incorporating the proxy features, if a packet gets to this process closure, it implies the packet @@ -81,9 +78,17 @@ use std::sync::atomic::Ordering; /// `proxy_cid_info`: is None if the packets were not proxied, and will thus use the session's pqcrypto to authenticate the data. /// If `proxy_cid_info` is Some, then a tuple of the original implicated cid (peer cid) and the original target cid (this cid) /// will be provided. In this case, we must use the virtual conn's crypto -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub fn process_primary_packet( - session_ref: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub fn process_primary_packet( + session_ref: &CitadelSession, cmd_aux: u8, packet: HdpPacket, proxy_cid_info: Option<(u64, u64)>, @@ -97,7 +102,7 @@ pub fn process_primary_packet( .. } = session.inner.deref(); - if state.load(Ordering::Relaxed) != SessionState::Connected { + if !state.is_connected() { log::warn!(target: "citadel", "Group packet dropped; session not connected"); return Ok(PrimaryProcessorResult::Void); } @@ -115,12 +120,16 @@ pub fn process_primary_packet( let header_bytes = &header[..]; let header = return_if_none!(Ref::new(header_bytes), "Unable to load header [PGP]") as Ref<&[u8], HdpHeader>; - let hyper_ratchet = return_if_none!( - get_proper_hyper_ratchet(header.drill_version.get(), &state_container, proxy_cid_info), + let stacked_ratchet = return_if_none!( + get_orientation_safe_ratchet( + header.entropy_bank_version.get(), + &state_container, + proxy_cid_info + ), "Unable to get proper StackedRatchet [PGP]" ); let security_level = header.security_level.into(); - //log::trace!(target: "citadel", "[Peer StackedRatchet] Obtained version {} w/ CID {} (local CID: {})", hyper_ratchet.version(), hyper_ratchet.get_cid(), header.session_cid.get()); + //log::trace!(target: "citadel", "[Peer StackedRatchet] Obtained version {} w/ CID {} (local CID: {})", stacked_ratchet.version(), stacked_ratchet.get_cid(), header.session_cid.get()); match header.cmd_aux { packet_flags::cmd::aux::group::GROUP_PAYLOAD => { log::trace!(target: "citadel", "RECV GROUP PAYLOAD {:?}", header); @@ -128,7 +137,7 @@ pub fn process_primary_packet( match state_container.on_group_payload_received( &header, payload.freeze(), - &hyper_ratchet, + &stacked_ratchet, ) { Ok(res) => { state_container.meta_expiry_state.on_event_confirmation(); @@ -150,7 +159,7 @@ pub fn process_primary_packet( InternalServerError { ticket_opt: Some(ticket), message: err.to_string(), - cid_opt: session.implicated_cid.get(), + cid_opt: session.session_cid.get(), }, ))?; } @@ -159,7 +168,7 @@ pub fn process_primary_packet( // Finally, alert the adjacent endpoint by crafting an error packet let error_packet = packet_crafter::file::craft_file_error_packet( - &hyper_ratchet, + &stacked_ratchet, ticket, security_level, v_conn, @@ -173,7 +182,7 @@ pub fn process_primary_packet( } _ => { - match validation::aead::validate_custom(&hyper_ratchet, &*header, payload) { + match validation::aead::validate_custom(&stacked_ratchet, &*header, payload) { Some((header, mut payload)) => { state_container.meta_expiry_state.on_event_confirmation(); match cmd_aux { @@ -182,14 +191,14 @@ pub fn process_primary_packet( let is_message = header.algorithm == 1; if is_message { let (plaintext, transfer, object_id) = return_if_none!( - validation::group::validate_message(&mut payload), + validation::group::validate_message::(&mut payload), "Bad GROUP HEADER packet" ); - log::trace!(target: "citadel", "Recv FastMessage. version {} w/ CID {} (local CID: {})", hyper_ratchet.version(), hyper_ratchet.get_cid(), header.session_cid.get()); + log::trace!(target: "citadel", "Recv FastMessage. version {} w/ CID {} (local CID: {})", stacked_ratchet.version(), stacked_ratchet.get_cid(), header.session_cid.get()); // Here, we do not go through all the fiasco like above. We just forward the message to the kernel, then send an ACK // so that the sending side can be notified of a successful send let resp_target_cid = get_resp_target_cid_from_header(&header); - log::trace!(target: "citadel", "Resp target cid {} obtained. version {} w/ CID {} (local CID: {})", resp_target_cid, hyper_ratchet.version(), hyper_ratchet.get_cid(), header.session_cid.get()); + log::trace!(target: "citadel", "Resp target cid {} obtained. version {} w/ CID {} (local CID: {})", resp_target_cid, stacked_ratchet.version(), stacked_ratchet.get_cid(), header.session_cid.get()); let ticket = header.context_info.get().into(); // we call this to ensure a flood of these packets doesn't cause ordinary groups from being dropped @@ -199,18 +208,18 @@ pub fn process_primary_packet( session, resp_target_cid, &header, - transfer.map(AliceToBobTransferType::Default), + transfer, &mut state_container, - &hyper_ratchet + &stacked_ratchet ), "Unable to attempt_kem_as_bob [PGP]" ); let target_cid = - if let Some((original_implicated_cid, _original_target_cid)) = + if let Some((original_session_cid, _original_target_cid)) = proxy_cid_info { - original_implicated_cid + original_session_cid } else { 0 }; @@ -226,7 +235,7 @@ pub fn process_primary_packet( let group_header_ack = packet_crafter::group::craft_group_header_ack( - &hyper_ratchet, + &stacked_ratchet, header.group.get(), resp_target_cid, object_id, @@ -252,7 +261,7 @@ pub fn process_primary_packet( let object_id = group_receiver_config.object_id; let ticket = header.context_info.get().into(); - //let sess_implicated_cid = session.implicated_cid.load(Ordering::Relaxed)?; + //let sess_session_cid = session.session_cid.load(Ordering::Relaxed)?; //let target_cid_header = header.target_cid.get(); // for HyperLAN conns, this is true @@ -317,7 +326,7 @@ pub fn process_primary_packet( let group_header_ack = packet_crafter::group::craft_group_header_ack( - &hyper_ratchet, + &stacked_ratchet, header.group.get(), resp_target_cid, object_id, @@ -406,14 +415,14 @@ pub fn process_primary_packet( C2S_ENCRYPTION_ONLY }; let truncate_packet = - packet_crafter::do_drill_update::craft_truncate( - &hyper_ratchet, + packet_crafter::do_entropy_bank_update::craft_truncate( + &stacked_ratchet, needs_truncate, target_cid, timestamp, security_level, ); - log::trace!(target: "citadel", "About to send TRUNCATE packet to MAYBE remove v {:?} | HR v {} | HR CID {}", needs_truncate, hyper_ratchet.version(), hyper_ratchet.get_cid()); + log::trace!(target: "citadel", "About to send TRUNCATE packet to MAYBE remove v {:?} | HR v {} | HR CID {}", needs_truncate, stacked_ratchet.version(), stacked_ratchet.get_cid()); session .send_to_primary_stream(None, truncate_packet)?; } @@ -491,7 +500,7 @@ pub fn process_primary_packet( // the window is done. Since this node is the transmitter, we then make a call to begin sending the next wave if !state_container - .on_wave_ack_received(hyper_ratchet.get_cid(), &header) + .on_wave_ack_received(stacked_ratchet.get_cid(), &header) { if udp_mode == UdpMode::Disabled { log::error!(target: "citadel", "There was an error sending the TCP window; Cancelling connection"); @@ -532,38 +541,38 @@ pub fn process_primary_packet( } #[inline] -pub(super) fn get_proper_hyper_ratchet( - header_drill_vers: u32, - state_container: &dyn ExpectedInnerTarget, +pub(super) fn get_orientation_safe_ratchet( + header_entropy_bank_vers: u32, + state_container: &dyn ExpectedInnerTarget>, proxy_cid_info: Option<(u64, u64)>, -) -> Option { - if let Some((original_implicated_cid, _original_target_cid)) = proxy_cid_info { +) -> Option { + if let Some((original_session_cid, _original_target_cid)) = proxy_cid_info { // since this conn was proxied, we need to go into the virtual conn layer to get the peer session crypto. HOWEVER: // In the case that a packet is proxied back to the source, the adjacent endpoint inscribes this node's cid // inside the target_cid (that way the packet routes correctly to this node). However, this is problematic here // since we use the original implicated CID if let Some(vconn) = state_container .active_virtual_connections - .get(&original_implicated_cid) + .get(&original_session_cid) { - //log::trace!(target: "citadel", "[Peer StackedRatchet] v{} from vconn w/ {}", header_drill_vers, original_implicated_cid); + //log::trace!(target: "citadel", "[Peer StackedRatchet] v{} from vconn w/ {}", header_entropy_bank_vers, original_session_cid); vconn - .borrow_endpoint_hyper_ratchet(Some(header_drill_vers)) + .borrow_endpoint_stacked_ratchet(Some(header_entropy_bank_vers)) .cloned() } else { - log::warn!(target: "citadel", "Unable to find vconn for {}. Unable to process primary group packet", original_implicated_cid); + log::warn!(target: "citadel", "Unable to find vconn for {}. Unable to process primary group packet", original_session_cid); None } } else { - // since this was not proxied, use the ordinary pqc and drill - if state_container.state.load(Ordering::Relaxed) != SessionState::Connected { + // since this was not proxied, use the ordinary pqc and entropy_bank + if !state_container.state.is_connected() { state_container.pre_connect_state.generated_ratchet.clone() } else { state_container .c2s_channel_container .as_ref()? .peer_session_crypto - .get_hyper_ratchet(Some(header_drill_vers)) + .get_ratchet(Some(header_entropy_bank_vers)) .cloned() } } @@ -573,18 +582,18 @@ pub(super) fn get_proper_hyper_ratchet( pub fn get_resp_target_cid(virtual_target: &VirtualConnectionType) -> Option { match virtual_target { VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: _target_cid, } => { // by logic of the network, target_cid must equal this node's CID // since we have entered this process function - //debug_assert_eq!(sess_implicated_cid, target_cid); + //debug_assert_eq!(sess_session_cid, target_cid); //debug_assert_eq!(target_cid_header, target_cid); - Some(*implicated_cid) + Some(*session_cid) } VirtualConnectionType::LocalGroupServer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, } => { // Since this is the receiving node, and we are already in a valid connection, return true Some(0) // ZERO, since we don't use ordinary p2p encryption @@ -606,136 +615,61 @@ pub fn get_resp_target_cid_from_header(header: &HdpHeader) -> u64 { } #[allow(unused)] -pub enum ToolsetUpdate<'a> { - E2E { - crypt: &'a mut PeerSessionCrypto, - local_cid: u64, - }, - Fcm { - fcm_crypt_container: &'a mut PeerSessionCrypto, - peer_cid: u64, - local_cid: u64, - }, +pub struct ToolsetUpdate<'a, R: Ratchet> { + pub(crate) crypt: &'a mut PeerSessionCrypto, + pub(crate) local_cid: u64, } -impl ToolsetUpdate<'_> { +impl ToolsetUpdate<'_, R> { pub(crate) fn update( &mut self, - constructor: ConstructorType, + constructor: R::Constructor, local_is_alice: bool, - ) -> Result { - match self { - ToolsetUpdate::E2E { crypt, local_cid } => { - let constructor = constructor.assume_default().ok_or_else(|| { - CryptError::DrillUpdateError("Constructor is not default type".to_string()) - })?; - crypt.update_sync_safe(constructor, local_is_alice, *local_cid) - } - - ToolsetUpdate::Fcm { - fcm_crypt_container, - local_cid, - .. - } => { - let constructor = constructor.assume_fcm().ok_or_else(|| { - CryptError::DrillUpdateError("Constructor is not FCM type".to_string()) - })?; - fcm_crypt_container.update_sync_safe(constructor, local_is_alice, *local_cid) - } - } + ) -> Result, CryptError> { + self.crypt + .update_sync_safe(constructor, local_is_alice, self.local_cid) } /// This should only be called after an update pub(crate) fn post_stage1_alice_or_bob(&mut self) { - match self { - ToolsetUpdate::E2E { crypt, .. } => { - crypt.post_alice_stage1_or_post_stage1_bob(); - } - - ToolsetUpdate::Fcm { - fcm_crypt_container, - .. - } => { - fcm_crypt_container.post_alice_stage1_or_post_stage1_bob(); - } - } + self.crypt.post_alice_stage1_or_post_stage1_bob() } pub(crate) fn deregister(&mut self, version: u32) -> Result<(), NetworkError> { - match self { - ToolsetUpdate::E2E { crypt, .. } => crypt - .deregister_oldest_hyper_ratchet(version) - .map_err(|err| NetworkError::Generic(err.to_string())), - - ToolsetUpdate::Fcm { - fcm_crypt_container, - .. - } => fcm_crypt_container - .deregister_oldest_hyper_ratchet(version) - .map_err(|err| NetworkError::Generic(err.to_string())), - } + self.crypt + .deregister_oldest_stacked_ratchet(version) + .map_err(|err| NetworkError::Generic(err.to_string())) } /// Unlocks the internal state, allowing future upgrades to the system. Returns the latest hyper ratchet - pub(crate) fn unlock( - &mut self, - requires_locked_by_alice: bool, - ) -> Option<(RatchetType, Option)> { - match self { - ToolsetUpdate::E2E { crypt, .. } => { - let lock_src = crypt.lock_set_by_alice; - crypt - .maybe_unlock(requires_locked_by_alice) - .map(|r| (RatchetType::Default(r.clone()), lock_src)) - } - - ToolsetUpdate::Fcm { - fcm_crypt_container, - .. - } => { - let lock_src = fcm_crypt_container.lock_set_by_alice; - fcm_crypt_container - .maybe_unlock(requires_locked_by_alice) - .map(|r| (RatchetType::Fcm(r.clone()), lock_src)) - } - } + pub(crate) fn unlock(&mut self, requires_locked_by_alice: bool) -> Option<(R, Option)> { + let lock_src = self.crypt.lock_set_by_alice; + self.crypt + .maybe_unlock(requires_locked_by_alice) + .map(|r| (r.clone(), lock_src)) } pub(crate) fn get_local_cid(&self) -> u64 { - match self { - ToolsetUpdate::E2E { local_cid, .. } => *local_cid, - ToolsetUpdate::Fcm { local_cid, .. } => *local_cid, - } + self.local_cid } - pub(crate) fn get_latest_ratchet(&self) -> Option> { - match self { - ToolsetUpdate::E2E { crypt, .. } => crypt - .get_hyper_ratchet(None) - .map(|r| RatchetType::Default(r.clone())), - - ToolsetUpdate::Fcm { - fcm_crypt_container, - .. - } => fcm_crypt_container - .get_hyper_ratchet(None) - .map(|r| RatchetType::Fcm(r.clone())), - } + pub(crate) fn get_latest_ratchet(&self) -> Option<&R> { + self.crypt.get_ratchet(None) } } /// peer_cid: from header.session_cid /// target_cid: from header.target_cid -/// Returns: Ok(latest_hyper_ratchet) -pub(crate) fn attempt_kem_as_alice_finish( - session: &CitadelSession, +/// Returns: Ok(latest_stacked_ratchet) +pub(crate) fn attempt_kem_as_alice_finish( + session: &CitadelSession, base_session_secrecy_mode: SecrecyMode, peer_cid: u64, target_cid: u64, - transfer: KemTransferStatus, - state_container: &mut StateContainerInner, - constructor: Option>, -) -> Result>, ()> { + transfer: KemTransferStatus, + state_container: &mut StateContainerInner, + constructor: Option, +) -> Result, ()> { let (mut toolset_update_method, secrecy_mode, session_password) = if target_cid != C2S_ENCRYPTION_ONLY { @@ -753,7 +687,7 @@ pub(crate) fn attempt_kem_as_alice_finish( return Err(()); } ( - ToolsetUpdate::E2E { + ToolsetUpdate { crypt, local_cid: target_cid, }, @@ -767,7 +701,7 @@ pub(crate) fn attempt_kem_as_alice_finish( .unwrap() .peer_session_crypto; ( - ToolsetUpdate::E2E { + ToolsetUpdate { crypt, local_cid: peer_cid, }, @@ -776,7 +710,6 @@ pub(crate) fn attempt_kem_as_alice_finish( ) }; - //let transfer_ocurred = transfer.has_some(); let requires_truncation = transfer.requires_truncation(); match transfer { @@ -784,7 +717,7 @@ pub(crate) fn attempt_kem_as_alice_finish( if let Some(mut constructor) = constructor { if let Err(err) = constructor.stage1_alice(transfer, session_password.as_ref()) { log::error!(target: "citadel", "Unable to construct hyper ratchet {:?}", err); - return Err(()); // return true, otherwise, the session ends + return Err(()); } if let Err(err) = toolset_update_method.update(constructor, true) { @@ -807,14 +740,16 @@ pub(crate) fn attempt_kem_as_alice_finish( SecrecyMode::Perfect | SecrecyMode::BestEffort => { if requires_truncation.is_some() { // we unlock once we get the truncate ack - Ok(Some(toolset_update_method.get_latest_ratchet().ok_or(())?)) + Ok(Some( + toolset_update_method + .get_latest_ratchet() + .cloned() + .ok_or(())?, + )) } else { Ok(Some(toolset_update_method.unlock(true).ok_or(())?.0)) } - } /*SecrecyMode::BestEffort => { - // since we don't unlock on header_acks, we have to unconditionally unlock here - Ok(Some(toolset_update_method.unlock(false).ok_or(())?.0)) - }*/ + } } } else { log::error!(target: "citadel", "No constructor, yet, KemTransferStatus is Some??"); @@ -822,9 +757,8 @@ pub(crate) fn attempt_kem_as_alice_finish( } } - KemTransferStatus::Omitted => match secrecy_mode { + KemTransferStatus::Contended => match secrecy_mode { SecrecyMode::Perfect => Ok(Some(toolset_update_method.unlock(true).ok_or(())?.0)), - SecrecyMode::BestEffort => Ok(Some(toolset_update_method.unlock(true).ok_or(())?.0)), }, @@ -838,14 +772,14 @@ pub(crate) fn attempt_kem_as_alice_finish( } /// NOTE! Assumes the `hr` passed is the latest version IF the transfer is some -pub(crate) fn attempt_kem_as_bob( - session: &CitadelSession, +pub(crate) fn attempt_kem_as_bob( + session: &CitadelSession, resp_target_cid: u64, header: &Ref<&[u8], HdpHeader>, - transfer: Option, - state_container: &mut StateContainerInner, - hr: &StackedRatchet, -) -> Option { + transfer: Option<>::AliceToBobWireTransfer>, + state_container: &mut StateContainerInner, + hr: &R, +) -> Option> { if let Some(transfer) = transfer { let (update, session_password) = if resp_target_cid != C2S_ENCRYPTION_ONLY { let session_password = state_container @@ -863,7 +797,7 @@ pub(crate) fn attempt_kem_as_bob( .as_mut()? .endpoint_crypto; ( - ToolsetUpdate::E2E { + ToolsetUpdate { crypt, local_cid: header.target_cid.get(), }, @@ -876,7 +810,7 @@ pub(crate) fn attempt_kem_as_bob( .unwrap() .peer_session_crypto; ( - ToolsetUpdate::E2E { + ToolsetUpdate { crypt, local_cid: header.session_cid.get(), }, @@ -890,56 +824,30 @@ pub(crate) fn attempt_kem_as_bob( } } -pub(crate) fn update_toolset_as_bob( - mut update_method: ToolsetUpdate<'_>, - transfer: AliceToBobTransferType, - hr: &StackedRatchet, +pub(crate) fn update_toolset_as_bob( + mut update_method: ToolsetUpdate<'_, R>, + transfer: >::AliceToBobWireTransfer, + hr: &R, session_password: PreSharedKey, -) -> Option { +) -> Option> { let cid = update_method.get_local_cid(); - let new_version = transfer.get_declared_new_version(); - //let (crypto_params, session_security_level) = transfer.get_security_opts(); - //let opts = ConstructorOpts::new_vec_init(Some(crypto_params), (session_security_level.value() + 1) as usize); let opts = hr.get_next_constructor_opts(); - if matches!(transfer, AliceToBobTransferType::Fcm(..)) { - let constructor = EndpointRatchetConstructor::::new_bob( - cid, - new_version, - opts, - transfer, - session_password.as_ref(), - )?; - Some( - update_method - .update(ConstructorType::Fcm(constructor), false) - .ok()?, - ) - } else { - let constructor = EndpointRatchetConstructor::::new_bob( - cid, - new_version, - opts, - transfer, - session_password.as_ref(), - )?; - Some( - update_method - .update(ConstructorType::Default(constructor), false) - .ok()?, - ) - } + + let constructor = + EndpointRatchetConstructor::::new_bob(cid, opts, transfer, session_password.as_ref())?; + update_method.update(constructor, false).ok() } /// Returns the virtual connection type for the response target cid. Is relative to the current node, not the receiving node pub fn get_v_conn_from_header(header: &HdpHeader) -> VirtualConnectionType { let target_cid = header.session_cid.get(); - let implicated_cid = header.target_cid.get(); + let session_cid = header.target_cid.get(); if target_cid != C2S_ENCRYPTION_ONLY { VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } } else { - VirtualConnectionType::LocalGroupServer { implicated_cid } + VirtualConnectionType::LocalGroupServer { session_cid } } } diff --git a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs index 0e4afc45f..9da639679 100644 --- a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs +++ b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs @@ -37,9 +37,9 @@ //! - [`file_packet`]: File transfer packets //! - [`session_manager`]: Session management -use bytes::BytesMut; - use crate::proto::packet_processor::peer::peer_cmd_packet; +use bytes::BytesMut; +use citadel_crypt::stacked_ratchet::Ratchet; use super::includes::*; use crate::error::NetworkError; @@ -51,11 +51,11 @@ use crate::error::NetworkError; skip_all, ret, err, - fields(implicated_cid=this_implicated_cid, is_server=session.is_server, packet_len=packet.len()) + fields(session_cid=this_session_cid, is_server=session.is_server, packet_len=packet.len()) ))] -pub async fn process_raw_packet( - this_implicated_cid: Option, - session: &CitadelSession, +pub async fn process_raw_packet( + this_session_cid: Option, + session: &CitadelSession, remote_peer: SocketAddr, local_primary_port: u16, packet: BytesMut, @@ -70,10 +70,10 @@ pub async fn process_raw_packet( // if proxying/p2p is involved, then the target_cid != 0 let cmd_primary = header.cmd_primary; let cmd_aux = header.cmd_aux; - let header_drill_vers = header.drill_version.get(); + let header_entropy_bank_vers = header.entropy_bank_version.get(); match check_proxy( - this_implicated_cid, + this_session_cid, header.cmd_primary, header.cmd_aux, header.session_cid.get(), @@ -89,12 +89,17 @@ pub async fn process_raw_packet( } packet_flags::cmd::primary::DO_CONNECT => { - super::connect_packet::process_connect(session, packet, header_drill_vers).await + super::connect_packet::process_connect(session, packet, header_entropy_bank_vers) + .await } packet_flags::cmd::primary::KEEP_ALIVE => { - super::keep_alive_packet::process_keep_alive(session, packet, header_drill_vers) - .await + super::keep_alive_packet::process_keep_alive( + session, + packet, + header_entropy_bank_vers, + ) + .await } packet_flags::cmd::primary::GROUP_PACKET => { @@ -107,25 +112,37 @@ pub async fn process_raw_packet( } packet_flags::cmd::primary::DO_DISCONNECT => { - super::disconnect_packet::process_disconnect(session, packet, header_drill_vers) - .await + super::disconnect_packet::process_disconnect( + session, + packet, + header_entropy_bank_vers, + ) + .await } packet_flags::cmd::primary::DO_DRILL_UPDATE => super::rekey_packet::process_rekey( session, packet, - header_drill_vers, + header_entropy_bank_vers, endpoint_cid_info, ), packet_flags::cmd::primary::DO_DEREGISTER => { - super::deregister_packet::process_deregister(session, packet, header_drill_vers) - .await + super::deregister_packet::process_deregister( + session, + packet, + header_entropy_bank_vers, + ) + .await } packet_flags::cmd::primary::DO_PRE_CONNECT => { - super::preconnect_packet::process_preconnect(session, packet, header_drill_vers) - .await + super::preconnect_packet::process_preconnect( + session, + packet, + header_entropy_bank_vers, + ) + .await } packet_flags::cmd::primary::PEER_CMD => { @@ -133,7 +150,7 @@ pub async fn process_raw_packet( session, cmd_aux, packet, - header_drill_vers, + header_entropy_bank_vers, endpoint_cid_info, ) .await @@ -146,7 +163,7 @@ pub async fn process_raw_packet( packet_flags::cmd::primary::HOLE_PUNCH => super::hole_punch::process_hole_punch( session, packet, - header_drill_vers, + header_entropy_bank_vers, endpoint_cid_info, ), @@ -173,7 +190,7 @@ pub(crate) enum ReceivePortType { /// /// # Parameters /// -/// - `this_implicated_cid`: The implicated CID of the current session. +/// - `this_session_cid`: The implicated CID of the current session. /// - `cmd_primary`: The primary command of the packet. /// - `cmd_aux`: The auxiliary command of the packet. /// - `header_session_cid`: The session CID of the packet. @@ -189,29 +206,29 @@ pub(crate) enum ReceivePortType { /// - `None`: If the packet should be proxied. #[allow(clippy::too_many_arguments)] #[inline] -pub(crate) fn check_proxy( - this_implicated_cid: Option, +pub(crate) fn check_proxy( + this_session_cid: Option, cmd_primary: u8, cmd_aux: u8, header_session_cid: u64, target_cid: u64, - session: &CitadelSession, + session: &CitadelSession, endpoint_cid_info: &mut Option<(u64, u64)>, recv_port_type: ReceivePortType, packet: HdpPacket, ) -> Option { if target_cid != 0 { // since target cid is not zero, there are two possibilities: - // either [A] we are at the hLAN server, in which case the this_implicated_cid != target_cid - // or, [B] we are at the destination, in which case implicated_cid == target_cid. If this is true, do normal processing + // either [A] we are at the hLAN server, in which case the this_session_cid != target_cid + // or, [B] we are at the destination, in which case session_cid == target_cid. If this is true, do normal processing // NOTE: When proxying DO NOT change the original implicated_CID in the header. // [*] in the case of proxying, it should only be done after a connection is well established // This would imply that the implicated cid is established in the HdpSession. As such, if the implicated CID is None, // then simply let normal logic below continue - if let Some(this_implicated_cid) = this_implicated_cid { + if let Some(this_session_cid) = this_session_cid { // this implies there is at least a connection between hLAN client and hLAN server, but we don't know which is which - if this_implicated_cid != target_cid { - log::trace!(target: "citadel", "Proxying {}:{} packet from {} to {}", cmd_primary, cmd_aux, this_implicated_cid, target_cid); + if this_session_cid != target_cid { + log::trace!(target: "citadel", "Proxying {}:{} packet from {} to {}", cmd_primary, cmd_aux, this_session_cid, target_cid); // Proxy will only occur if there exists a virtual connection, in which case, we get the TcpSender (since these are primary packets) let mut state_container = inner_mut_state!(session.state_container); @@ -268,7 +285,7 @@ pub(crate) fn check_proxy( return None; } else { - // since implicated_cid == target_cid, and target_cid != 0, we are at the destination + // since session_cid == target_cid, and target_cid != 0, we are at the destination // and need to use the endpoint crypto in order to process the packets *endpoint_cid_info = Some((header_session_cid, target_cid)) } diff --git a/citadel_proto/src/proto/packet_processor/register_packet.rs b/citadel_proto/src/proto/packet_processor/register_packet.rs index 2099ff612..31dd1b5ba 100644 --- a/citadel_proto/src/proto/packet_processor/register_packet.rs +++ b/citadel_proto/src/proto/packet_processor/register_packet.rs @@ -55,21 +55,30 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{RegisterFailure, RegisterOkay}; -use citadel_crypt::prelude::ConstructorOpts; -use citadel_crypt::stacked_ratchet::constructor::{ - BobToAliceTransfer, BobToAliceTransferType, StackedRatchetConstructor, +use citadel_crypt::endpoint_crypto_container::{ + AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, }; -use std::sync::atomic::Ordering; +use citadel_crypt::prelude::ConstructorOpts; +use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_user::serialization::SyncIO; /// This will handle a registration packet -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub async fn process_register( - session_ref: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session_ref.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub async fn process_register( + session_ref: &CitadelSession, packet: HdpPacket, remote_addr: SocketAddr, ) -> Result { let session = session_ref.clone(); - let state = session.state.load(Ordering::Relaxed); + let state = session.state.get(); if state != SessionState::NeedsRegister && state != SessionState::SocketJustOpened @@ -97,7 +106,7 @@ pub async fn process_register( { let algorithm = header.algorithm; - match validation::do_register::validate_stage0(&payload) { + match validation::do_register::validate_stage0::(&payload) { Some((transfer, passwordless)) => { // Now, create a stage 1 packet let timestamp = session.time_tracker.get_global_time_ns(); @@ -119,32 +128,33 @@ pub async fn process_register( async move { let cid = header.session_cid.get(); - let bob_constructor = StackedRatchetConstructor::new_bob( - cid, - 0, - ConstructorOpts::new_vec_init( - Some(transfer.params), - (transfer.security_level.value() + 1) as usize, - ), - transfer, - session_password.as_ref(), - ) - .ok_or(NetworkError::InvalidRequest("Bad bob transfer"))?; + let mut bob_constructor = + >::new_bob( + cid, + ConstructorOpts::new_vec_init( + Some(transfer.crypto_params()), + transfer.security_level(), + ), + transfer, + session_password.as_ref(), + ) + .ok_or(NetworkError::InvalidRequest("Bad bob transfer"))?; let transfer = return_if_none!( bob_constructor.stage0_bob(), "Unable to advance past stage0-bob" ); - let stage1_packet = packet_crafter::do_register::craft_stage1( - algorithm, - timestamp, - transfer, - header.session_cid.get(), - ); + let stage1_packet = + packet_crafter::do_register::craft_stage1::( + algorithm, + timestamp, + transfer, + header.session_cid.get(), + ); let mut state_container = inner_mut_state!(session.state_container); - state_container.register_state.created_hyper_ratchet = + state_container.register_state.created_stacked_ratchet = Some(return_if_none!( bob_constructor.finish(), "Unable to finish bob constructor" @@ -163,9 +173,7 @@ pub async fn process_register( state_container.register_state.on_register_packet_received(); std::mem::drop(state_container); - session - .state - .store(SessionState::NeedsRegister, Ordering::Relaxed); + session.state.set(SessionState::NeedsRegister); return Ok(PrimaryProcessorResult::EndSession( "Unable to validate STAGE0_REGISTER packet", @@ -191,22 +199,18 @@ pub async fn process_register( let algorithm = header.algorithm; // pqc is stored in the register state container for now - //debug_assert!(session.post_quantum.is_none()); if let Some(mut alice_constructor) = state_container.register_state.constructor.take() { - let transfer = return_if_none!( - BobToAliceTransfer::deserialize_from(&payload[..]), + let transfer: >::BobToAliceWireTransfer = return_if_none!( + SyncIO::deserialize_from_vector(&payload[..]).ok(), "Unable to deserialize BobToAliceTransfer" ); - let security_level = transfer.security_level; + let security_level = transfer.security_level(); alice_constructor - .stage1_alice( - BobToAliceTransferType::Default(transfer), - session.session_password.as_ref(), - ) + .stage1_alice(transfer, session.session_password.as_ref()) .map_err(|err| NetworkError::Generic(err.to_string()))?; - let new_hyper_ratchet = return_if_none!( + let new_stacked_ratchet = return_if_none!( alice_constructor.finish(), "Unable to finish alice constructor" ); @@ -218,7 +222,7 @@ pub async fn process_register( ); let stage2_packet = packet_crafter::do_register::craft_stage2( - &new_hyper_ratchet, + &new_stacked_ratchet, algorithm, timestamp, proposed_credentials, @@ -226,8 +230,8 @@ pub async fn process_register( ); //let mut state_container = inner_mut!(session.state_container); - state_container.register_state.created_hyper_ratchet = - Some(new_hyper_ratchet); + state_container.register_state.created_stacked_ratchet = + Some(new_stacked_ratchet); state_container.register_state.last_stage = packet_flags::cmd::aux::do_register::STAGE2; state_container.register_state.on_register_packet_received(); @@ -253,13 +257,16 @@ pub async fn process_register( == packet_flags::cmd::aux::do_register::STAGE1 { let algorithm = header.algorithm; - let hyper_ratchet = return_if_none!( - state_container.register_state.created_hyper_ratchet.clone(), + let stacked_ratchet = return_if_none!( + state_container + .register_state + .created_stacked_ratchet + .clone(), "Unable to load created hyper ratchet" ); if let Some((stage2_packet, conn_info)) = validation::do_register::validate_stage2( - &hyper_ratchet, + &stacked_ratchet, &header, payload, remote_addr, @@ -276,7 +283,7 @@ pub async fn process_register( .register_impersonal_hyperlan_client_network_account( conn_info, creds, - hyper_ratchet.clone(), + stacked_ratchet.clone(), ) .await { @@ -286,7 +293,7 @@ pub async fn process_register( session.create_register_success_message(); let packet = packet_crafter::do_register::craft_success( - &hyper_ratchet, + &stacked_ratchet, algorithm, timestamp, success_message, @@ -339,14 +346,17 @@ pub async fn process_register( if state_container.register_state.last_stage == packet_flags::cmd::aux::do_register::STAGE2 { - let hyper_ratchet = return_if_none!( - state_container.register_state.created_hyper_ratchet.clone(), + let stacked_ratchet = return_if_none!( + state_container + .register_state + .created_stacked_ratchet + .clone(), "Unable to load created hyper ratchet" ); if let Some((success_message, conn_info)) = validation::do_register::validate_success( - &hyper_ratchet, + &stacked_ratchet, &header, payload, remote_addr, @@ -372,7 +382,7 @@ pub async fn process_register( async move { match account_manager .register_personal_hyperlan_server( - hyper_ratchet, + stacked_ratchet, credentials, conn_info, ) @@ -394,7 +404,7 @@ pub async fn process_register( kernel_tx.unbounded_send(NodeResult::RegisterOkay( RegisterOkay { ticket: reg_ticket.get(), - cnac: new_cnac, + cid: new_cnac.get_cid(), welcome_message: success_message, }, ))?; diff --git a/citadel_proto/src/proto/packet_processor/rekey_packet.rs b/citadel_proto/src/proto/packet_processor/rekey_packet.rs index ac807958e..93c52cae5 100644 --- a/citadel_proto/src/proto/packet_processor/rekey_packet.rs +++ b/citadel_proto/src/proto/packet_processor/rekey_packet.rs @@ -36,9 +36,9 @@ //! use citadel_proto::proto::packet::HdpPacket; //! //! fn handle_rekey(session: &CitadelSession, packet: HdpPacket) { -//! let header_drill_vers = 1; +//! let header_entropy_bank_vers = 1; //! let proxy_info = None; -//! match rekey_packet::process_rekey(session, packet, header_drill_vers, proxy_info) { +//! match rekey_packet::process_rekey(session, packet, header_entropy_bank_vers, proxy_info) { //! Ok(result) => { //! // Handle successful rekey //! } @@ -55,24 +55,30 @@ use crate::prelude::ReKeyReturnType; use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; use crate::proto::packet_processor::header_to_response_vconn_type; use crate::proto::packet_processor::primary_group_packet::{ - attempt_kem_as_alice_finish, attempt_kem_as_bob, get_proper_hyper_ratchet, + attempt_kem_as_alice_finish, attempt_kem_as_bob, get_orientation_safe_ratchet, get_resp_target_cid_from_header, ToolsetUpdate, }; -use citadel_crypt::stacked_ratchet::constructor::{AliceToBobTransferType, ConstructorType}; -use citadel_crypt::stacked_ratchet::RatchetType; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecrecyMode; use std::ops::Deref; -use std::sync::atomic::Ordering; -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub fn process_rekey( - session: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub fn process_rekey( + session: &CitadelSession, packet: HdpPacket, - header_drill_vers: u32, + header_entropy_bank_vers: u32, proxy_cid_info: Option<(u64, u64)>, ) -> Result { - if session.state.load(Ordering::Relaxed) != SessionState::Connected { - log::error!(target: "citadel", "Session state is not connected; dropping drill update packet"); + if !session.state.is_connected() { + log::error!(target: "citadel", "Session state is not connected; dropping entropy_bank update packet"); return Ok(PrimaryProcessorResult::Void); } @@ -85,12 +91,12 @@ pub fn process_rekey( let (header, payload, _, _) = packet.decompose(); let mut state_container = inner_mut_state!(state_container); - let hyper_ratchet = return_if_none!( - get_proper_hyper_ratchet(header_drill_vers, &state_container, proxy_cid_info), + let stacked_ratchet = return_if_none!( + get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, proxy_cid_info), "Unable to get proper HR" ); let (header, payload) = return_if_none!( - validation::aead::validate_custom(&hyper_ratchet, &header, payload), + validation::aead::validate_custom(&stacked_ratchet, &header, payload), "Unable to validate packet" ); let payload = &payload[..]; @@ -100,9 +106,9 @@ pub fn process_rekey( match header.cmd_aux { // Bob - packet_flags::cmd::aux::do_drill_update::STAGE0 => { - log::trace!(target: "citadel", "DO_DRILL_UPDATE STAGE 0 PACKET RECV"); - match validation::do_drill_update::validate_stage0(payload) { + packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE0 => { + log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE STAGE 0 PACKET RECV"); + match validation::do_stacked_ratchet_update::validate_stage0::(payload) { Some(transfer) => { let resp_target_cid = get_resp_target_cid_from_header(&header); let status = return_if_none!( @@ -110,14 +116,14 @@ pub fn process_rekey( session, resp_target_cid, &header, - Some(AliceToBobTransferType::Default(transfer)), + Some(transfer), &mut state_container, - &hyper_ratchet + &stacked_ratchet ), "Unable to attempt KEM as Bob" ); - let packet = packet_crafter::do_drill_update::craft_stage1( - &hyper_ratchet, + let packet = packet_crafter::do_entropy_bank_update::craft_stage1( + &stacked_ratchet, status, timestamp, resp_target_cid, @@ -127,16 +133,16 @@ pub fn process_rekey( } _ => { - log::error!(target: "citadel", "Invalid stage0 DO_DRILL_UPDATE packet"); + log::error!(target: "citadel", "Invalid stage0 DO_STACKED_RATCHET_UPDATE packet"); Ok(PrimaryProcessorResult::Void) } } } // Alice - packet_flags::cmd::aux::do_drill_update::STAGE1 => { - log::trace!(target: "citadel", "DO_DRILL_UPDATE STAGE 1 PACKET RECV"); - match validation::do_drill_update::validate_stage1(payload) { + packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE1 => { + log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE STAGE 1 PACKET RECV"); + match validation::do_stacked_ratchet_update::validate_stage1(payload) { Some(transfer) => { //let mut state_container = inner_mut!(session.state_container); let peer_cid = header.session_cid.get(); @@ -151,7 +157,7 @@ pub fn process_rekey( } else { return_if_none!(state_container .ratchet_update_state - .alice_hyper_ratchet + .alice_stacked_ratchet .take()) }; log::trace!(target: "citadel", "Obtained constructor for {}", resp_target_cid); @@ -162,7 +168,7 @@ pub fn process_rekey( let needs_early_kernel_alert = transfer.update_status.omitted(); - let latest_hr = return_if_none!(return_if_none!( + let latest_hr = return_if_none!( attempt_kem_as_alice_finish( session, secrecy_mode, @@ -170,14 +176,14 @@ pub fn process_rekey( target_cid, transfer.update_status, &mut state_container, - Some(ConstructorType::Default(constructor)) + Some(constructor) ) .ok(), "Unable to attempt KEM as alice finish" ) - .unwrap_or(RatchetType::Default(hyper_ratchet)) - .assume_default()); - let truncate_packet = packet_crafter::do_drill_update::craft_truncate( + .unwrap_or(stacked_ratchet); + + let truncate_packet = packet_crafter::do_entropy_bank_update::craft_truncate( &latest_hr, needs_truncate, resp_target_cid, @@ -200,17 +206,17 @@ pub fn process_rekey( } _ => { - log::error!(target: "citadel", "Invalid stage1 DO_DRILL_UPDATE packet"); + log::error!(target: "citadel", "Invalid stage1 DO_STACKED_RATCHET_UPDATE packet"); Ok(PrimaryProcessorResult::Void) } } } - // Bob will always receive this, whether the toolset being upgraded or not. This allows Bob to begin using the latest drill version - packet_flags::cmd::aux::do_drill_update::TRUNCATE => { - log::trace!(target: "citadel", "DO_DRILL_UPDATE TRUNCATE PACKET RECV"); + // Bob will always receive this, whether the toolset being upgraded or not. This allows Bob to begin using the latest entropy_bank version + packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE => { + log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE TRUNCATE PACKET RECV"); let truncate_packet = return_if_none!( - validation::do_drill_update::validate_truncate(payload), + validation::do_stacked_ratchet_update::validate_truncate(payload), "Invalid truncate" ); let resp_target_cid = get_resp_target_cid_from_header(&header); @@ -224,7 +230,7 @@ pub fn process_rekey( let crypt = &mut endpoint_container.endpoint_crypto; let local_cid = header.target_cid.get(); ( - ToolsetUpdate::E2E { crypt, local_cid }, + ToolsetUpdate { crypt, local_cid }, endpoint_container.default_security_settings.secrecy_mode, ) } else { @@ -239,7 +245,7 @@ pub fn process_rekey( .unwrap() .peer_session_crypto; let local_cid = header.session_cid.get(); - (ToolsetUpdate::E2E { crypt, local_cid }, secrecy_mode) + (ToolsetUpdate { crypt, local_cid }, secrecy_mode) }; // We optionally deregister at this endpoint to prevent any further packets with this version from being sent @@ -265,8 +271,8 @@ pub fn process_rekey( // If we didn't have to deregister, then our job is done. alice does not need to hear from Bob // But, if deregistration occurred, we need to alert alice that way she can unlock hers if let Some(truncate_vers) = truncate_packet.truncate_version { - let truncate_ack = packet_crafter::do_drill_update::craft_truncate_ack( - &hyper_ratchet, + let truncate_ack = packet_crafter::do_entropy_bank_update::craft_truncate_ack( + &stacked_ratchet, truncate_vers, resp_target_cid, timestamp, @@ -284,10 +290,10 @@ pub fn process_rekey( Ok(PrimaryProcessorResult::Void) } - packet_flags::cmd::aux::do_drill_update::TRUNCATE_ACK => { - log::trace!(target: "citadel", "DO_DRILL_UPDATE TRUNCATE_ACK PACKET RECV"); + packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE_ACK => { + log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE TRUNCATE_ACK PACKET RECV"); let truncate_ack_packet = return_if_none!( - validation::do_drill_update::validate_truncate_ack(payload), + validation::do_stacked_ratchet_update::validate_truncate_ack(payload), "Unable to validate truncate ack" ); log::trace!(target: "citadel", "Adjacent node has finished deregistering version {}", truncate_ack_packet.truncated_version); @@ -303,7 +309,7 @@ pub fn process_rekey( let crypt = &mut endpoint_container.endpoint_crypto; let local_cid = header.target_cid.get(); ( - ToolsetUpdate::E2E { crypt, local_cid }, + ToolsetUpdate { crypt, local_cid }, endpoint_container.default_security_settings.secrecy_mode, ) } else { @@ -318,7 +324,7 @@ pub fn process_rekey( .unwrap() .peer_session_crypto; let local_cid = header.session_cid.get(); - (ToolsetUpdate::E2E { crypt, local_cid }, secrecy_mode) + (ToolsetUpdate { crypt, local_cid }, secrecy_mode) }; let _ = return_if_none!(method.unlock(true)); // unconditional unlock @@ -333,7 +339,7 @@ pub fn process_rekey( header_to_response_vconn_type(&header), &session.kernel_tx, ReKeyReturnType::Success { - version: hyper_ratchet.version(), + version: stacked_ratchet.version(), }, )?; @@ -341,7 +347,7 @@ pub fn process_rekey( } _ => { - log::error!(target: "citadel", "Invalid auxiliary command for DO_DRILL_UPDATE packet. Dropping"); + log::error!(target: "citadel", "Invalid auxiliary command for DO_STACKED_RATCHET_UPDATE packet. Dropping"); Ok(PrimaryProcessorResult::Void) } } diff --git a/citadel_proto/src/proto/packet_processor/udp_packet.rs b/citadel_proto/src/proto/packet_processor/udp_packet.rs index bac62a381..3f9f031c8 100644 --- a/citadel_proto/src/proto/packet_processor/udp_packet.rs +++ b/citadel_proto/src/proto/packet_processor/udp_packet.rs @@ -31,14 +31,23 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; use crate::proto::packet_processor::primary_group_packet::get_resp_target_cid_from_header; +use citadel_crypt::stacked_ratchet::Ratchet; /// This will handle an inbound group packet -#[cfg_attr(feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err, fields(is_server = _session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get())))] -pub fn process_udp_packet( - _session: &CitadelSession, +#[cfg_attr(feature = "localhost-testing", tracing::instrument( + level = "trace", + target = "citadel", + skip_all, + ret, + err, + fields(is_server = _session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() + ) +))] +pub fn process_udp_packet( + _session: &CitadelSession, packet: HdpPacket, hr_version: u32, - accessor: &EndpointCryptoAccessor, + accessor: &EndpointCryptoAccessor, ) -> Result { let (header, payload, _, _) = packet.decompose(); diff --git a/citadel_proto/src/proto/peer/channel.rs b/citadel_proto/src/proto/peer/channel.rs index 618a4aba5..086d0fd20 100644 --- a/citadel_proto/src/proto/peer/channel.rs +++ b/citadel_proto/src/proto/peer/channel.rs @@ -68,6 +68,7 @@ use crate::proto::peer::peer_layer::{PeerConnectionType, PeerSignal}; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::SessionRequest; use crate::proto::state_container::VirtualConnectionType; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::macros::support::Pin; use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecurityLevel; @@ -87,8 +88,8 @@ pub struct PeerChannel { impl PeerChannel { #[allow(clippy::too_many_arguments)] - pub(crate) fn new( - server_remote: NodeRemote, + pub(crate) fn new( + node_remote: NodeRemote, target_cid: u64, vconn_type: VirtualConnectionType, channel_id: Ticket, @@ -97,14 +98,15 @@ impl PeerChannel { receiver: UnboundedReceiver, to_outbound_stream: Sender, ) -> Self { - let implicated_cid = vconn_type.get_implicated_cid(); + let session_cid = vconn_type.get_session_cid(); let recv_type = ReceivePortType::OrderedReliable; + let server_remote = thin_remote_from_node_remote(node_remote); let send_half = PeerChannelSendHalf { to_outbound_stream, target_cid, vconn_type, - implicated_cid, + session_cid, channel_id, security_level, }; @@ -131,8 +133,8 @@ impl PeerChannel { } /// Gets the CID of the local user - pub fn get_implicated_cid(&self) -> u64 { - self.send_half.vconn_type.get_implicated_cid() + pub fn get_session_cid(&self) -> u64 { + self.send_half.vconn_type.get_session_cid() } /// Gets the metadata of the virtual connection @@ -153,7 +155,7 @@ pub struct PeerChannelSendHalf { to_outbound_stream: Sender, target_cid: u64, #[allow(dead_code)] - implicated_cid: u64, + session_cid: u64, vconn_type: VirtualConnectionType, channel_id: Ticket, security_level: SecurityLevel, @@ -222,10 +224,16 @@ pub struct PeerChannelRecvHalf { #[allow(dead_code)] channel_id: Ticket, is_alive: Arc, - server_remote: NodeRemote, recv_type: ReceivePortType, + server_remote: Box, } +pub(crate) trait ThinRemoteFn: + Fn(NodeRequest) -> Result<(), NetworkError> + Send + Sync + 'static +{ +} +impl Result<(), NetworkError> + Send + Sync + 'static> ThinRemoteFn for T {} + impl Debug for PeerChannelRecvHalf { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "PeerChannel Rx {:?}", self.vconn_type) @@ -255,7 +263,7 @@ impl Stream for PeerChannelRecvHalf { impl Drop for PeerChannelRecvHalf { fn drop(&mut self) { if let VirtualConnectionType::LocalGroupPeer { - implicated_cid: local_cid, + session_cid: local_cid, peer_cid, } = self.vconn_type { @@ -265,10 +273,10 @@ impl Drop for PeerChannelRecvHalf { ReceivePortType::OrderedReliable => { self.is_alive.store(false, Ordering::SeqCst); NodeRequest::PeerCommand(PeerCommand { - implicated_cid: local_cid, + session_cid: local_cid, command: PeerSignal::Disconnect { peer_conn_type: PeerConnectionType::LocalGroupPeer { - implicated_cid: local_cid, + session_cid: local_cid, peer_cid, }, disconnect_response: None, @@ -279,7 +287,7 @@ impl Drop for PeerChannelRecvHalf { ReceivePortType::UnorderedUnreliable => { if let Some(peer_conn_type) = self.vconn_type.try_as_peer_connection() { NodeRequest::PeerCommand(PeerCommand { - implicated_cid: local_cid, + session_cid: local_cid, command: PeerSignal::DisconnectUDP { peer_conn_type }, }) } else { @@ -289,7 +297,7 @@ impl Drop for PeerChannelRecvHalf { } }; - if let Err(err) = self.server_remote.try_send(command) { + if let Err(err) = (self.server_remote)(command) { log::warn!(target: "citadel", "[PeerChannelRecvHalf] unable to send stop signal to session: {:?}", err); } } @@ -302,16 +310,28 @@ pub struct UdpChannel { recv_half: PeerChannelRecvHalf, } +pub(crate) fn thin_remote_from_node_remote( + node_remote: NodeRemote, +) -> Box { + Box::new(move |command| { + node_remote + .try_send(command) + .map_err(|err| NetworkError::Generic(err.to_string())) + }) +} + impl UdpChannel { - pub fn new( + pub fn new( send_half: OutboundUdpSender, receiver: UnboundedReceiver, target_cid: u64, vconn_type: VirtualConnectionType, channel_id: Ticket, is_alive: Arc, - server_remote: NodeRemote, + server_remote: NodeRemote, ) -> Self { + let server_remote = thin_remote_from_node_remote(server_remote); + Self { send_half, recv_half: PeerChannelRecvHalf { diff --git a/citadel_proto/src/proto/peer/group_channel.rs b/citadel_proto/src/proto/peer/group_channel.rs index 4c6871eda..14f1e93f6 100644 --- a/citadel_proto/src/proto/peer/group_channel.rs +++ b/citadel_proto/src/proto/peer/group_channel.rs @@ -24,7 +24,7 @@ let group_channel = GroupChannel::new( tx, key, ticket, - implicated_cid, + session_cid, recv ); @@ -55,7 +55,7 @@ let (send_half, recv_half) = group_channel.split(); use crate::error::NetworkError; use crate::proto::outbound_sender::{Sender, UnboundedReceiver}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; -use crate::proto::remote::{NodeRemote, Ticket}; +use crate::proto::remote::Ticket; use crate::proto::session::SessionRequest; use citadel_io::tokio_stream::StreamExt; use citadel_types::crypto::SecBuffer; @@ -83,27 +83,25 @@ impl Deref for GroupChannel { impl GroupChannel { pub fn new( - node_remote: NodeRemote, tx: Sender, key: MessageGroupKey, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, recv: UnboundedReceiver, ) -> Self { Self { send_half: GroupChannelSendHalf { - node_remote, tx: tx.clone(), ticket, key, - implicated_cid, + session_cid, }, recv_half: GroupChannelRecvHalf { recv, tx, ticket, - implicated_cid, + session_cid, key, }, } @@ -125,7 +123,7 @@ impl GroupChannel { } pub fn cid(&self) -> u64 { - self.recv_half.implicated_cid + self.recv_half.session_cid } } @@ -137,12 +135,10 @@ pub enum GroupBroadcastPayload { #[derive(Clone)] pub struct GroupChannelSendHalf { - #[allow(dead_code)] - node_remote: NodeRemote, tx: Sender, ticket: Ticket, key: MessageGroupKey, - implicated_cid: u64, + session_cid: u64, } impl Debug for GroupChannelSendHalf { @@ -150,7 +146,7 @@ impl Debug for GroupChannelSendHalf { write!( f, "GroupChannelTx {} connected to [{:?}]", - self.implicated_cid, self.key + self.session_cid, self.key ) } } @@ -159,7 +155,7 @@ impl GroupChannelSendHalf { /// Broadcasts a message to the group pub async fn send_message(&self, message: SecBuffer) -> Result<(), NetworkError> { self.send_group_command(GroupBroadcast::Message { - sender: self.implicated_cid, + sender: self.session_cid, key: self.key, message, }) @@ -220,7 +216,7 @@ impl GroupChannelSendHalf { } fn permission_gate(&self) -> Result<(), NetworkError> { - if self.implicated_cid == self.key.cid { + if self.session_cid == self.key.cid { Ok(()) } else { Err(NetworkError::InvalidRequest( @@ -234,7 +230,7 @@ pub struct GroupChannelRecvHalf { recv: UnboundedReceiver, tx: Sender, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, key: MessageGroupKey, } @@ -243,7 +239,7 @@ impl Debug for GroupChannelRecvHalf { write!( f, "GroupChannelRx: {} subscribed to {:?}", - self.implicated_cid, self.key + self.session_cid, self.key ) } } @@ -258,7 +254,7 @@ impl Stream for GroupChannelRecvHalf { impl Drop for GroupChannelRecvHalf { fn drop(&mut self) { - log::trace!(target: "citadel", "Dropping group channel recv half for {:?} | {:?}", self.implicated_cid, self.key); + log::trace!(target: "citadel", "Dropping group channel recv half for {:?} | {:?}", self.session_cid, self.key); let request = SessionRequest::Group { ticket: self.ticket, broadcast: GroupBroadcast::LeaveRoom { key: self.key }, diff --git a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs index 2f837e563..f086ab06e 100644 --- a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs +++ b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs @@ -52,32 +52,32 @@ use crate::proto::peer::p2p_conn_handler::generic_error; use crate::proto::state_container::StateContainerInner; use async_trait::async_trait; use bytes::Bytes; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::sync::Mutex; use citadel_types::crypto::SecurityLevel; use netbeam::reliable_conn::{ConnAddr, ReliableOrderedStreamToTarget}; use std::net::SocketAddr; use std::str::FromStr; -pub(crate) struct ReliableOrderedCompatStream { +pub(crate) struct ReliableOrderedCompatStream { to_primary_stream: OutboundPrimaryStreamSender, from_stream: Mutex>, peer_external_addr: SocketAddr, local_bind_addr: SocketAddr, - hr: StackedRatchet, + hr: R, security_level: SecurityLevel, target_cid: u64, } -impl ReliableOrderedCompatStream { +impl ReliableOrderedCompatStream { /// For C2S, using this is straight forward (set target_cid to 0) /// For P2P, using this is not as straight forward. This will use the central node for routing packets. As such, the target_cid must be set to the peers to enable routing. Additionally, this will need to use the p2p ratchet. This implies that /// BOTH nodes must already have the ratchets loaded pub(crate) fn new( to_primary_stream: OutboundPrimaryStreamSender, - state_container: &mut StateContainerInner, + state_container: &mut StateContainerInner, target_cid: u64, - hr: StackedRatchet, + hr: R, security_level: SecurityLevel, ) -> Self { let (from_stream_tx, from_stream_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); @@ -103,7 +103,7 @@ impl ReliableOrderedCompatStream { } #[async_trait] -impl ReliableOrderedStreamToTarget for ReliableOrderedCompatStream { +impl ReliableOrderedStreamToTarget for ReliableOrderedCompatStream { async fn send_to_peer(&self, input: &[u8]) -> std::io::Result<()> { let packet = crate::proto::packet_crafter::hole_punch::generate_packet( &self.hr, @@ -128,7 +128,7 @@ impl ReliableOrderedStreamToTarget for ReliableOrderedCompatStream { } } -impl ConnAddr for ReliableOrderedCompatStream { +impl ConnAddr for ReliableOrderedCompatStream { fn local_addr(&self) -> std::io::Result { Ok(self.local_bind_addr) } diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index d2932e536..f7cb2dad2 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -34,7 +34,7 @@ attempt_simultaneous_hole_punch( ticket, session, peer_nat_info, - implicated_cid, + session_cid, kernel_tx, channel_signal, sync_time, @@ -79,6 +79,7 @@ use crate::proto::peer::peer_layer::PeerConnectionType; use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; use crate::proto::state_container::VirtualConnectionType; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::prelude::UdpMode; use citadel_user::re_exports::__private::Formatter; use citadel_wire::exports::tokio_rustls::rustls; @@ -130,17 +131,17 @@ impl Drop for DirectP2PRemote { } } -async fn setup_listener_non_initiator( +async fn setup_listener_non_initiator( local_bind_addr: SocketAddr, remote_addr: SocketAddr, - session: CitadelSession, + session: CitadelSession, v_conn: VirtualConnectionType, hole_punched_addr: TargettedSocketAddr, ticket: Ticket, udp_mode: UdpMode, ) -> Result<(), NetworkError> { // TODO: use custom self-signed - let (listener, _) = Node::create_listen_socket( + let (listener, _) = Node::::create_listen_socket( ServerUnderlyingProtocol::new_quic_self_signed(), None, None, @@ -158,9 +159,9 @@ async fn setup_listener_non_initiator( .await } -async fn p2p_conn_handler( +async fn p2p_conn_handler( mut p2p_listener: GenericNetworkListener, - session: CitadelSession, + session: CitadelSession, _necessary_remote_addr: SocketAddr, v_conn: VirtualConnectionType, hole_punched_addr: TargettedSocketAddr, @@ -168,7 +169,7 @@ async fn p2p_conn_handler( udp_mode: UdpMode, ) -> Result<(), NetworkError> { let kernel_tx = session.kernel_tx.clone(); - let implicated_cid = session.implicated_cid.clone(); + let session_cid = session.session_cid.clone(); let weak = &session.as_weak(); std::mem::drop(session); @@ -182,7 +183,7 @@ async fn p2p_conn_handler( handle_p2p_stream( p2p_stream, - implicated_cid, + session_cid, session, kernel_tx, true, @@ -210,10 +211,10 @@ async fn p2p_conn_handler( /// optionally returns a receiver that gets triggered once the connection is upgraded. Only returned when the stream is a client stream, not a server stream #[allow(clippy::too_many_arguments)] -fn handle_p2p_stream( +fn handle_p2p_stream( mut p2p_stream: GenericNetworkStream, - implicated_cid: DualRwLock>, - session: CitadelSession, + session_cid: DualRwLock>, + session: CitadelSession, kernel_tx: UnboundedSender, from_listener: bool, v_conn: VirtualConnectionType, @@ -247,12 +248,12 @@ fn handle_p2p_stream( let p2p_handle = P2PInboundHandle::new( remote_peer, local_bind_addr.port(), - implicated_cid, + session_cid, kernel_tx, p2p_primary_stream_tx.clone(), peer_cid, ); - let writer_future = CitadelSession::outbound_stream(p2p_primary_stream_rx, sink); + let writer_future = CitadelSession::::outbound_stream(p2p_primary_stream_rx, sink); let reader_future = CitadelSession::execute_inbound_stream(stream, session.clone(), Some(p2p_handle)); let stopper_future = p2p_stopper(stopper_rx); @@ -313,7 +314,7 @@ pub struct P2PInboundHandle { pub remote_peer: SocketAddr, pub local_bind_port: u16, // this has to be the CID of the local session, not the peer's CID - pub implicated_cid: DualRwLock>, + pub session_cid: DualRwLock>, pub kernel_tx: UnboundedSender, pub to_primary_stream: OutboundPrimaryStreamSender, pub peer_cid: u64, @@ -323,7 +324,7 @@ impl P2PInboundHandle { fn new( remote_peer: SocketAddr, local_bind_port: u16, - implicated_cid: DualRwLock>, + session_cid: DualRwLock>, kernel_tx: UnboundedSender, to_primary_stream: OutboundPrimaryStreamSender, peer_cid: u64, @@ -331,7 +332,7 @@ impl P2PInboundHandle { Self { remote_peer, local_bind_port, - implicated_cid, + session_cid, kernel_tx, to_primary_stream, peer_cid, @@ -353,16 +354,16 @@ async fn p2p_stopper(receiver: Receiver<()>) -> Result<(), NetworkError> { skip_all, ret, err, - fields(implicated_cid=implicated_cid.get(), peer_cid=peer_connection_type.get_original_target_cid() + fields(session_cid=session_cid.get(), peer_cid=peer_connection_type.get_original_target_cid() ) ))] #[allow(clippy::too_many_arguments)] -pub(crate) async fn attempt_simultaneous_hole_punch( +pub(crate) async fn attempt_simultaneous_hole_punch( peer_connection_type: PeerConnectionType, ticket: Ticket, - session: CitadelSession, + session: CitadelSession, peer_nat_info: PeerNatInfo, - implicated_cid: DualRwLock>, + session_cid: DualRwLock>, kernel_tx: UnboundedSender, channel_signal: NodeResult, sync_time: Instant, @@ -400,7 +401,7 @@ pub(crate) async fn attempt_simultaneous_hole_punch( client_config.clone(), ) .map_err(generic_error)?; - let p2p_stream = Node::quic_p2p_connect_defaults( + let p2p_stream = Node::::quic_p2p_connect_defaults( quic_endpoint.endpoint, None, peer_nat_info.tls_domain, @@ -412,7 +413,7 @@ pub(crate) async fn attempt_simultaneous_hole_punch( log::trace!(target: "citadel", "~!@ P2P UDP Hole-punch + QUIC finished successfully for INITIATOR @!~"); handle_p2p_stream( p2p_stream, - implicated_cid, + session_cid, session.clone(), kernel_tx.clone(), false, diff --git a/citadel_proto/src/proto/peer/peer_layer.rs b/citadel_proto/src/proto/peer/peer_layer.rs index 2cb30d2db..f863278b2 100644 --- a/citadel_proto/src/proto/peer/peer_layer.rs +++ b/citadel_proto/src/proto/peer/peer_layer.rs @@ -22,7 +22,7 @@ let peer_layer = HyperNodePeerLayer::new(persistence_handler); // Create a new message group let group_key = peer_layer.create_new_message_group( - implicated_cid, + session_cid, &initial_peers, message_group_options )?; @@ -56,6 +56,7 @@ use crate::proto::peer::message_group::{MessageGroup, MessageGroupPeer}; use crate::proto::peer::peer_crypt::KeyExchangeProcess; use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualConnectionType; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::time::error::Error; use citadel_io::tokio::time::Duration; use citadel_io::tokio_util::time::{delay_queue, delay_queue::DelayQueue}; @@ -82,9 +83,9 @@ impl PeerLayerTimeoutFunction f /// When HyperLAN client A needs to send a POST_REGISTER signal to HyperLAN client B (who is disconnected), /// the request needs to stay in memory until either the peer joins OR HyperLAN client A disconnects. Hence the need for this layer -pub struct HyperNodePeerLayerInner { +pub struct CitadelNodePeerLayerInner { // When a signal is routed to the target destination, the server needs to keep track of the state while awaiting - pub(crate) persistence_handler: PersistenceHandler, + pub(crate) persistence_handler: PersistenceHandler, pub(crate) message_groups: HashMap>, pub(crate) simultaneous_ticket_mappings: HashMap>, waker: Arc, @@ -103,12 +104,12 @@ struct SharedInner { const MAILBOX: &str = "mailbox"; #[derive(Clone)] -pub struct HyperNodePeerLayer { - pub(crate) inner: std::sync::Arc>, +pub struct CitadelNodePeerLayer { + pub(crate) inner: std::sync::Arc>>, waker: std::sync::Arc, } -pub struct HyperNodePeerLayerExecutor { +pub struct CitadelNodePeerLayerExecutor { inner: Arc>, waker: Arc, } @@ -135,11 +136,11 @@ impl TrackedPosting { } } -impl HyperNodePeerLayer { +impl CitadelNodePeerLayer { #[allow(clippy::arc_with_non_send_sync)] - pub fn new(persistence_handler: PersistenceHandler) -> HyperNodePeerLayer { + pub fn new(persistence_handler: PersistenceHandler) -> CitadelNodePeerLayer { let waker = Arc::new(AtomicWaker::new()); - let inner = HyperNodePeerLayerInner { + let inner = CitadelNodePeerLayerInner { waker: waker.clone(), inner: Arc::new(citadel_io::RwLock::new(Default::default())), simultaneous_ticket_mappings: Default::default(), @@ -151,8 +152,8 @@ impl HyperNodePeerLayer { Self { inner, waker } } - pub async fn create_executor(&self) -> HyperNodePeerLayerExecutor { - HyperNodePeerLayerExecutor { + pub async fn create_executor(&self) -> CitadelNodePeerLayerExecutor { + CitadelNodePeerLayerExecutor { waker: self.waker.clone(), inner: self.inner.read().await.inner.clone(), } @@ -198,16 +199,16 @@ impl HyperNodePeerLayer { /// Cleans up the internal entries #[allow(unused_results)] - pub async fn on_session_shutdown(&self, implicated_cid: u64) -> Result<(), NetworkError> { + pub async fn on_session_shutdown(&self, session_cid: u64) -> Result<(), NetworkError> { let pers = { let mut this = self.inner.write().await; - this.message_groups.remove(&implicated_cid); - this.inner.write().observed_postings.remove(&implicated_cid); + this.message_groups.remove(&session_cid); + this.inner.write().observed_postings.remove(&session_cid); this.persistence_handler.clone() }; let _ = pers - .remove_byte_map_values_by_key(implicated_cid, 0, MAILBOX) + .remove_byte_map_values_by_key(session_cid, 0, MAILBOX) .await?; Ok(()) } @@ -216,12 +217,12 @@ impl HyperNodePeerLayer { #[allow(unused_results)] pub async fn create_new_message_group( &self, - implicated_cid: u64, + session_cid: u64, initial_peers: &Vec, options: MessageGroupOptions, ) -> Option { let mut this = self.inner.write().await; - let map = this.message_groups.get_mut(&implicated_cid)?; + let map = this.message_groups.get_mut(&session_cid)?; let mgid = options.id; if map.len() <= u8::MAX as usize { if let std::collections::hash_map::Entry::Vacant(e) = map.entry(mgid) { @@ -238,24 +239,24 @@ impl HyperNodePeerLayer { .insert(peer_cid, MessageGroupPeer { peer_cid }); } - // add the implicated_cid to the concurrent peers + // add the session_cid to the concurrent peers message_group.concurrent_peers.insert( - implicated_cid, + session_cid, MessageGroupPeer { - peer_cid: implicated_cid, + peer_cid: session_cid, }, ); e.insert(message_group); Some(MessageGroupKey { - cid: implicated_cid, + cid: session_cid, mgid, }) } else { None } } else { - log::warn!(target: "citadel", "The maximum number of groups per session has been reached for {}", implicated_cid); + log::warn!(target: "citadel", "The maximum number of groups per session has been reached for {}", session_cid); None } } @@ -402,7 +403,7 @@ impl HyperNodePeerLayer { /// `target_cid`: Should be the destination #[allow(unused_results)] pub async fn try_add_mailbox( - pers: &PersistenceHandler, + pers: &PersistenceHandler, target_cid: u64, signal: PeerSignal, ) -> Result<(), NetworkError> { @@ -418,20 +419,20 @@ impl HyperNodePeerLayer { } } -impl HyperNodePeerLayerExecutor { +impl CitadelNodePeerLayerExecutor { pub(self) fn poll_purge(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.waker.register(cx.waker()); let mut this = self.inner.write(); while let Some(res) = futures::ready!(this.delay_queue.poll_expired(cx)) { - let (implicated_cid, ticket) = res.into_inner(); - if let Some(active_postings) = this.observed_postings.get_mut(&implicated_cid) { + let (session_cid, ticket) = res.into_inner(); + if let Some(active_postings) = this.observed_postings.get_mut(&session_cid) { if let Some(posting) = active_postings.remove(&ticket) { - log::warn!(target: "citadel", "Running on_timeout for active posting {} for CID {}", ticket, implicated_cid); + log::warn!(target: "citadel", "Running on_timeout for active posting {} for CID {}", ticket, session_cid); (posting.on_timeout)(posting.signal) } else { - log::warn!(target: "citadel", "Attempted to remove active posting {} for CID {}, but failed", implicated_cid, ticket); + log::warn!(target: "citadel", "Attempted to remove active posting {} for CID {}, but failed", session_cid, ticket); } } } @@ -440,7 +441,7 @@ impl HyperNodePeerLayerExecutor { } } -impl HyperNodePeerLayerInner { +impl CitadelNodePeerLayerInner { pub fn insert_mapped_ticket(&mut self, cid: u64, ticket: Ticket, mapped_ticket: Ticket) { self.simultaneous_ticket_mappings .entry(cid) @@ -453,19 +454,19 @@ impl HyperNodePeerLayerInner { .get_mut(&cid)? .remove(&ticket) } - /// Determines if `peer_cid` is already attempting to register to `implicated_cid` + /// Determines if `peer_cid` is already attempting to register to `session_cid` /// Returns the target's ticket for their corresponding request pub fn check_simultaneous_register( &mut self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Option { - log::trace!(target: "citadel", "Checking simultaneous register between {} and {}", implicated_cid, peer_cid); + log::trace!(target: "citadel", "Checking simultaneous register between {} and {}", session_cid, peer_cid); self.check_simultaneous_event(peer_cid, |posting| if let PeerSignal::PostRegister { peer_conn_type: conn, inviter_username: _, invitee_username: _, ticket_opt: _, invitee_response: None, .. } = &posting.signal { - log::trace!(target: "citadel", "Checking if posting from conn={:?} ~ {:?}", conn, implicated_cid); - if let PeerConnectionType::LocalGroupPeer { implicated_cid: _, peer_cid: b } = conn { - *b == implicated_cid + log::trace!(target: "citadel", "Checking if posting from conn={:?} ~ {:?}", conn, session_cid); + if let PeerConnectionType::LocalGroupPeer { session_cid: _, peer_cid: b } = conn { + *b == session_cid } else { false } @@ -474,19 +475,19 @@ impl HyperNodePeerLayerInner { }) } - /// Determines if `peer_cid` is already attempting to connect to `implicated_cid` + /// Determines if `peer_cid` is already attempting to connect to `session_cid` /// Returns the target's ticket and signal for their corresponding request pub fn check_simultaneous_connect( &mut self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Option { - log::trace!(target: "citadel", "Checking simultaneous register between {} and {}", implicated_cid, peer_cid); + log::trace!(target: "citadel", "Checking simultaneous register between {} and {}", session_cid, peer_cid); self.check_simultaneous_event(peer_cid, |posting| if let PeerSignal::PostConnect { peer_conn_type: conn, ticket_opt: _, invitee_response: _, session_security_settings: _, udp_mode: _, .. } = &posting.signal { - log::trace!(target: "citadel", "Checking if posting from conn={:?} ~ {:?}", conn, implicated_cid); - if let PeerConnectionType::LocalGroupPeer { implicated_cid: _, peer_cid: b } = conn { - *b == implicated_cid + log::trace!(target: "citadel", "Checking if posting from conn={:?} ~ {:?}", conn, session_cid); + if let PeerConnectionType::LocalGroupPeer { session_cid: _, peer_cid: b } = conn { + *b == session_cid } else { false } @@ -497,13 +498,13 @@ impl HyperNodePeerLayerInner { pub fn check_simultaneous_deregister( &mut self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Option { - log::trace!(target: "citadel", "Checking simultaneous deregister between {} and {}", implicated_cid, peer_cid); + log::trace!(target: "citadel", "Checking simultaneous deregister between {} and {}", session_cid, peer_cid); self.check_simultaneous_event(peer_cid, |posting| if let PeerSignal::DeregistrationSuccess { peer_conn_type: peer } = &posting.signal { - log::trace!(target: "citadel", "Checking if posting from {} == {}", peer, implicated_cid); - peer.get_original_target_cid() == implicated_cid + log::trace!(target: "citadel", "Checking if posting from {} == {}", peer, session_cid); + peer.get_original_target_cid() == session_cid } else { false }) @@ -523,60 +524,60 @@ impl HyperNodePeerLayerInner { .map(|(ticket, _)| *ticket) } - /// An observed posting is associated with the `implicated_cid` - /// `on_timeout`: This function will be called if a timeout occurs. The provided session belongs to `implicated_cid` + /// An observed posting is associated with the `session_cid` + /// `on_timeout`: This function will be called if a timeout occurs. The provided session belongs to `session_cid` /// NOTE: the ticket MUST be unique per session, otherwise unexpired items may disappear unnecessarily! If the ticket ID's are provided /// by the HyperLAN client's side, this should work out #[allow(unused_results)] pub async fn insert_tracked_posting( &self, - implicated_cid: u64, + session_cid: u64, timeout: Duration, ticket: Ticket, signal: PeerSignal, on_timeout: impl FnOnce(PeerSignal) + SyncContextRequirements, ) { let mut this = self.inner.write(); - let delay_key = this.delay_queue.insert((implicated_cid, ticket), timeout); - log::trace!(target: "citadel", "Creating TrackedPosting {} (Ticket: {})", implicated_cid, ticket); + let delay_key = this.delay_queue.insert((session_cid, ticket), timeout); + log::trace!(target: "citadel", "Creating TrackedPosting {} (Ticket: {})", session_cid, ticket); - if let Some(map) = this.observed_postings.get_mut(&implicated_cid) { + if let Some(map) = this.observed_postings.get_mut(&session_cid) { let tracked_posting = TrackedPosting::new(signal, delay_key, on_timeout); map.insert(ticket, tracked_posting); std::mem::drop(this); self.waker.wake(); } else { - log::error!(target: "citadel", "Unable to find implicated_cid in observed_posting. Bad init state?"); + log::error!(target: "citadel", "Unable to find session_cid in observed_posting. Bad init state?"); } } pub fn remove_tracked_posting_inner( &mut self, - implicated_cid: u64, + session_cid: u64, ticket: Ticket, ) -> Option { - log::trace!(target: "citadel", "Removing tracked posting for {} (ticket: {})", implicated_cid, ticket); + log::trace!(target: "citadel", "Removing tracked posting for {} (ticket: {})", session_cid, ticket); let mut this = self.inner.write(); - if let Some(active_postings) = this.observed_postings.get_mut(&implicated_cid) { + if let Some(active_postings) = this.observed_postings.get_mut(&session_cid) { if let Some(active_posting) = active_postings.remove(&ticket) { - log::trace!(target: "citadel", "Successfully removed tracked posting {} (ticket: {})", implicated_cid, ticket); + log::trace!(target: "citadel", "Successfully removed tracked posting {} (ticket: {})", session_cid, ticket); let _ = this.delay_queue.remove(&active_posting.key); std::mem::drop(this); self.waker.wake(); Some(active_posting.signal) } else { - log::warn!(target: "citadel", "Tracked posting for {} (ticket: {}) does not exist since key for ticket does not exist", implicated_cid, ticket); + log::warn!(target: "citadel", "Tracked posting for {} (ticket: {}) does not exist since key for ticket does not exist", session_cid, ticket); None } } else { - log::warn!(target: "citadel", "Tracked posting for {} (ticket: {}) does not exist since key for cid does not exist", implicated_cid, ticket); + log::warn!(target: "citadel", "Tracked posting for {} (ticket: {}) does not exist since key for cid does not exist", session_cid, ticket); None } } } -impl Stream for HyperNodePeerLayerExecutor { +impl Stream for CitadelNodePeerLayerExecutor { type Item = (); fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -591,7 +592,7 @@ impl Stream for HyperNodePeerLayerExecutor { } } -impl futures::Future for HyperNodePeerLayerExecutor { +impl futures::Future for CitadelNodePeerLayerExecutor { type Output = Result<(), NetworkError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -636,7 +637,7 @@ pub enum PeerSignal { }, // This is used for the mailbox BroadcastConnected { - implicated_cid: u64, + session_cid: u64, group_broadcast: GroupBroadcast, }, PostFileUploadRequest { @@ -684,42 +685,42 @@ pub enum ChannelPacket { #[derive(PartialEq, Debug, Serialize, Deserialize, Copy, Clone, Eq, Hash)] pub enum PeerConnectionType { - // implicated_cid, target_cid + // session_cid, target_cid LocalGroupPeer { - implicated_cid: u64, + session_cid: u64, peer_cid: u64, }, - // implicated_cid, icid, target_cid + // session_cid, icid, target_cid ExternalGroupPeer { - implicated_cid: u64, + session_cid: u64, interserver_cid: u64, peer_cid: u64, }, } impl PeerConnectionType { - pub fn get_original_implicated_cid(&self) -> u64 { + pub fn get_original_session_cid(&self) -> u64 { match self { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: _target_cid, - } => *implicated_cid, + } => *session_cid, PeerConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: _icid, peer_cid: _target_cid, - } => *implicated_cid, + } => *session_cid, } } pub fn get_original_target_cid(&self) -> u64 { match self { PeerConnectionType::LocalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, peer_cid: target_cid, } => *target_cid, PeerConnectionType::ExternalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, interserver_cid: _icid, peer_cid: target_cid, } => *target_cid, @@ -729,20 +730,20 @@ impl PeerConnectionType { pub fn reverse(&self) -> PeerConnectionType { match self { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => PeerConnectionType::LocalGroupPeer { - implicated_cid: *target_cid, - peer_cid: *implicated_cid, + session_cid: *target_cid, + peer_cid: *session_cid, }, PeerConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: icid, peer_cid: target_cid, } => PeerConnectionType::ExternalGroupPeer { - implicated_cid: *target_cid, + session_cid: *target_cid, interserver_cid: *icid, - peer_cid: *implicated_cid, + peer_cid: *session_cid, }, } } @@ -750,18 +751,18 @@ impl PeerConnectionType { pub fn as_virtual_connection(self) -> VirtualConnectionType { match self { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, }, PeerConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: icid, peer_cid: target_cid, } => VirtualConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: icid, peer_cid: target_cid, }, @@ -773,17 +774,17 @@ impl Display for PeerConnectionType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => { - write!(f, "hLAN {implicated_cid} <-> {target_cid}") + write!(f, "hLAN {session_cid} <-> {target_cid}") } PeerConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: icid, peer_cid: target_cid, } => { - write!(f, "hWAN {implicated_cid} <-> {icid} <-> {target_cid}") + write!(f, "hWAN {session_cid} <-> {icid} <-> {target_cid}") } } } @@ -791,19 +792,17 @@ impl Display for PeerConnectionType { #[derive(PartialEq, Debug, Serialize, Deserialize, Copy, Clone)] pub enum NodeConnectionType { - // implicated_cid + // session_cid LocalGroupPeerToLocalGroupServer(u64), - // implicated_cid, icid + // session_cid, icid LocalGroupPeerToExternalGroupServer(u64, u64), } impl NodeConnectionType { - pub fn get_implicated_cid(&self) -> u64 { + pub fn get_session_cid(&self) -> u64 { match self { - NodeConnectionType::LocalGroupPeerToLocalGroupServer(implicated_cid) => *implicated_cid, - NodeConnectionType::LocalGroupPeerToExternalGroupServer(implicated_cid, _) => { - *implicated_cid - } + NodeConnectionType::LocalGroupPeerToLocalGroupServer(session_cid) => *session_cid, + NodeConnectionType::LocalGroupPeerToExternalGroupServer(session_cid, _) => *session_cid, } } } diff --git a/citadel_proto/src/proto/remote.rs b/citadel_proto/src/proto/remote.rs index 050007cb7..8bfdc8070 100644 --- a/citadel_proto/src/proto/remote.rs +++ b/citadel_proto/src/proto/remote.rs @@ -46,9 +46,10 @@ use crate::kernel::kernel_communicator::{ CallbackKey, KernelAsyncCallbackHandler, KernelStreamSubscription, }; use crate::prelude::NodeRequest; -use crate::proto::node::HdpServerRemoteInner; +use crate::proto::node::CitadelNodeRemoteInner; use crate::proto::outbound_sender::BoundedSender; use bytemuck::NoUninit; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::sync::mpsc::error::TrySendError; use citadel_user::account_manager::AccountManager; use citadel_wire::hypernode_type::NodeType; @@ -58,14 +59,14 @@ use std::sync::Arc; /// allows convenient communication with the server #[derive(Clone)] -pub struct NodeRemote { +pub struct NodeRemote { outbound_send_request_tx: BoundedSender<(NodeRequest, Ticket)>, - inner: Arc, + inner: Arc>, } #[async_trait::async_trait] #[auto_impl::auto_impl(Box, &mut, &, Arc)] -pub trait Remote: Clone + Send { +pub trait Remote: Clone + Send { /// Sends a request to the server and returns a ticket for tracking the response. async fn send(&self, request: NodeRequest) -> Result { let ticket = self.get_next_ticket(); @@ -88,14 +89,14 @@ pub trait Remote: Clone + Send { ) -> Result; /// Returns the account manager instance. - fn account_manager(&self) -> &AccountManager; + fn account_manager(&self) -> &AccountManager; /// Returns the next available ticket. fn get_next_ticket(&self) -> Ticket; } #[async_trait::async_trait] -impl Remote for NodeRemote { +impl Remote for NodeRemote { async fn send(&self, request: NodeRequest) -> Result { NodeRemote::send(self, request).await } @@ -115,7 +116,7 @@ impl Remote for NodeRemote { NodeRemote::send_callback_subscription(self, request).await } - fn account_manager(&self) -> &AccountManager { + fn account_manager(&self) -> &AccountManager { NodeRemote::account_manager(self) } @@ -124,24 +125,24 @@ impl Remote for NodeRemote { } } -impl Debug for NodeRemote { +impl Debug for NodeRemote { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "HdpServerRemote") + write!(f, "CitadelNodeRemote") } } -impl NodeRemote { +impl NodeRemote { /// Creates a new [`NodeRemote`] instance. pub(crate) fn new( outbound_send_request_tx: BoundedSender<(NodeRequest, Ticket)>, callback_handler: KernelAsyncCallbackHandler, - account_manager: AccountManager, + account_manager: AccountManager, node_type: NodeType, ) -> Self { // starts at 1. Ticket 0 is for reserved Self { outbound_send_request_tx, - inner: Arc::new(HdpServerRemoteInner { + inner: Arc::new(CitadelNodeRemoteInner { callback_handler, account_manager, node_type, @@ -183,7 +184,7 @@ impl NodeRemote { ) -> Result { let callback_key = CallbackKey { ticket, - implicated_cid: request.implicated_cid(), + session_cid: request.session_cid(), }; let rx = self.inner.callback_handler.register_stream(callback_key)?; @@ -242,12 +243,12 @@ impl NodeRemote { &self.inner.node_type } - pub fn account_manager(&self) -> &AccountManager { + pub fn account_manager(&self) -> &AccountManager { &self.inner.account_manager } } -impl Unpin for NodeRemote {} +impl Unpin for NodeRemote {} /// A type sent through the server when a request is made #[derive( diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index 0697b3102..4f4d4bd6e 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -58,7 +58,6 @@ use bytes::{Bytes, BytesMut}; use citadel_io::tokio_util::codec::LengthDelimitedCodec; use futures::{SinkExt, StreamExt, TryFutureExt, TryStreamExt}; -use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::proto::UdpMode; use citadel_user::account_manager::AccountManager; @@ -103,7 +102,7 @@ use crate::proto::packet_processor::includes::{Duration, SocketAddr}; use crate::proto::packet_processor::raw_primary_packet::{check_proxy, ReceivePortType}; use crate::proto::packet_processor::{self, PrimaryProcessorResult}; use crate::proto::peer::p2p_conn_handler::P2PInboundHandle; -use crate::proto::peer::peer_layer::{HyperNodePeerLayer, PeerSignal}; +use crate::proto::peer::peer_layer::{CitadelNodePeerLayer, PeerSignal}; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session_manager::CitadelSessionManager; use crate::proto::session_queue_handler::{ @@ -118,8 +117,8 @@ use crate::proto::state_container::{ use crate::proto::state_subcontainers::preconnect_state_container::UdpChannelSender; use crate::proto::state_subcontainers::rekey_container::calculate_update_frequency; use crate::proto::transfer_stats::TransferStats; -use atomic::Atomic; use bytemuck::NoUninit; +use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; use citadel_crypt::prelude::{ConstructorOpts, FixedSizedSource}; use citadel_crypt::streaming_crypt_scrambler::{scramble_encrypt_source, ObjectSource}; use citadel_types::crypto::SecurityLevel; @@ -142,11 +141,12 @@ use std::time::{SystemTime, UNIX_EPOCH}; //define_outer_struct_wrapper!(HdpSession, HdpSessionInner); /// Allows a connection stream to be worked on by a single worker -pub struct CitadelSession { +#[derive(Clone)] +pub struct CitadelSession { #[cfg(not(feature = "multi-threaded"))] - pub inner: std::rc::Rc, + pub inner: std::rc::Rc>, #[cfg(feature = "multi-threaded")] - pub inner: std::sync::Arc, + pub inner: std::sync::Arc>, } enum SessionShutdownReason { @@ -154,7 +154,7 @@ enum SessionShutdownReason { Error(NetworkError), } -impl CitadelSession { +impl CitadelSession { pub fn strong_count(&self) -> usize { #[cfg(not(feature = "multi-threaded"))] { @@ -168,28 +168,28 @@ impl CitadelSession { } #[cfg(not(feature = "multi-threaded"))] - pub fn as_weak(&self) -> std::rc::Weak { + pub fn as_weak(&self) -> std::rc::Weak> { std::rc::Rc::downgrade(&self.inner) } #[cfg(feature = "multi-threaded")] - pub fn as_weak(&self) -> std::sync::Weak { + pub fn as_weak(&self) -> std::sync::Weak> { std::sync::Arc::downgrade(&self.inner) } #[cfg(feature = "multi-threaded")] - pub fn upgrade_weak(this: &std::sync::Weak) -> Option { + pub fn upgrade_weak(this: &std::sync::Weak>) -> Option { this.upgrade().map(|inner| Self { inner }) } #[cfg(not(feature = "multi-threaded"))] - pub fn upgrade_weak(this: &std::rc::Weak) -> Option { + pub fn upgrade_weak(this: &std::rc::Weak>) -> Option { this.upgrade().map(|inner| Self { inner }) } } -impl From for CitadelSession { - fn from(inner: CitadelSessionInner) -> Self { +impl From> for CitadelSession { + fn from(inner: CitadelSessionInner) -> Self { #[cfg(not(feature = "multi-threaded"))] { Self { @@ -206,38 +206,28 @@ impl From for CitadelSession { } } -impl Deref for CitadelSession { - type Target = CitadelSessionInner; +impl Deref for CitadelSession { + type Target = CitadelSessionInner; fn deref(&self) -> &Self::Target { self.inner.deref() } } -impl Clone for CitadelSession { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - } - } -} - -//define_struct!(HdpSession, HdpSessionInner); - /// Structure for holding and keep track of packets, as well as basic connection information #[allow(unused)] -pub struct CitadelSessionInner { - pub(super) implicated_cid: DualRwLock>, +pub struct CitadelSessionInner { + pub(super) session_cid: DualRwLock>, pub(super) kernel_ticket: DualCell, pub(super) remote_peer: SocketAddr, // Sends results directly to the kernel pub(super) kernel_tx: UnboundedSender, pub(super) to_primary_stream: DualLateInit>, // Setting this will determine what algorithm is used during the DO_CONNECT stage - pub(super) session_manager: CitadelSessionManager, - pub(super) state: Arc>, - pub(super) state_container: StateContainer, - pub(super) account_manager: AccountManager, + pub(super) session_manager: CitadelSessionManager, + pub(super) state: DualCell, + pub(super) state_container: StateContainer, + pub(super) account_manager: AccountManager, pub(super) time_tracker: TimeTracker, pub(super) local_node_type: NodeType, pub(super) remote_node_type: Option, @@ -246,14 +236,14 @@ pub struct CitadelSessionInner { pub(super) dc_signal_sender: DualRwLock>>, pub(super) is_server: bool, pub(super) stopper_tx: DualRwLock>, - pub(super) queue_handle: DualLateInit, + pub(super) queue_handle: DualLateInit>, pub(super) peer_only_connect_protocol: DualRwLock>, pub(super) primary_stream_quic_conn: DualRwLock>, pub(super) local_nat_type: NatType, pub(super) adjacent_nat_type: DualLateInit>, pub(super) connect_mode: DualRwLock>, pub(super) client_config: Arc, - pub(super) hypernode_peer_layer: HyperNodePeerLayer, + pub(super) hypernode_peer_layer: CitadelNodePeerLayer, pub(super) stun_servers: Option>, pub(super) init_time: Instant, pub(super) file_transfer_compatible: DualLateInit, @@ -270,7 +260,7 @@ pub enum SessionState { /// or if it needs to register SocketJustOpened, /// If the endpoint does not have an implicated CID with the current node, then registration must occur. - /// This will imply the use of standard encryption to get the hyperencrypted drill over the wire + /// This will imply the use of standard encryption to get the hyperencrypted entropy_bank over the wire NeedsRegister, /// The system just initiated, and needs to begin the session NeedsConnect, @@ -289,31 +279,31 @@ pub enum HdpSessionInitMode { Register(SocketAddr, ProposedCredentials), } -pub(crate) struct SessionInitParams { +pub(crate) struct SessionInitParams { pub on_drop: UnboundedSender<()>, pub local_nat_type: NatType, - pub citadel_remote: NodeRemote, + pub citadel_remote: NodeRemote, pub local_bind_addr: SocketAddr, pub local_node_type: NodeType, pub kernel_tx: UnboundedSender, - pub session_manager: crate::proto::session_manager::CitadelSessionManager, - pub account_manager: AccountManager, + pub session_manager: CitadelSessionManager, + pub account_manager: AccountManager, pub time_tracker: TimeTracker, pub remote_peer: SocketAddr, pub init_ticket: Ticket, pub client_config: Arc, - pub hypernode_peer_layer: HyperNodePeerLayer, + pub hypernode_peer_layer: CitadelNodePeerLayer, // this is set only when a local client is attempting to start an outbound session - pub client_only_settings: Option, + pub client_only_settings: Option>, pub stun_servers: Option>, pub init_time: Instant, pub session_password: PreSharedKey, } -pub(crate) struct ClientOnlySessionInitSettings { +pub(crate) struct ClientOnlySessionInitSettings { pub init_mode: HdpSessionInitMode, pub peer_only_connect_proto: ConnectProtocol, - pub cnac: Option, + pub cnac: Option>, pub proposed_credentials: ProposedCredentials, pub udp_mode: UdpMode, pub keep_alive_timeout_ns: i64, @@ -321,14 +311,14 @@ pub(crate) struct ClientOnlySessionInitSettings { pub connect_mode: Option, } -impl CitadelSession { +impl CitadelSession { pub(crate) fn new( - session_init_params: SessionInitParams, + session_init_params: SessionInitParams, ) -> Result<(citadel_io::tokio::sync::broadcast::Sender<()>, Self), NetworkError> { let (stopper_tx, _stopper_rx) = citadel_io::tokio::sync::broadcast::channel(10); let client_only_settings = &session_init_params.client_only_settings; let is_server = client_only_settings.is_none(); - let (cnac, state, implicated_cid) = + let (cnac, state, session_cid) = if let Some(client_init_settings) = &session_init_params.client_only_settings { match &client_init_settings.init_mode { HdpSessionInitMode::Connect(auth) => { @@ -339,38 +329,23 @@ impl CitadelSession { .clone() .ok_or(NetworkError::InvalidRequest("Client does not exist"))?; let cid = cnac.get_cid(); - ( - Some(cnac), - Arc::new(Atomic::new(SessionState::NeedsConnect)), - Some(cid), - ) + (Some(cnac), SessionState::NeedsConnect, Some(cid)) } AuthenticationRequest::Passwordless { .. } => { // register will redirect to preconnect afterwards - ( - None, - Arc::new(Atomic::new(SessionState::NeedsRegister)), - None, - ) + (None, SessionState::NeedsRegister, None) } } } - HdpSessionInitMode::Register(..) => ( - None, - Arc::new(Atomic::new(SessionState::NeedsRegister)), - None, - ), + HdpSessionInitMode::Register(..) => (None, SessionState::NeedsRegister, None), } } else { - ( - None, - Arc::new(Atomic::new(SessionState::SocketJustOpened)), - None, - ) + (None, SessionState::SocketJustOpened, None) }; + let state = DualCell::from(state); let timestamp = session_init_params.time_tracker.get_global_time_ns(); let hypernode_peer_layer = session_init_params.hypernode_peer_layer; let connect_mode = client_only_settings.as_ref().and_then(|r| r.connect_mode); @@ -417,7 +392,7 @@ impl CitadelSession { local_bind_addr, local_node_type, remote_node_type, - implicated_cid: DualRwLock::from(implicated_cid), + session_cid: DualRwLock::from(session_cid), time_tracker, kernel_ticket: kernel_ticket.into(), remote_peer, @@ -478,7 +453,7 @@ impl CitadelSession { let this_queue_worker = self.clone(); let this_close = self.clone(); - let (session_future, handle_zero_state, implicated_cid) = { + let (session_future, handle_zero_state, session_cid) = { let quic_conn_opt = primary_stream.take_quic_connection(); let (writer, reader) = misc::net::safe_split_stream(primary_stream); @@ -499,7 +474,7 @@ impl CitadelSession { let timestamp = this.time_tracker.get_global_time_ns(); let cnac_opt = inner_state!(this.state_container).cnac.clone(); - let implicated_cid = this.implicated_cid.clone(); + let session_cid = this.session_cid.clone(); let persistence_handler = this.account_manager.get_persistence_handler().clone(); let stopper = inner!(this.stopper_tx).subscribe(); @@ -515,7 +490,7 @@ impl CitadelSession { persistence_handler, primary_outbound_tx, this_outbound, - this.state.load(Ordering::SeqCst), + this.state.get(), timestamp, cnac_opt, ); @@ -536,12 +511,12 @@ impl CitadelSession { // separate task, we solve the issue of re-entrancing of mutex spawn!(queue_worker_future); - (session_future, handle_zero_state, implicated_cid) + (session_future, handle_zero_state, session_cid) }; if let Err(err) = handle_zero_state.await { log::error!(target: "citadel", "Unable to proceed past session zero-state. Stopping session: {:?}", &err); - return Err((err, implicated_cid.get())); + return Err((err, session_cid.get())); } let res = session_future @@ -550,14 +525,14 @@ impl CitadelSession { match res { Ok(_) => { - log::trace!(target: "citadel", "Done EXECUTING sess (Ok(())) | cid: {:?} | is_server: {}", this_close.implicated_cid.get(), this_close.is_server); - Ok(implicated_cid.get()) + log::trace!(target: "citadel", "Done EXECUTING sess (Ok(())) | cid: {:?} | is_server: {}", this_close.session_cid.get(), this_close.is_server); + Ok(session_cid.get()) } Err(err) => { let ticket = this_close.kernel_ticket.get(); let reason = err.to_string(); - let cid = implicated_cid.get(); + let cid = session_cid.get(); log::trace!(target: "citadel", "Session {} connected to {} is ending! Reason: {}. (strong count: {})", ticket.0, peer_addr, reason.as_str(), this_close.strong_count()); @@ -596,12 +571,12 @@ impl CitadelSession { )] async fn handle_zero_state( zero_packet: Option, - persistence_handler: PersistenceHandler, + persistence_handler: PersistenceHandler, to_outbound: OutboundPrimaryStreamSender, - session: CitadelSession, + session: CitadelSession, state: SessionState, timestamp: i64, - cnac: Option, + cnac: Option>, ) -> Result<(), NetworkError> { if let Some(zero) = zero_packet { to_outbound @@ -628,19 +603,19 @@ impl CitadelSession { .register_state .passwordless .ok_or(NetworkError::InternalError("Passwordless state not loaded"))?; - // we supply 0,0 for cid and new drill vers by default, even though it will be reset by bob - let alice_constructor = StackedRatchetConstructor::new_alice( - ConstructorOpts::new_vec_init( - Some(session_security_settings.crypto_params), - (session_security_settings.security_level.value() + 1) as usize, - ), - proposed_cid, - 0, - Some(session_security_settings.security_level), - ) - .ok_or(NetworkError::InternalError( - "Unable to construct Alice ratchet", - ))?; + // we supply 0,0 for cid and new entropy_bank vers by default, even though it will be reset by bob + let alice_constructor = + >::new_alice( + ConstructorOpts::new_vec_init( + Some(session_security_settings.crypto_params), + session_security_settings.security_level, + ), + proposed_cid, + 0, + ) + .ok_or(NetworkError::InternalError( + "Unable to construct Alice ratchet", + ))?; state_container.register_state.last_packet_time = Some(Instant::now()); log::trace!(target: "citadel", "Running stage0 alice"); @@ -652,7 +627,7 @@ impl CitadelSession { ))?; let stage0_register_packet = - crate::proto::packet_crafter::do_register::craft_stage0( + crate::proto::packet_crafter::do_register::craft_stage0::( session_security_settings.crypto_params.into(), timestamp, transfer, @@ -692,8 +667,8 @@ impl CitadelSession { } pub(crate) fn begin_connect( - session: &CitadelSession, - cnac: &ClientNetworkAccount, + session: &CitadelSession, + cnac: &ClientNetworkAccount, ) -> Result<(), NetworkError> { log::trace!(target: "citadel", "Beginning pre-connect subroutine!"); let session_ref = session; @@ -706,7 +681,7 @@ impl CitadelSession { let session_security_settings = state_container.session_security_settings.unwrap(); let peer_only_connect_mode = session_ref.peer_only_connect_protocol.get().unwrap(); // reset the toolset's ARA - let static_aux_hr = &cnac.refresh_static_hyper_ratchet(); + let static_aux_hr = &cnac.refresh_static_ratchet(); // security level inside static hr may not be what the declared session security level for this session is. Session security level can be no higher than the initial static HR level, since the chain requires recursion from the initial value let _ = static_aux_hr.verify_level(Some(session_security_settings.security_level)).map_err(|_| NetworkError::InvalidRequest("The specified security setting for the session exceeds the registration security setting"))?; let opts = static_aux_hr @@ -714,16 +689,11 @@ impl CitadelSession { .into_iter() .take((session_security_settings.security_level.value() + 1) as usize) .collect(); - //static_aux_hr.verify_level(Some(security_level)).map_err(|_| NetworkError::Generic(format!("Invalid security level. Maximum security level for this account is {:?}", static_aux_hr.get_default_security_level())))?; - let alice_constructor = StackedRatchetConstructor::new_alice( - opts, - cnac.get_cid(), - 0, - Some(session_security_settings.security_level), - ) - .ok_or(NetworkError::InternalError( - "Unable to construct Alice ratchet", - ))?; + let alice_constructor = + >::new_alice(opts, cnac.get_cid(), 0) + .ok_or(NetworkError::InternalError( + "Unable to construct Alice ratchet", + ))?; let transfer = alice_constructor .stage0_alice() .ok_or(NetworkError::InternalError( @@ -766,7 +736,7 @@ impl CitadelSession { // tcp_conn_awaiter must be provided in order to know when the begin loading the UDP conn for the user. The TCP connection must first be loaded in order to place the udp conn inside the virtual_conn hashmap pub(crate) fn udp_socket_loader( - this: CitadelSession, + this: CitadelSession, v_target: VirtualTargetType, udp_conn: UdpSplittableTypes, addr: TargettedSocketAddr, @@ -812,7 +782,7 @@ impl CitadelSession { .ok_or(NetworkError::InternalError("HdpSession no longer exists"))?; let accessor = match v_target { - VirtualConnectionType::LocalGroupServer { implicated_cid: _ } => { + VirtualConnectionType::LocalGroupServer { session_cid: _ } => { let mut state_container = inner_mut_state!(sess.state_container); state_container.udp_primary_outbound_tx = Some(udp_sender.clone()); log::trace!(target: "citadel", "C2S UDP subroutine inserting UDP channel ... (is_server={})", is_server); @@ -847,7 +817,7 @@ impl CitadelSession { } VirtualConnectionType::LocalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, peer_cid: target_cid, } => { let mut state_container = inner_mut_state!(sess.state_container); @@ -941,7 +911,8 @@ impl CitadelSession { .map(|r| { #[cfg_attr( feature = "localhost-testing", - tracing::instrument(level = "trace", target = "citadel", skip_all, fields(packet_length = r.len())) + tracing::instrument(level = "trace", target = "citadel", skip_all, fields(packet_length = r.len() + )) )] fn process_outbound_packet(r: BytesMut) -> Bytes { r.freeze() @@ -961,7 +932,7 @@ impl CitadelSession { )] pub async fn execute_inbound_stream( mut reader: CleanShutdownStream, - this_main: CitadelSession, + this_main: CitadelSession, p2p_handle: Option, ) -> Result<(), NetworkError> { let this_main = &this_main; @@ -969,7 +940,7 @@ impl CitadelSession { let ( ref remote_peer, ref local_primary_port, - ref implicated_cid, + ref session_cid, ref kernel_tx, ref primary_stream, peer_cid, @@ -978,7 +949,7 @@ impl CitadelSession { ( p2p.remote_peer, p2p.local_bind_port, - p2p.implicated_cid, + p2p.session_cid, p2p.kernel_tx, p2p.to_primary_stream, Some(p2p.peer_cid), @@ -988,14 +959,14 @@ impl CitadelSession { let borrow = this_main; let remote_peer = borrow.remote_peer; let local_primary_port = borrow.local_bind_addr.port(); - let implicated_cid = borrow.implicated_cid.clone(); + let session_cid = borrow.session_cid.clone(); let kernel_tx = borrow.kernel_tx.clone(); let primary_stream = borrow.to_primary_stream.clone().unwrap(); let is_server = borrow.is_server; ( remote_peer, local_primary_port, - implicated_cid, + session_cid, kernel_tx, primary_stream, None, @@ -1003,16 +974,16 @@ impl CitadelSession { ) }; - fn evaluate_result( + fn evaluate_result( result: Result, primary_stream: &OutboundPrimaryStreamSender, kernel_tx: &UnboundedSender, - session: &CitadelSession, + session: &CitadelSession, cid_opt: Option, ) -> std::io::Result<()> { match result { Ok(PrimaryProcessorResult::ReplyToSender(return_packet)) => { - CitadelSession::send_to_primary_stream_closure( + CitadelSession::::send_to_primary_stream_closure( primary_stream, kernel_tx, return_packet, @@ -1028,7 +999,7 @@ impl CitadelSession { } Err(reason) => { - log::error!(target: "citadel", "[PrimaryProcessor] session ending: {:?} | Session end state: {:?}", reason, session.state.load(Ordering::Relaxed)); + log::error!(target: "citadel", "[PrimaryProcessor] session ending: {:?} | Session end state: {:?}", reason, session.state.get()); Err(std::io::Error::new( std::io::ErrorKind::Other, reason.into_string(), @@ -1047,8 +1018,8 @@ impl CitadelSession { } } - fn handle_session_terminating_error( - session: &CitadelSession, + fn handle_session_terminating_error( + session: &CitadelSession, err: std::io::Error, is_server: bool, peer_cid: Option, @@ -1068,19 +1039,19 @@ impl CitadelSession { if err_string.contains(SUCCESS_DISCONNECT) { SessionShutdownReason::ProperShutdown } else { - let implicated_cid = session.implicated_cid.get().unwrap_or_default(); + let session_cid = session.session_cid.get().unwrap_or_default(); let v_conn_type = if let Some(peer_cid) = peer_cid { VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid, } } else { - VirtualConnectionType::LocalGroupServer { implicated_cid } + VirtualConnectionType::LocalGroupServer { session_cid } }; if let Err(err) = session.send_to_kernel(NodeResult::Disconnect(Disconnect { ticket: session.kernel_ticket.get(), - cid_opt: session.implicated_cid.get(), + cid_opt: session.session_cid.get(), success: false, v_conn_type: Some(v_conn_type), message: err_string.clone(), @@ -1109,16 +1080,16 @@ impl CitadelSession { let res = reader .try_for_each_concurrent(None, |packet| async move { - let implicated_cid = implicated_cid.get(); + let session_cid = session_cid.get(); let result = packet_processor::raw_primary_packet::process_raw_packet( - implicated_cid, + session_cid, this_main, *remote_peer, *local_primary_port, packet, ) .await; - evaluate_result(result, primary_stream, kernel_tx, this_main, implicated_cid) + evaluate_result(result, primary_stream, kernel_tx, this_main, session_cid) }) .map_err(|err| handle_session_terminating_error(this_main, err, is_server, peer_cid)) .await; @@ -1157,7 +1128,7 @@ impl CitadelSession { feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err(Debug)) )] - async fn execute_queue_worker(this_main: CitadelSession) -> Result<(), NetworkError> { + async fn execute_queue_worker(this_main: CitadelSession) -> Result<(), NetworkError> { log::trace!(target: "citadel", "HdpSession async timer subroutine executed"); let queue_worker = { @@ -1186,7 +1157,7 @@ impl CitadelSession { )), LOGIN_EXPIRATION_TIME, |state_container| { - if state_container.state.load(Ordering::SeqCst) != SessionState::Connected { + if !state_container.state.is_connected() { QueueWorkerResult::EndSession } else { // remove it from being called again @@ -1199,7 +1170,7 @@ impl CitadelSession { queue_worker.insert_reserved_fn(Some(QueueWorkerTicket::Periodic(DRILL_REKEY_WORKER, 0)), Duration::from_nanos(DRILL_UPDATE_FREQUENCY_LOW_BASE), move |state_container| { let ticket = kernel_ticket; - if state_container.state.load(Ordering::Relaxed) == SessionState::Connected { + if state_container.state.is_connected() { let timestamp = time_tracker.get_global_time_ns(); let security_level = state_container.session_security_settings.as_ref().map(|r| r.security_level).unwrap(); @@ -1212,18 +1183,18 @@ impl CitadelSession { } }).collect::>(); - let virtual_target = VirtualTargetType::LocalGroupServer { implicated_cid: C2S_ENCRYPTION_ONLY }; - if state_container.initiate_drill_update(timestamp, virtual_target, Some(ticket)).is_ok() { + let virtual_target = VirtualTargetType::LocalGroupServer { session_cid: C2S_ENCRYPTION_ONLY }; + if state_container.initiate_entropy_bank_update(timestamp, virtual_target, Some(ticket)).is_ok() { // now, call for each p2p session for vconn in p2p_sessions { - if let Err(err) = state_container.initiate_drill_update(timestamp, vconn, None) { - log::warn!(target: "citadel", "Unable to initiate drill update for {:?}: {:?}", vconn, err); + if let Err(err) = state_container.initiate_entropy_bank_update(timestamp, vconn, None) { + log::warn!(target: "citadel", "Unable to initiate entropy_bank update for {:?}: {:?}", vconn, err); } } QueueWorkerResult::AdjustPeriodicity(calculate_update_frequency(security_level.value(), &state_container.transfer_stats)) } else { - log::warn!(target: "citadel", "initiate_drill_update subroutine signalled failure"); + log::warn!(target: "citadel", "initiate_entropy_bank_update subroutine signalled failure"); QueueWorkerResult::EndSession } } else { @@ -1234,7 +1205,7 @@ impl CitadelSession { queue_worker.insert_reserved_fn(Some(QueueWorkerTicket::Periodic(KEEP_ALIVE_CHECKER, 0)), Duration::from_millis(KEEP_ALIVE_INTERVAL_MS), move |state_container| { let timestamp = time_tracker_2.get_global_time_ns(); - if state_container.state.load(Ordering::SeqCst) == SessionState::Connected { + if state_container.state.is_connected() { if state_container.keep_alive_timeout_ns != 0 { if state_container.keep_alive_subsystem_timed_out(timestamp) && state_container.meta_expiry_state.expired() { log::error!(target: "citadel", "The keep alive subsystem has timed out. Executing shutdown phase (skipping proper disconnect)"); @@ -1256,7 +1227,7 @@ impl CitadelSession { Some(QueueWorkerTicket::Periodic(FIREWALL_KEEP_ALIVE, 0)), FIREWALL_KEEP_ALIVE_UDP, move |state_container| { - if state_container.state.load(Ordering::SeqCst) == SessionState::Connected { + if state_container.state.is_connected() { if state_container.udp_mode == UdpMode::Disabled { //log::trace!(target: "citadel", "TCP only mode detected. Removing FIREWALL_KEEP_ALIVE subroutine"); return QueueWorkerResult::Complete; @@ -1295,7 +1266,7 @@ impl CitadelSession { match v_conn { VirtualConnectionType::LocalGroupServer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, } => { let crypt_container = &mut state_container .c2s_channel_container @@ -1303,7 +1274,7 @@ impl CitadelSession { .unwrap() .peer_session_crypto; - let latest_hr = crypt_container.get_hyper_ratchet(None).unwrap(); + let latest_hr = crypt_container.get_ratchet(None).unwrap(); let packet = packet_crafter::file::craft_revfs_pull( latest_hr, security_level, @@ -1316,14 +1287,14 @@ impl CitadelSession { self.send_to_primary_stream(Some(ticket), packet) } VirtualConnectionType::LocalGroupPeer { - implicated_cid: _, + session_cid: _, peer_cid: target_cid, } => { let endpoint_container = state_container.get_peer_endpoint_container_mut(target_cid)?; let latest_hr = endpoint_container .endpoint_crypto - .get_hyper_ratchet(None) + .get_ratchet(None) .unwrap(); let packet = packet_crafter::file::craft_revfs_pull( latest_hr, @@ -1362,7 +1333,7 @@ impl CitadelSession { match v_conn { VirtualConnectionType::LocalGroupServer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, } => { let crypt_container = &mut state_container .c2s_channel_container @@ -1370,7 +1341,7 @@ impl CitadelSession { .unwrap() .peer_session_crypto; - let latest_hr = crypt_container.get_hyper_ratchet(None).unwrap(); + let latest_hr = crypt_container.get_ratchet(None).unwrap(); let packet = packet_crafter::file::craft_revfs_delete( latest_hr, security_level, @@ -1382,14 +1353,14 @@ impl CitadelSession { self.send_to_primary_stream(Some(ticket), packet) } VirtualConnectionType::LocalGroupPeer { - implicated_cid: _, + session_cid: _, peer_cid: target_cid, } => { let endpoint_container = state_container.get_peer_endpoint_container_mut(target_cid)?; let latest_hr = endpoint_container .endpoint_crypto - .get_hyper_ratchet(None) + .get_ratchet(None) .unwrap(); let packet = packet_crafter::file::craft_revfs_delete( latest_hr, @@ -1414,7 +1385,7 @@ impl CitadelSession { } fn ensure_connected(&self, ticket: &Ticket) -> Result<(), NetworkError> { - if self.state.load(Ordering::Relaxed) != SessionState::Connected { + if !self.state.is_connected() { Err(NetworkError::Generic(format!("Attempted to send a request (ticket: {ticket}) outbound, but the session is not connected"))) } else { Ok(()) @@ -1476,7 +1447,7 @@ impl CitadelSession { let mut group_sender_rx = citadel_io::tokio_stream::wrappers::ReceiverStream::new(group_sender_rx); let (stop_tx, stop_rx) = citadel_io::tokio::sync::oneshot::channel(); - // the above are the same for all vtarget types. Now, we need to get the proper drill and pqc + // the above are the same for all vtarget types. Now, we need to get the proper entropy_bank and pqc let mut state_container = inner_mut_state!(this.state_container); @@ -1492,9 +1463,9 @@ impl CitadelSession { groups_needed, metadata, ) = match virtual_target { - VirtualTargetType::LocalGroupServer { implicated_cid } => { + VirtualTargetType::LocalGroupServer { session_cid } => { // if we are sending this just to the HyperLAN server (in the case of file uploads), - // then, we use this session's pqc, the cnac's latest drill, and 0 for target_cid + // then, we use this session's pqc, the cnac's latest entropy_bank, and 0 for target_cid if !*self.file_transfer_compatible { return Err(NetworkError::msg("File transfer is not enabled for this session. Both nodes must use a filesystem backend")); } @@ -1509,7 +1480,7 @@ impl CitadelSession { .map(|r| r.object_id) .unwrap_or_else(|| crypt_container.get_next_object_id()); let group_id_start = crypt_container.get_and_increment_group_id(); - let latest_hr = crypt_container.get_hyper_ratchet(None).cloned().unwrap(); + let latest_hr = crypt_container.get_ratchet(None).cloned().unwrap(); let static_aux_ratchet = crypt_container .toolset .get_static_auxiliary_ratchet() @@ -1545,10 +1516,10 @@ impl CitadelSession { ) .expect("Invalid timestamp") .to_rfc2822(), - author: implicated_cid.to_string(), + author: session_cid.to_string(), plaintext_length: file_size, group_count: groups_needed, - cid: implicated_cid, + cid: session_cid, transfer_type, }; @@ -1570,17 +1541,17 @@ impl CitadelSession { file_header, object_id, target_cid, - implicated_cid, + session_cid, groups_needed, file_metadata, ) } VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => { - log::trace!(target: "citadel", "Sending HyperLAN peer ({}) <-> HyperLAN Peer ({})", implicated_cid, target_cid); + log::trace!(target: "citadel", "Sending HyperLAN peer ({}) <-> HyperLAN Peer ({})", session_cid, target_cid); // here, we don't use the base session's PQC. Instead, we use the c2s vconn's pqc to ensure the peer can't access the contents // of the file let crypt_container_c2s = &state_container @@ -1611,7 +1582,7 @@ impl CitadelSession { let latest_usable_ratchet = endpoint_container .endpoint_crypto - .get_hyper_ratchet(None) + .get_ratchet(None) .unwrap(); let preferred_primary_stream = endpoint_container @@ -1647,10 +1618,10 @@ impl CitadelSession { ) .expect("Invalid timestamp") .to_rfc2822(), - author: implicated_cid.to_string(), + author: session_cid.to_string(), plaintext_length: file_size, group_count: groups_needed, - cid: implicated_cid, + cid: session_cid, transfer_type, }; @@ -1749,7 +1720,7 @@ impl CitadelSession { // let mut relative_group_id = 0; - let implicated_cid = this.implicated_cid.get(); + let session_cid = this.session_cid.get(); // while waiting, we likely have a set of GroupSenders to process while let Some(sender) = group_sender_rx.next().await { match sender { @@ -1757,29 +1728,27 @@ impl CitadelSession { let (group_id, key) = { // construct the OutboundTransmitters let sess = this; - if sess.state.load(Ordering::Relaxed) != SessionState::Connected { + if !sess.state.is_connected() { log::warn!(target: "citadel", "Since transmitting the file, the session ended"); return; } let mut state_container = inner_mut_state!(sess.state_container); - let proper_latest_hyper_ratchet = match virtual_target { - VirtualConnectionType::LocalGroupServer { implicated_cid: _ } => { + let proper_latest_stacked_ratchet = match virtual_target { + VirtualConnectionType::LocalGroupServer { session_cid: _ } => { state_container .c2s_channel_container .as_ref() .unwrap() .peer_session_crypto - .get_hyper_ratchet(None) + .get_ratchet(None) } VirtualConnectionType::LocalGroupPeer { - implicated_cid: _, + session_cid: _, peer_cid, } => match state_container.get_peer_session_crypto(peer_cid) { - Some(peer_sess_crypt) => { - peer_sess_crypt.get_hyper_ratchet(None) - } + Some(peer_sess_crypt) => peer_sess_crypt.get_ratchet(None), None => { log::warn!(target: "citadel", "Since transmitting the file, the peer session ended"); @@ -1793,17 +1762,17 @@ impl CitadelSession { } }; - if proper_latest_hyper_ratchet.is_none() { + if proper_latest_stacked_ratchet.is_none() { log::error!(target: "citadel", "Unable to unwrap StackedRatchet (X-05)"); return; } - let hyper_ratchet = proper_latest_hyper_ratchet.unwrap(); + let stacked_ratchet = proper_latest_stacked_ratchet.unwrap(); let mut transmitter = GroupTransmitter::new_from_group_sender( to_primary_stream.clone(), sender, - RatchetPacketCrafterContainer::new(hyper_ratchet.clone(), None), + RatchetPacketCrafterContainer::new(stacked_ratchet.clone(), None), object_id, ticket, security_level, @@ -1870,8 +1839,8 @@ impl CitadelSession { if kernel_tx2.unbounded_send(NodeResult::InternalServerError(InternalServerError { ticket_opt: Some(ticket), - cid_opt: implicated_cid, - message: format!("Timeout on ticket {ticket}") + cid_opt: session_cid, + message: format!("Timeout on ticket {ticket}"), })).is_err() { log::error!(target: "citadel", "[File] Unable to send kernel error signal. Ending session"); QueueWorkerResult::EndSession @@ -1910,7 +1879,7 @@ impl CitadelSession { .clone() .unbounded_send(NodeResult::InternalServerError(InternalServerError { ticket_opt: Some(ticket), - cid_opt: implicated_cid, + cid_opt: session_cid, message: err.to_string(), })); } @@ -1928,7 +1897,7 @@ impl CitadelSession { // TODO: Make a generic version to allow requests the ability to bypass the session manager pub(crate) fn spawn_message_sender_function( - this: CitadelSession, + this: CitadelSession, mut rx: citadel_io::tokio::sync::mpsc::Receiver, ) { let task = async move { @@ -1966,7 +1935,7 @@ impl CitadelSession { .unbounded_send(NodeResult::InternalServerError( InternalServerError { ticket_opt: Some(ticket), - cid_opt: this.implicated_cid.get(), + cid_opt: this.session_cid.get(), message: err.into_string(), }, )) @@ -1982,7 +1951,7 @@ impl CitadelSession { .unbounded_send(NodeResult::InternalServerError( InternalServerError { ticket_opt: Some(ticket), - cid_opt: this.implicated_cid.get(), + cid_opt: this.session_cid.get(), message: err.into_string(), }, )) @@ -2013,7 +1982,7 @@ impl CitadelSession { ) -> Result<(), NetworkError> { log::trace!(target: "citadel", "Dispatching peer command {:?} ...", peer_command); let this = self; - let state = this.state.load(Ordering::SeqCst); + let state = this.state.get(); if state != SessionState::Connected { log::warn!(target: "citadel", "Session is not connected (s={:?}); will still attempt send peer command {:?}", state, peer_command) @@ -2044,7 +2013,7 @@ impl CitadelSession { )), }, ticket, - implicated_cid: self.implicated_cid.get().ok_or_else(|| { + session_cid: self.session_cid.get().ok_or_else(|| { NetworkError::InternalError("Implicated CID not set") })?, })) @@ -2076,7 +2045,7 @@ impl CitadelSession { .outgoing_peer_connect_attempts .contains_key(&a.get_original_target_cid()) { - log::warn!(target: "citadel", "{} is already attempting to connect to {}", a.get_original_implicated_cid(), a.get_original_target_cid()) + log::warn!(target: "citadel", "{} is already attempting to connect to {}", a.get_original_session_cid(), a.get_original_target_cid()) } state_container @@ -2099,7 +2068,7 @@ impl CitadelSession { n => n, }; - let hyper_ratchet = state_container + let stacked_ratchet = state_container .c2s_channel_container .as_ref() .ok_or_else(|| { @@ -2108,10 +2077,10 @@ impl CitadelSession { )) })? .peer_session_crypto - .get_hyper_ratchet(None) + .get_ratchet(None) .unwrap(); let packet = super::packet_crafter::peer_cmd::craft_peer_signal( - hyper_ratchet, + stacked_ratchet, signal_processed, ticket, timestamp, @@ -2127,11 +2096,11 @@ impl CitadelSession { } async fn listen_udp_port( - this: CitadelSession, + this: CitadelSession, _hole_punched_addr_ip: IpAddr, local_port: u16, mut stream: S, - peer_session_accessor: EndpointCryptoAccessor, + peer_session_accessor: EndpointCryptoAccessor, ) -> Result<(), NetworkError> { while let Some(res) = stream.next().await { match res { @@ -2157,7 +2126,7 @@ impl CitadelSession { receiver: UnboundedReceiver<(u8, BytesMut)>, hole_punched_addr: TargettedSocketAddr, mut sink: S, - peer_session_accessor: EndpointCryptoAccessor, + peer_session_accessor: EndpointCryptoAccessor, ) -> Result<(), NetworkError> { let mut receiver = citadel_io::tokio_stream::wrappers::UnboundedReceiverStream::new(receiver); @@ -2188,7 +2157,7 @@ impl CitadelSession { pub fn process_inbound_packet_udp( &self, packet: HdpPacket, - accessor: &EndpointCryptoAccessor, + accessor: &EndpointCryptoAccessor, ) -> Result<(), NetworkError> { if packet.get_length() < HDP_HEADER_BYTE_LEN { return Ok(()); @@ -2201,10 +2170,10 @@ impl CitadelSession { return Ok(()); } - let hr_version = header.drill_version.get(); + let hr_version = header.entropy_bank_version.get(); let mut endpoint_cid_info = None; match check_proxy( - self.implicated_cid.get(), + self.session_cid.get(), header.cmd_primary, header.cmd_aux, header.session_cid.get(), @@ -2247,7 +2216,7 @@ impl CitadelSession { /// Returns true if the disconnect initiate was a success, false if not. An error returns if something else occurs pub fn initiate_disconnect(&self, ticket: Ticket) -> Result { let session = self; - if session.state.load(Ordering::Relaxed) != SessionState::Connected { + if !session.state.is_connected() { log::error!(target: "citadel", "Must be connected to HyperLAN in order to start disconnect") } @@ -2273,14 +2242,14 @@ impl CitadelSession { to_kernel_tx, disconnect_stage0_packet, Some(ticket), - self.implicated_cid.get(), + self.session_cid.get(), ) })? .map(|_| true) } } -impl CitadelSessionInner { +impl CitadelSessionInner { /// Stores the proposed credentials into the register state container pub(crate) fn store_proposed_credentials(&mut self, proposed_credentials: ProposedCredentials) { let mut state_container = inner_mut_state!(self.state_container); @@ -2332,7 +2301,7 @@ impl CitadelSessionInner { Err(err) => { self.send_to_kernel(NodeResult::InternalServerError(InternalServerError { ticket_opt: ticket, - cid_opt: self.implicated_cid.get(), + cid_opt: self.session_cid.get(), message: err.to_string(), })) .map_err(|err| NetworkError::Generic(err.to_string()))?; @@ -2357,7 +2326,7 @@ impl CitadelSessionInner { self.send_to_kernel(NodeResult::InternalServerError(InternalServerError { ticket_opt: ticket, message: err.to_string(), - cid_opt: self.implicated_cid.get(), + cid_opt: self.session_cid.get(), })) .map_err(|err| NetworkError::Generic(err.to_string()))?; Err(err) @@ -2369,8 +2338,7 @@ impl CitadelSessionInner { /// Stops the future from running pub fn shutdown(&self) { - self.state - .store(SessionState::Disconnected, Ordering::SeqCst); + self.state.set(SessionState::Disconnected); let _ = inner!(self.stopper_tx).send(()); } @@ -2396,8 +2364,8 @@ impl CitadelSessionInner { } pub(crate) fn is_provisional(&self) -> bool { - let state = self.state.load(Ordering::Relaxed); - //self.implicated_cid.is_none() + let state = self.state.get(); + //self.session_cid.is_none() // SocketJustOpened is only the state for a session created from an incoming connection state == SessionState::SocketJustOpened || state == SessionState::NeedsConnect @@ -2411,18 +2379,10 @@ impl CitadelSessionInner { disconnect_success: bool, msg: T, ) { - if self - .session_manager - .provisional_session_active(&self.remote_peer) - { - // If we are provisional, we don't need to send a disconnect signal - return; - } - if let Some(tx) = self.dc_signal_sender.take() { let _ = tx.unbounded_send(NodeResult::Disconnect(Disconnect { ticket: ticket.unwrap_or_else(|| self.kernel_ticket.get()), - cid_opt: self.implicated_cid.get(), + cid_opt: self.session_cid.get(), success: disconnect_success, v_conn_type: None, message: msg.into(), @@ -2435,16 +2395,18 @@ impl CitadelSessionInner { } } -impl Drop for CitadelSessionInner { +impl Drop for CitadelSession { fn drop(&mut self) { - log::trace!(target: "citadel", "*** Dropping HdpSession {:?} ***", self.implicated_cid.get()); - self.send_session_dc_signal(None, false, "Session dropped"); + if self.strong_count() == 1 { + log::trace!(target: "citadel", "*** Dropping HdpSession {:?} ***", self.session_cid.get()); + self.send_session_dc_signal(None, false, "Session dropped"); - if self.on_drop.unbounded_send(()).is_err() { - //log::error!(target: "citadel", "Unable to cleanly alert node that session ended: {:?}", err); - } + if self.on_drop.unbounded_send(()).is_err() { + //log::error!(target: "citadel", "Unable to cleanly alert node that session ended: {:?}", err); + } - let _ = inner!(self.stopper_tx).send(()); + let _ = inner!(self.stopper_tx).send(()); + } } } diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index d76238b8b..e67a06655 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -42,7 +42,7 @@ use std::sync::atomic::Ordering; use bytes::BytesMut; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_user::account_manager::AccountManager; use citadel_user::auth::proposed_credentials::ProposedCredentials; use citadel_user::prelude::{ConnectProtocol, UserIdentifierExt}; @@ -67,8 +67,8 @@ use crate::proto::packet_processor::includes::{Duration, Instant}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::peer::peer_layer::{ - HyperNodePeerLayer, HyperNodePeerLayerInner, MailboxTransfer, PeerConnectionType, PeerResponse, - PeerSignal, + CitadelNodePeerLayer, CitadelNodePeerLayerInner, MailboxTransfer, PeerConnectionType, + PeerResponse, PeerSignal, }; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::{ @@ -88,20 +88,20 @@ use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::exports::tokio_rustls::rustls::ClientConfig; use std::sync::Arc; -define_outer_struct_wrapper!(CitadelSessionManager, HdpSessionManagerInner); +define_outer_struct_wrapper!(CitadelSessionManager, HdpSessionManagerInner, , ); /// Used for handling stateful connections between two peer -pub struct HdpSessionManagerInner { +pub struct HdpSessionManagerInner { local_node_type: NodeType, - pub(crate) sessions: HashMap, CitadelSession)>, - account_manager: AccountManager, - pub(crate) hypernode_peer_layer: HyperNodePeerLayer, - server_remote: Option, + pub(crate) sessions: HashMap, CitadelSession)>, + account_manager: AccountManager, + pub(crate) hypernode_peer_layer: CitadelNodePeerLayer, + server_remote: Option>, incoming_cxn_count: usize, /// Connections which have no implicated CID go herein. They are strictly expected to be /// in the state of NeedsRegister. Once they leave that state, they are eventually polled /// by the [CitadelSessionManager] and thereafter placed inside an appropriate session - pub provisional_connections: HashMap, CitadelSession)>, + pub provisional_connections: HashMap, CitadelSession)>, kernel_tx: UnboundedSender, time_tracker: TimeTracker, clean_shutdown_tracker_tx: UnboundedSender<()>, @@ -110,12 +110,12 @@ pub struct HdpSessionManagerInner { stun_servers: Option>, } -impl CitadelSessionManager { +impl CitadelSessionManager { /// Creates a new [SessionManager] which handles individual connections pub fn new( local_node_type: NodeType, kernel_tx: UnboundedSender, - account_manager: AccountManager, + account_manager: AccountManager, time_tracker: TimeTracker, client_config: Arc, stun_servers: Option>, @@ -125,7 +125,7 @@ impl CitadelSessionManager { let inner = HdpSessionManagerInner { clean_shutdown_tracker_tx, clean_shutdown_tracker: Some(clean_shutdown_tracker_rx), - hypernode_peer_layer: HyperNodePeerLayer::new( + hypernode_peer_layer: CitadelNodePeerLayer::new( account_manager.get_persistence_handler().clone(), ), server_remote: None, @@ -145,7 +145,7 @@ impl CitadelSessionManager { /// Loads the server remote, and gets the time tracker for the calling [Node] /// Used during the init stage - pub(crate) fn load_server_remote_get_tt(&self, server_remote: NodeRemote) -> TimeTracker { + pub(crate) fn load_server_remote_get_tt(&self, server_remote: NodeRemote) -> TimeTracker { let mut this = inner_mut!(self); this.server_remote = Some(server_remote); this.time_tracker @@ -157,15 +157,10 @@ impl CitadelSessionManager { this.sessions.contains_key(&cid) } - pub fn provisional_session_active(&self, peer_addr: &SocketAddr) -> bool { - let this = inner!(self); - this.provisional_connections.contains_key(peer_addr) - } - /// Called by the higher-level [Node] async writer loop /// `nid_local` is only needed in case a provisional id is needed. /// This is initiated by the local HyperNode's request to connect to an external server - /// `proposed_credentials`: Must be Some if implicated_cid is None! + /// `proposed_credentials`: Must be Some if session_cid is None! #[allow(clippy::too_many_arguments)] pub async fn initiate_connection( &self, @@ -291,7 +286,7 @@ impl CitadelSessionManager { // create conn to peer let primary_stream = - Node::create_session_transport_init(peer_addr, default_client_config) + Node::::create_session_transport_init(peer_addr, default_client_config) .await .map_err(|err| NetworkError::SocketError(err.to_string()))?; let local_bind_addr = primary_stream @@ -383,12 +378,12 @@ impl CitadelSessionManager { skip_all, ret, err, - fields(implicated_cid=new_session.implicated_cid.get(), is_server=new_session.is_server, peer_addr=peer_addr.to_string() + fields(session_cid=new_session.session_cid.get(), is_server=new_session.is_server, peer_addr=peer_addr.to_string() ) ))] async fn execute_session_with_safe_shutdown( - session_manager: CitadelSessionManager, - new_session: CitadelSession, + session_manager: CitadelSessionManager, + new_session: CitadelSession, peer_addr: SocketAddr, tcp_stream: GenericNetworkStream, ) -> Result<(), NetworkError> { @@ -429,7 +424,7 @@ impl CitadelSessionManager { // Especially needed for FCM // The only time the static HR won't get refreshed if a lingering connection gets cleaned-up if sess.do_static_hr_refresh_atexit.get() { - let _ = cnac.refresh_static_hyper_ratchet(); + let _ = cnac.refresh_static_ratchet(); } if cnac.passwordless() { @@ -444,11 +439,11 @@ impl CitadelSessionManager { // the following shutdown sequence is valid for only for the HyperLAN server // This final sequence alerts all CIDs in the network if sess.is_server { - // if the account was newly registered, it is possible that implicated_cid is none + // if the account was newly registered, it is possible that session_cid is none // if this is the case, ignore safe-shutdown of the session since no possible vconns // exist - if let Some(implicated_cid) = sess.implicated_cid.get() { - let task = async move { peer_layer.on_session_shutdown(implicated_cid).await }; + if let Some(session_cid) = sess.session_cid.get() { + let task = async move { peer_layer.on_session_shutdown(session_cid).await }; spawn!(task); @@ -462,21 +457,21 @@ impl CitadelSessionManager { let peer_cid = peer_id; // toggling this off ensures that any higher-level channels are disabled vconn.is_active.store(false, Ordering::SeqCst); - if peer_cid != implicated_cid && peer_cid != 0 { + if peer_cid != session_cid && peer_cid != 0 { let vconn = vconn.connection_type; - if let VirtualConnectionType::LocalGroupPeer { implicated_cid: _, peer_cid: _ } = vconn { - if peer_cid != implicated_cid { - log::trace!(target: "citadel", "Alerting {} that {} disconnected", peer_cid, implicated_cid); + if let VirtualConnectionType::LocalGroupPeer { session_cid: _, peer_cid: _ } = vconn { + if peer_cid != session_cid { + log::trace!(target: "citadel", "Alerting {peer_cid} that {session_cid} disconnected"); let peer_conn_type = PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid, }; let signal = PeerSignal::Disconnect { peer_conn_type, - disconnect_response: Some(PeerResponse::Disconnected(format!("{peer_cid} disconnected from {implicated_cid} forcibly"))), + disconnect_response: Some(PeerResponse::Disconnected(format!("{peer_cid} disconnected from {session_cid} forcibly"))), }; - if let Err(_err) = sess_mgr.send_signal_to_peer_direct(peer_cid, |peer_hyper_ratchet| { - super::packet_crafter::peer_cmd::craft_peer_signal(peer_hyper_ratchet, signal, Ticket(0), timestamp, security_level) + if let Err(_err) = sess_mgr.send_signal_to_peer_direct(peer_cid, |peer_stacked_ratchet| { + super::packet_crafter::peer_cmd::craft_peer_signal(peer_stacked_ratchet, signal, Ticket(0), timestamp, security_level) }) { //log::error!(target: "citadel", "Unable to send shutdown signal to {}: {:?}", peer_cid, err); } @@ -484,8 +479,8 @@ impl CitadelSessionManager { if let Some(peer_sess) = sess_mgr.sessions.get(&peer_cid) { let peer_sess = &peer_sess.1; let mut peer_state_container = inner_mut_state!(peer_sess.state_container); - if peer_state_container.active_virtual_connections.remove(&implicated_cid).is_none() { - log::warn!(target: "citadel", "While dropping session {}, attempted to remove vConn to {}, but peer did not have the vConn listed. Report to developers", implicated_cid, peer_cid); + if peer_state_container.active_virtual_connections.remove(&session_cid).is_none() { + log::warn!(target: "citadel", "While dropping session {}, attempted to remove vConn to {}, but peer did not have the vConn listed. Report to developers", session_cid, peer_cid); } } } @@ -509,7 +504,7 @@ impl CitadelSessionManager { /// This future should be joined up higher at the [node] layer pub async fn run_peer_container( - citadel_session_manager: CitadelSessionManager, + citadel_session_manager: CitadelSessionManager, ) -> Result<(), NetworkError> { let peer_container = { inner!(citadel_session_manager).hypernode_peer_layer.clone() }; peer_container.create_executor().await.await @@ -580,15 +575,15 @@ impl CitadelSessionManager { pub fn process_outbound_broadcast_command( &self, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, command: GroupBroadcast, ) -> Result<(), NetworkError> { let this = inner!(self); - if let Some(existing_session) = this.sessions.get(&implicated_cid) { + if let Some(existing_session) = this.sessions.get(&session_cid) { inner_mut_state!(existing_session.1.state_container) .process_outbound_broadcast_command(ticket, &command) } else { - Err(NetworkError::Generic(format!("Hypernode session for {implicated_cid} does not exist! Not going to handle group broadcast signal {command:?} ..."))) + Err(NetworkError::Generic(format!("Hypernode session for {session_cid} does not exist! Not going to handle group broadcast signal {command:?} ..."))) } } @@ -599,7 +594,7 @@ impl CitadelSessionManager { ticket: Ticket, max_group_size: Option, source: Box, - implicated_cid: u64, + session_cid: u64, virtual_target: VirtualTargetType, security_level: SecurityLevel, transfer_type: TransferType, @@ -608,7 +603,7 @@ impl CitadelSessionManager { let local_encryption_level = None; - if let Some(existing_session) = this.sessions.get(&implicated_cid) { + if let Some(existing_session) = this.sessions.get(&session_cid) { existing_session.1.process_outbound_file( ticket, max_group_size, @@ -622,7 +617,7 @@ impl CitadelSessionManager { ) } else { Err(NetworkError::Generic(format!( - "Hypernode session for {implicated_cid} does not exist! Not going to send data ..." + "Hypernode session for {session_cid} does not exist! Not going to send data ..." ))) } } @@ -630,18 +625,18 @@ impl CitadelSessionManager { pub fn revfs_pull( &self, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, v_conn: VirtualConnectionType, virtual_path: PathBuf, delete_on_pull: bool, security_level: SecurityLevel, ) -> Result<(), NetworkError> { let lock = inner!(self); - if let Some((_, sess)) = lock.sessions.get(&implicated_cid) { + if let Some((_, sess)) = lock.sessions.get(&session_cid) { sess.revfs_pull(ticket, v_conn, virtual_path, delete_on_pull, security_level) } else { Err(NetworkError::Generic(format!( - "Hypernode session for {implicated_cid} does not exist! Not going to process request ..." + "Hypernode session for {session_cid} does not exist! Not going to process request ..." ))) } } @@ -649,37 +644,37 @@ impl CitadelSessionManager { pub fn revfs_delete( &self, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, v_conn: VirtualConnectionType, virtual_path: PathBuf, security_level: SecurityLevel, ) -> Result<(), NetworkError> { let lock = inner!(self); - if let Some((_, sess)) = lock.sessions.get(&implicated_cid) { + if let Some((_, sess)) = lock.sessions.get(&session_cid) { sess.revfs_delete(ticket, v_conn, virtual_path, security_level) } else { Err(NetworkError::Generic(format!( - "Hypernode session for {implicated_cid} does not exist! Not going to process request ..." + "Hypernode session for {session_cid} does not exist! Not going to process request ..." ))) } } /// Returns true if the process continued successfully - pub fn initiate_update_drill_subroutine( + pub fn initiate_update_entropy_bank_subroutine( &self, virtual_target: VirtualTargetType, ticket: Ticket, ) -> Result<(), NetworkError> { - let implicated_cid = virtual_target.get_implicated_cid(); + let session_cid = virtual_target.get_session_cid(); let this = inner!(self); - if let Some(sess) = this.sessions.get(&implicated_cid) { + if let Some(sess) = this.sessions.get(&session_cid) { let sess = &sess.1; let timestamp = sess.time_tracker.get_global_time_ns(); let mut state_container = inner_mut_state!(sess.state_container); - state_container.initiate_drill_update(timestamp, virtual_target, Some(ticket)) + state_container.initiate_entropy_bank_update(timestamp, virtual_target, Some(ticket)) } else { Err(NetworkError::Generic(format!( - "Unable to initiate drill update subroutine for {implicated_cid} (not an active session)" + "Unable to initiate entropy_bank update subroutine for {session_cid} (not an active session)" ))) } } @@ -687,17 +682,17 @@ impl CitadelSessionManager { /// Returns true if the process initiated successfully pub fn initiate_deregistration_subroutine( &self, - implicated_cid: u64, + session_cid: u64, connection_type: VirtualConnectionType, ticket: Ticket, ) -> Result<(), NetworkError> { let this = inner!(self); - if let Some(sess) = this.sessions.get(&implicated_cid) { + if let Some(sess) = this.sessions.get(&session_cid) { let sess = &sess.1; sess.initiate_deregister(connection_type, ticket) } else { Err(NetworkError::Generic(format!( - "Unable to initiate deregister subroutine for {implicated_cid} (not an active session)" + "Unable to initiate deregister subroutine for {session_cid} (not an active session)" ))) } } @@ -714,17 +709,17 @@ impl CitadelSessionManager { /// In the case that this return false, further interaction should be avoided pub async fn dispatch_peer_command( &self, - implicated_cid: u64, + session_cid: u64, ticket: Ticket, peer_command: PeerSignal, security_level: SecurityLevel, ) -> Result<(), NetworkError> { let sess = { let this = inner!(self); - if let Some(sess) = this.sessions.get(&implicated_cid) { + if let Some(sess) = this.sessions.get(&session_cid) { sess.1.clone() } else { - return Err(NetworkError::msg(format!("Session for {implicated_cid} not found in session manager. Failed to dispatch peer command {peer_command:?}"))); + return Err(NetworkError::msg(format!("Session for {session_cid} not found in session manager. Failed to dispatch peer command {peer_command:?}"))); } }; @@ -744,11 +739,11 @@ impl CitadelSessionManager { /// DO_CONNECT stage /// This will return false if the provisional connection was already removed. This can happen to really /// slow connections, or during background execution on android/ios - pub fn upgrade_connection(&self, socket_addr: SocketAddr, implicated_cid: u64) -> bool { + pub fn upgrade_connection(&self, socket_addr: SocketAddr, session_cid: u64) -> bool { let mut this = inner_mut!(self); if let Some((_, stopper, session)) = this.provisional_connections.remove(&socket_addr) { - //let _ = this.hypernode_peer_layer.register_peer(implicated_cid, true); - if let Some(lingering_conn) = this.sessions.insert(implicated_cid, (stopper, session)) { + //let _ = this.hypernode_peer_layer.register_peer(session_cid, true); + if let Some(lingering_conn) = this.sessions.insert(session_cid, (stopper, session)) { // sometimes (especially on cellular networks), when the network changes due to // changing cell towers (or between WIFI/Cellular modes), the session lingers // without cleaning itself up. It will automatically drop by itself, however, @@ -757,7 +752,7 @@ impl CitadelSessionManager { // from a provisional to a protected connection must be allowed. As such, issue a warning here, // then return true to allow the new connection to proceed instead of returning false // due to overlapping connection - log::warn!(target: "citadel", "Cleaned up lingering session for {}", implicated_cid); + log::warn!(target: "citadel", "Cleaned up lingering session for {}", session_cid); let prev_conn = &lingering_conn.1; prev_conn.do_static_hr_refresh_atexit.set(false); } @@ -771,11 +766,11 @@ impl CitadelSessionManager { /// Returns true if the disconnect was a success, false if not. An error returns if something else occurs pub fn initiate_disconnect( &self, - implicated_cid: u64, + session_cid: u64, ticket: Ticket, ) -> Result { let this = inner!(self); - let res = match this.sessions.get(&implicated_cid) { + let res = match this.sessions.get(&session_cid) { Some(session) => session.1.initiate_disconnect(ticket), None => Ok(false), }; @@ -787,9 +782,9 @@ impl CitadelSessionManager { this.kernel_tx .unbounded_send(NodeResult::Disconnect(Disconnect { ticket, - cid_opt: Some(implicated_cid), + cid_opt: Some(session_cid), success: true, - v_conn_type: Some(VirtualConnectionType::LocalGroupServer { implicated_cid }), + v_conn_type: Some(VirtualConnectionType::LocalGroupServer { session_cid }), message: "Already disconnected".to_string(), }))?; } @@ -821,7 +816,7 @@ impl CitadelSessionManager { &self, timestamp: i64, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, peers_to_notify: Vec, security_level: SecurityLevel, options: MessageGroupOptions, @@ -829,18 +824,18 @@ impl CitadelSessionManager { let peer_layer = { inner!(self).hypernode_peer_layer.clone() }; let key = peer_layer - .create_new_message_group(implicated_cid, &peers_to_notify, options) + .create_new_message_group(session_cid, &peers_to_notify, options) .await?; // notify all the peers for peer_cid in peers_to_notify { let this = inner!(self); - if let Err(err) = this.send_signal_to_peer_direct(peer_cid, |peer_hyper_ratchet| { + if let Err(err) = this.send_signal_to_peer_direct(peer_cid, |peer_stacked_ratchet| { let signal = GroupBroadcast::Invitation { - sender: implicated_cid, + sender: session_cid, key, }; super::packet_crafter::peer_cmd::craft_group_message_packet( - peer_hyper_ratchet, + peer_stacked_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -871,10 +866,10 @@ impl CitadelSessionManager { for peer_cid in group.concurrent_peers.keys() { if *peer_cid != cid_host { if let Err(err) = - this.send_signal_to_peer_direct(*peer_cid, |peer_hyper_ratchet| { + this.send_signal_to_peer_direct(*peer_cid, |peer_stacked_ratchet| { let signal = GroupBroadcast::Disconnected { key }; super::packet_crafter::peer_cmd::craft_group_message_packet( - peer_hyper_ratchet, + peer_stacked_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -900,7 +895,7 @@ impl CitadelSessionManager { pub async fn kick_from_message_group( &self, mode: GroupMemberAlterMode, - implicated_cid: u64, + session_cid: u64, timestamp: i64, ticket: Ticket, key: MessageGroupKey, @@ -920,14 +915,14 @@ impl CitadelSessionManager { if mode == GroupMemberAlterMode::Kick { // notify all the peers removed for peer in &peers_removed { - if *peer != implicated_cid { + if *peer != session_cid { to_broadcast_dc.push(*peer); } } } for peer in peers_remaining { - if peer != implicated_cid { + if peer != session_cid { to_broadcast_left.push(peer); } } @@ -986,7 +981,7 @@ impl CitadelSessionManager { /// Note: uses mail_if_offline: true. This allows a member to disconnect, but to still receive messages later-on pub async fn broadcast_signal_to_group( &self, - implicated_cid: u64, + session_cid: u64, timestamp: i64, ticket: Ticket, key: MessageGroupKey, @@ -998,7 +993,7 @@ impl CitadelSessionManager { if let Some(peers_to_broadcast_to) = peer_layer.get_peers_in_message_group(key).await { let broadcastees = peers_to_broadcast_to .iter() - .filter(|peer| **peer != implicated_cid) + .filter(|peer| **peer != session_cid) .map(|r| (*r, true)); log::trace!(target: "citadel", "[Server/Group] peers_and_statuses: {:?}", broadcastees); let (_success, failed) = self @@ -1056,21 +1051,21 @@ impl CitadelSessionManager { /// Ensures the mailbox and tracked event queue are loaded into the [PeerLayer] pub async fn register_session_with_peer_layer( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result, NetworkError> { let peer_layer = { inner!(self).hypernode_peer_layer.clone() }; - peer_layer.register_peer(implicated_cid).await + peer_layer.register_peer(session_cid).await } - /// Removes a virtual connection `implicated_cid` from `peer_cid` + /// Removes a virtual connection `session_cid` from `peer_cid` pub fn disconnect_virtual_conn( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, - on_internal_disconnect: impl FnOnce(&StackedRatchet) -> BytesMut, + on_internal_disconnect: impl FnOnce(&R) -> BytesMut, ) -> Result<(), String> { - if implicated_cid == peer_cid { + if session_cid == peer_cid { return Err("Implicated CID cannot equal peer cid".to_string()); } @@ -1084,7 +1079,7 @@ impl CitadelSessionManager { .borrow_hr(None, |hr, state_container| { let removed = state_container .active_virtual_connections - .remove(&implicated_cid); + .remove(&session_cid); if removed.is_some() { let packet = on_internal_disconnect(hr); to_primary @@ -1103,7 +1098,7 @@ impl CitadelSessionManager { pub fn route_packet_to( &self, target_cid: u64, - packet: impl FnOnce(&StackedRatchet) -> BytesMut, + packet: impl FnOnce(&R) -> BytesMut, ) -> Result<(), String> { let lock = inner!(self); let (_, sess_ref) = lock @@ -1119,21 +1114,21 @@ impl CitadelSessionManager { }).map_err(|err| err.into_string())? } - /// Stores the `signal` inside the internal timed-queue for `implicated_cid`, and then sends `packet` to `target_cid`. + /// Stores the `signal` inside the internal timed-queue for `session_cid`, and then sends `packet` to `target_cid`. /// After `timeout`, the closure `on_timeout` is executed #[allow(clippy::too_many_arguments)] pub async fn route_signal_primary( &self, - peer_layer: &mut HyperNodePeerLayerInner, - implicated_cid: u64, + peer_layer: &mut CitadelNodePeerLayerInner, + session_cid: u64, target_cid: u64, ticket: Ticket, signal: PeerSignal, - packet: impl FnOnce(&StackedRatchet) -> BytesMut, + packet: impl FnOnce(&R) -> BytesMut, timeout: Duration, on_timeout: impl Fn(PeerSignal) + SyncContextRequirements, ) -> Result<(), String> { - if implicated_cid == target_cid { + if session_cid == target_cid { return Err("Target CID cannot be equal to the implicated CID".to_string()); } @@ -1156,23 +1151,23 @@ impl CitadelSessionManager { // get the target cid's session if let Some(ref sess_ref) = peer_sess { peer_layer - .insert_tracked_posting(implicated_cid, timeout, ticket, signal, on_timeout) + .insert_tracked_posting(session_cid, timeout, ticket, signal, on_timeout) .await; let peer_sender = sess_ref.to_primary_stream.as_ref().unwrap(); let accessor = EndpointCryptoAccessor::C2S(sess_ref.state_container.clone()); accessor.borrow_hr(None, |hr, _| { - log::trace!(target: "citadel", "Routing packet through primary stream ({} -> {})", implicated_cid, target_cid); + log::trace!(target: "citadel", "Routing packet through primary stream ({} -> {})", session_cid, target_cid); let packet = packet(hr); peer_sender.unbounded_send(packet).map_err(|err| err.to_string()) }).map_err(|err| err.into_string())? } else { - // session is not active, but user is registered (thus offline). Setup return ticket tracker on implicated_cid + // session is not active, but user is registered (thus offline). Setup return ticket tracker on session_cid // and deliver to the mailbox of target_cid, that way target_cid receives mail on connect. TODO: external svc route, if available { peer_layer .insert_tracked_posting( - implicated_cid, + session_cid, timeout, ticket, signal.clone(), @@ -1180,7 +1175,7 @@ impl CitadelSessionManager { ) .await; } - HyperNodePeerLayer::try_add_mailbox(&pers, target_cid, signal) + CitadelNodePeerLayer::try_add_mailbox(&pers, target_cid, signal) .await .map_err(|err| err.into_string()) } @@ -1237,9 +1232,9 @@ impl CitadelSessionManager { for (peer, is_registered) in peers_and_statuses { if is_registered { if this - .send_signal_to_peer_direct(peer, |peer_hyper_ratchet| { + .send_signal_to_peer_direct(peer, |peer_stacked_ratchet| { super::packet_crafter::peer_cmd::craft_group_message_packet( - peer_hyper_ratchet, + peer_stacked_ratchet, &signal, ticket, C2S_ENCRYPTION_ONLY, @@ -1267,11 +1262,11 @@ impl CitadelSessionManager { // TODO: optimize this into a single operation for peer in to_mail { - HyperNodePeerLayer::try_add_mailbox( + CitadelNodePeerLayer::try_add_mailbox( &pers, peer, PeerSignal::BroadcastConnected { - implicated_cid: peer, + session_cid: peer, group_broadcast: signal.clone(), }, ) @@ -1291,17 +1286,17 @@ impl CitadelSessionManager { #[inline] pub async fn route_signal_response_primary( &self, - implicated_cid: u64, + session_cid: u64, target_cid: u64, ticket: Ticket, - session: &CitadelSession, - packet: impl FnOnce(&StackedRatchet) -> BytesMut, + session: &CitadelSession, + packet: impl FnOnce(&R) -> BytesMut, post_send: impl FnOnce( - &CitadelSession, + &CitadelSession, PeerSignal, ) -> Result, ) -> Result, String> { - // Instead of checking for registration, check the `implicated_cid`'s timed queue for a ticket corresponding to Ticket. + // Instead of checking for registration, check the `session_cid`'s timed queue for a ticket corresponding to Ticket. let tracked_posting = { session .hypernode_peer_layer @@ -1311,7 +1306,7 @@ impl CitadelSessionManager { .remove_tracked_posting_inner(target_cid, ticket) }; if let Some(tracked_posting) = tracked_posting { - // since the posting was valid, we just need to forward the signal to `implicated_cid` + // since the posting was valid, we just need to forward the signal to `session_cid` let this = inner!(self); if let Some(target_sess) = this.sessions.get(&target_cid) { //let ret = target_sess.clone(); @@ -1331,7 +1326,7 @@ impl CitadelSessionManager { Ok((post_send)(sess_ref, tracked_posting)) } else { - // session no longer exists. Could have been that the `implicated_cid` responded too late. Send an error back, saying it expired + // session no longer exists. Could have been that the `session_cid` responded too late. Send an error back, saying it expired Err(format!( "Session for {target_cid} is not active, and thus no room for consent" )) @@ -1339,13 +1334,13 @@ impl CitadelSessionManager { } else { // the tracked posting doesn't exist. It may have expired. In either case, the potential session is invalid Err(format!( - "Tracked posting {ticket} for {target_cid} -> {implicated_cid} does not exist" + "Tracked posting {ticket} for {target_cid} -> {session_cid} does not exist" )) } } } -impl HdpSessionManagerInner { +impl HdpSessionManagerInner { /// Clears a session from the SessionManager pub fn clear_session(&mut self, cid: u64, init_time: Instant) { if let Some((_, session)) = self.sessions.get(&cid) { @@ -1362,7 +1357,7 @@ impl HdpSessionManagerInner { pub fn send_signal_to_peer_direct( &self, target_cid: u64, - packet: impl FnOnce(&StackedRatchet) -> BytesMut, + packet: impl FnOnce(&R) -> BytesMut, ) -> Result<(), NetworkError> { if let Some(peer_sess) = self.sessions.get(&target_cid) { let peer_sess = &peer_sess.1; diff --git a/citadel_proto/src/proto/session_queue_handler.rs b/citadel_proto/src/proto/session_queue_handler.rs index e817bf06d..d60be8619 100644 --- a/citadel_proto/src/proto/session_queue_handler.rs +++ b/citadel_proto/src/proto/session_queue_handler.rs @@ -43,6 +43,7 @@ use std::task::{Context, Poll, Waker}; use crate::inner_arg::ExpectedInnerTargetMut; use crate::proto::state_container::{StateContainer, StateContainerInner}; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -55,45 +56,51 @@ pub const DRILL_REKEY_WORKER: usize = 1; pub const KEEP_ALIVE_CHECKER: usize = 2; pub const FIREWALL_KEEP_ALIVE: usize = 3; -pub trait QueueFunction: - Fn(&mut dyn ExpectedInnerTargetMut) -> QueueWorkerResult + Send + 'static +pub trait QueueFunction: + Fn(&mut dyn ExpectedInnerTargetMut>) -> QueueWorkerResult + Send + 'static { } impl< - T: Fn(&mut dyn ExpectedInnerTargetMut) -> QueueWorkerResult + R: Ratchet, + T: Fn(&mut dyn ExpectedInnerTargetMut>) -> QueueWorkerResult + Send + 'static, - > QueueFunction for T + > QueueFunction for T { } -pub struct SessionQueueWorker { - entries: HashMap, delay_queue::Key, Duration)>, +#[allow(clippy::type_complexity)] +pub struct SessionQueueWorker { + entries: HashMap>, delay_queue::Key, Duration)>, expirations: DelayQueue, - state_container: Option, + state_container: Option>, sess_shutdown: Sender<()>, waker: Option, - rx: UnboundedReceiver, + rx: UnboundedReceiver>, // keeps track of how many items have been added rolling_idx: usize, } #[derive(Clone)] -pub struct SessionQueueWorkerHandle { - tx: UnboundedSender, +pub struct SessionQueueWorkerHandle { + tx: UnboundedSender>, rolling_idx: Arc, } -type ChannelInner = (Option, Duration, Box); +type ChannelInner = ( + Option, + Duration, + Box>, +); -impl SessionQueueWorkerHandle { +impl SessionQueueWorkerHandle { /// Inserts a reserved system process. We now spawn this as a task to prevent deadlocking pub fn insert_reserved( &self, key: Option, timeout: Duration, - on_timeout: impl Fn(&mut dyn ExpectedInnerTargetMut) -> QueueWorkerResult + on_timeout: impl Fn(&mut dyn ExpectedInnerTargetMut>) -> QueueWorkerResult + Send + 'static, ) { @@ -105,7 +112,7 @@ impl SessionQueueWorkerHandle { pub fn insert_oneshot( &self, call_in: Duration, - on_call: impl Fn(&mut dyn ExpectedInnerTargetMut) + Send + 'static, + on_call: impl Fn(&mut dyn ExpectedInnerTargetMut>) + Send + 'static, ) { self.insert_reserved(None, call_in, move |sess| { (on_call)(sess); @@ -119,7 +126,7 @@ impl SessionQueueWorkerHandle { idx: usize, target_cid: u64, timeout: Duration, - on_timeout: impl Fn(&mut dyn ExpectedInnerTargetMut) -> QueueWorkerResult + on_timeout: impl Fn(&mut dyn ExpectedInnerTargetMut>) -> QueueWorkerResult + Send + 'static, ) { @@ -147,8 +154,8 @@ pub enum QueueWorkerResult { AdjustPeriodicity(Duration), } -impl SessionQueueWorker { - pub fn new(sess_shutdown: Sender<()>) -> (Self, SessionQueueWorkerHandle) { +impl SessionQueueWorker { + pub fn new(sess_shutdown: Sender<()>) -> (Self, SessionQueueWorkerHandle) { let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let handle = SessionQueueWorkerHandle { tx, @@ -169,7 +176,7 @@ impl SessionQueueWorker { } /// MUST be called when a session's timer subroutine begins! - pub fn load_state_container(&mut self, state_container: StateContainer) { + pub fn load_state_container(&mut self, state_container: StateContainer) { self.state_container = Some(state_container); } @@ -179,7 +186,7 @@ impl SessionQueueWorker { &mut self, key_orig: Option, timeout: Duration, - on_timeout: Box, + on_timeout: Box>, ) { // the zero in the default unwrap ensures that the key is going to be unique let key_new = key_orig.unwrap_or(QueueWorkerTicket::Oneshot( @@ -199,7 +206,7 @@ impl SessionQueueWorker { &mut self, key: Option, timeout: Duration, - on_timeout: impl Fn(&mut dyn ExpectedInnerTargetMut) -> QueueWorkerResult + on_timeout: impl Fn(&mut dyn ExpectedInnerTargetMut>) -> QueueWorkerResult + Send + 'static, ) { @@ -228,7 +235,7 @@ impl SessionQueueWorker { } = &mut *self; let mut state_container = inner_mut_state!(state_container.as_ref().unwrap()); - if state_container.state.load(Ordering::Relaxed) != SessionState::Disconnected { + if state_container.state.get() != SessionState::Disconnected { while let Some(res) = futures::ready!(expirations.poll_expired(cx)) { let entry: QueueWorkerTicket = res.into_inner(); //log::trace!(target: "citadel", "POLL_EXPIRED: {:?}", &entry); @@ -284,7 +291,7 @@ impl SessionQueueWorker { } } -impl Stream for SessionQueueWorker { +impl Stream for SessionQueueWorker { // DelayQueue seems much more specific, where a user may care that it // has reached capacity, so return those errors instead of panicking. type Item = (); @@ -309,7 +316,7 @@ impl Stream for SessionQueueWorker { } } -impl futures::Future for SessionQueueWorker { +impl futures::Future for SessionQueueWorker { type Output = Result<(), NetworkError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index c69b2dd7e..402f87b26 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -77,7 +77,7 @@ use std::sync::Arc; use crate::proto::packet_processor::primary_group_packet::{ attempt_kem_as_alice_finish, get_resp_target_cid_from_header, }; -use citadel_crypt::stacked_ratchet::constructor::{ConstructorType, StackedRatchetConstructor}; +use citadel_crypt::sync_toggle::{CurrentToggleState, SyncToggle}; use serde::{Deserialize, Serialize}; use crate::proto::outbound_sender::{unbounded, UnboundedSender}; @@ -96,6 +96,7 @@ use crate::constants::{ use crate::error::NetworkError; use crate::functional::IfEqConditional; use crate::prelude::{InternalServerError, PreSharedKey, ReKeyResult, ReKeyReturnType}; +use crate::proto::misc::dual_cell::DualCell; use crate::proto::misc::dual_late_init::DualLateInit; use crate::proto::misc::dual_rwlock::DualRwLock; use crate::proto::misc::ordered_channel::OrderedChannel; @@ -126,10 +127,11 @@ use crate::proto::state_subcontainers::register_state_container::RegisterState; use crate::proto::state_subcontainers::rekey_container::RatchetUpdateState; use crate::proto::transfer_stats::TransferStats; use crate::proto::{packet_crafter, send_with_error_logging}; -use atomic::Atomic; use bytes::Bytes; -use citadel_crypt::endpoint_crypto_container::{KemTransferStatus, PeerSessionCrypto}; -use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; +use citadel_crypt::endpoint_crypto_container::{ + EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, +}; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecrecyMode; use citadel_types::crypto::SecurityLevel; @@ -144,23 +146,24 @@ use citadel_user::serialization::SyncIO; use either::Either; use std::sync::atomic::{AtomicBool, Ordering}; -impl Debug for StateContainer { +impl Debug for StateContainer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "StateContainer") } } -define_outer_struct_wrapper!(StateContainer, StateContainerInner); +define_outer_struct_wrapper!(StateContainer, StateContainerInner, , ); /// For keeping track of the stages -pub struct StateContainerInner { - pub(super) pre_connect_state: PreConnectState, - pub(super) hdp_server_remote: NodeRemote, +pub struct StateContainerInner { + pub(super) pre_connect_state: PreConnectState, + pub(super) node_remote: NodeRemote, /// No hashmap here, since register is only for a single target - pub(super) register_state: RegisterState, + pub(super) register_state: RegisterState, /// No hashmap here, since connect is only for a single target pub(super) connect_state: ConnectState, - pub(super) ratchet_update_state: RatchetUpdateState, + // TODO: move c2s ratchet updates into here + pub(super) ratchet_update_state: RatchetUpdateState, pub(super) deregister_state: DeRegisterState, pub(super) meta_expiry_state: MetaExpiryState, pub(super) network_stats: NetworkStats, @@ -173,30 +176,30 @@ pub struct StateContainerInner { SecurityLevel, )>, >, - pub(super) updates_in_progress: HashMap>, + pub(super) updates_in_progress: HashMap, pub(super) inbound_files: HashMap, pub(super) outbound_files: HashMap, pub(super) file_transfer_handles: HashMap>, pub(super) inbound_groups: HashMap, - pub(super) outbound_transmitters: HashMap, - pub(super) peer_kem_states: HashMap, + pub(super) outbound_transmitters: HashMap>, + pub(super) peer_kem_states: HashMap>, // u64 is peer id, ticket is the local original ticket (ticket may // transform if a simultaneous connect) pub(super) outgoing_peer_connect_attempts: HashMap, pub(super) udp_primary_outbound_tx: Option, pub(super) kernel_tx: UnboundedSender, - pub(super) active_virtual_connections: HashMap, - pub(super) c2s_channel_container: Option, + pub(super) active_virtual_connections: HashMap>, + pub(super) c2s_channel_container: Option>, pub(crate) keep_alive_timeout_ns: i64, - pub(crate) state: Arc>, + pub(crate) state: DualCell, // whenever a c2s or p2p channel is loaded, this is fired to signal any UDP loaders that it is safe to store the UDP conn in the corresponding v_conn pub(super) tcp_loaded_status: Option>, pub(super) hole_puncher_pipes: HashMap>, - pub(super) cnac: Option, + pub(super) cnac: Option>, pub(super) time_tracker: TimeTracker, pub(super) session_security_settings: Option, - pub(super) queue_handle: DualLateInit, + pub(super) queue_handle: DualLateInit>, pub(super) group_channels: HashMap>, pub(super) transfer_stats: TransferStats, pub(super) udp_mode: UdpMode, @@ -264,7 +267,7 @@ impl FileKey { } /// For keeping track of connections -pub struct VirtualConnection { +pub struct VirtualConnection { /// For determining the type of connection pub connection_type: VirtualConnectionType, pub last_delivered_message_timestamp: DualRwLock>, @@ -275,17 +278,15 @@ pub struct VirtualConnection { pub endpoint_container: Option>, } -impl VirtualConnection { +impl VirtualConnection { /// If No version is supplied, uses the latest committed version - pub fn borrow_endpoint_hyper_ratchet(&self, version: Option) -> Option<&StackedRatchet> { + pub fn borrow_endpoint_stacked_ratchet(&self, version: Option) -> Option<&R> { let endpoint_container = self.endpoint_container.as_ref()?; - endpoint_container - .endpoint_crypto - .get_hyper_ratchet(version) + endpoint_container.endpoint_crypto.get_ratchet(version) } } -pub struct EndpointChannelContainer { +pub struct EndpointChannelContainer { pub(crate) default_security_settings: SessionSecuritySettings, // this is only loaded if STUN-like NAT-traversal works pub(crate) direct_p2p_remote: Option, @@ -298,7 +299,7 @@ pub struct EndpointChannelContainer { pub(crate) file_transfer_compatible: bool, } -pub struct C2SChannelContainer { +pub struct C2SChannelContainer { to_channel: OrderedChannel, // for UDP pub(crate) to_unordered_channel: Option, @@ -313,7 +314,7 @@ pub(crate) struct UnorderedChannelContainer { stopper_tx: citadel_io::tokio::sync::oneshot::Sender<()>, } -impl EndpointChannelContainer { +impl EndpointChannelContainer { pub fn get_direct_p2p_primary_stream(&self) -> Option<&OutboundPrimaryStreamSender> { Some(&self.direct_p2p_remote.as_ref()?.p2p_primary_stream) } @@ -329,19 +330,19 @@ impl Drop for VirtualConnection { #[derive(Eq, PartialEq, Copy, Clone, Debug, Hash, Serialize, Deserialize)] pub enum VirtualConnectionType { LocalGroupPeer { - implicated_cid: u64, + session_cid: u64, peer_cid: u64, }, ExternalGroupPeer { - implicated_cid: u64, + session_cid: u64, interserver_cid: u64, peer_cid: u64, }, LocalGroupServer { - implicated_cid: u64, + session_cid: u64, }, ExternalGroupServer { - implicated_cid: u64, + session_cid: u64, interserver_cid: u64, }, } @@ -360,53 +361,49 @@ impl VirtualConnectionType { /// Gets the target cid, agnostic to type pub fn get_target_cid(&self) -> u64 { match self { - VirtualConnectionType::LocalGroupServer { - implicated_cid: _cid, - } => { + VirtualConnectionType::LocalGroupServer { session_cid: _cid } => { // by rule of the network, the target CID is zero if a hyperlan peer -> hyperlan server conn 0 } VirtualConnectionType::LocalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, peer_cid: target_cid, } => *target_cid, VirtualConnectionType::ExternalGroupPeer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, interserver_cid: _icid, peer_cid: target_cid, } => *target_cid, VirtualConnectionType::ExternalGroupServer { - implicated_cid: _implicated_cid, + session_cid: _session_cid, interserver_cid: icid, } => *icid, } } /// Gets the target cid, agnostic to type - pub fn get_implicated_cid(&self) -> u64 { + pub fn get_session_cid(&self) -> u64 { match self { - VirtualConnectionType::LocalGroupServer { - implicated_cid: cid, - } => *cid, + VirtualConnectionType::LocalGroupServer { session_cid: cid } => *cid, VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: _target_cid, - } => *implicated_cid, + } => *session_cid, VirtualConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: _icid, peer_cid: _target_cid, - } => *implicated_cid, + } => *session_cid, VirtualConnectionType::ExternalGroupServer { - implicated_cid, + session_cid, interserver_cid: _icid, - } => *implicated_cid, + } => *session_cid, } } @@ -425,19 +422,19 @@ impl VirtualConnectionType { pub fn try_as_peer_connection(&self) -> Option { match self { VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid, } => Some(PeerConnectionType::LocalGroupPeer { - implicated_cid: *implicated_cid, + session_cid: *session_cid, peer_cid: *peer_cid, }), VirtualConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: icid, peer_cid, } => Some(PeerConnectionType::ExternalGroupPeer { - implicated_cid: *implicated_cid, + session_cid: *session_cid, interserver_cid: *icid, peer_cid: *peer_cid, }), @@ -449,11 +446,11 @@ impl VirtualConnectionType { pub fn set_target_cid(&mut self, target_cid: u64) { match self { VirtualConnectionType::LocalGroupPeer { - implicated_cid: _, + session_cid: _, peer_cid, } | VirtualConnectionType::ExternalGroupPeer { - implicated_cid: _, + session_cid: _, interserver_cid: _, peer_cid, } => *peer_cid = target_cid, @@ -462,17 +459,17 @@ impl VirtualConnectionType { } } - pub fn set_implicated_cid(&mut self, cid: u64) { + pub fn set_session_cid(&mut self, cid: u64) { match self { VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: _, } | VirtualConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: _, peer_cid: _, - } => *implicated_cid = cid, + } => *session_cid = cid, _ => {} } @@ -482,40 +479,38 @@ impl VirtualConnectionType { impl Display for VirtualConnectionType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - VirtualConnectionType::LocalGroupServer { - implicated_cid: cid, - } => { + VirtualConnectionType::LocalGroupServer { session_cid: cid } => { write!(f, "Local Group Peer to Local Group Server ({cid})") } VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => { write!( f, - "Local Group Peer to Local Group Peer ({implicated_cid} -> {target_cid})" + "Local Group Peer to Local Group Peer ({session_cid} -> {target_cid})" ) } VirtualConnectionType::ExternalGroupPeer { - implicated_cid, + session_cid, interserver_cid: icid, peer_cid: target_cid, } => { write!( f, - "Local Group Peer to External Group Peer ({implicated_cid} -> {icid} -> {target_cid})" + "Local Group Peer to External Group Peer ({session_cid} -> {icid} -> {target_cid})" ) } VirtualConnectionType::ExternalGroupServer { - implicated_cid, + session_cid, interserver_cid: icid, } => { write!( f, - "Local Group Peer to External Group Server ({implicated_cid} -> {icid})" + "Local Group Peer to External Group Server ({session_cid} -> {icid})" ) } } @@ -530,11 +525,9 @@ pub(super) struct NetworkStats { pub(super) rtt_ns: Option, } -//define_outer_struct_wrapper!(GroupSender, GroupSenderDevice); - -pub(crate) struct OutboundTransmitterContainer { - ratchet_constructor: Option, - pub(crate) burst_transmitter: GroupTransmitter, +pub(crate) struct OutboundTransmitterContainer { + ratchet_constructor: Option, + pub(crate) burst_transmitter: GroupTransmitter, // in the case of file transfers, it is desirable to wake-up the async task // that enqueues the next group object_notifier: Option>, @@ -548,19 +541,16 @@ pub(crate) struct OutboundTransmitterContainer { pub has_begun: bool, } -impl OutboundTransmitterContainer { +impl OutboundTransmitterContainer { pub fn new( object_notifier: Option>, - mut burst_transmitter: GroupTransmitter, + mut burst_transmitter: GroupTransmitter, group_plaintext_length: usize, parent_object_total_groups: usize, relative_group_id: u32, ticket: Ticket, ) -> Self { - let ratchet_constructor = burst_transmitter - .hyper_ratchet_container - .base_constructor - .take(); + let ratchet_constructor = burst_transmitter.ratchet_container.base_constructor.take(); let transmission_start_time = Instant::now(); let has_begun = false; @@ -623,21 +613,21 @@ impl GroupReceiverContainer { } } -impl StateContainerInner { +impl StateContainerInner { /// Creates a new container #[allow(clippy::too_many_arguments)] pub fn create( kernel_tx: UnboundedSender, - hdp_server_remote: NodeRemote, + hdp_server_remote: NodeRemote, keep_alive_timeout_ns: i64, - state: Arc>, - cnac: Option, + state: DualCell, + cnac: Option>, time_tracker: TimeTracker, session_security_settings: Option, is_server: bool, transfer_stats: TransferStats, udp_mode: UdpMode, - ) -> StateContainer { + ) -> StateContainer { let inner = Self { outgoing_peer_connect_attempts: Default::default(), file_transfer_handles: HashMap::new(), @@ -656,7 +646,7 @@ impl StateContainerInner { state, c2s_channel_container: None, keep_alive_timeout_ns, - hdp_server_remote, + node_remote: hdp_server_remote, meta_expiry_state: Default::default(), pre_connect_state: Default::default(), udp_primary_outbound_tx: None, @@ -695,8 +685,8 @@ impl StateContainerInner { /// Attempts to find the direct p2p stream. If not found, will use the default /// to_server stream. Note: the underlying crypto is still the same pub fn get_preferred_stream(&self, peer_cid: u64) -> &OutboundPrimaryStreamSender { - fn get_inner( - this: &StateContainerInner, + fn get_inner( + this: &StateContainerInner, peer_cid: u64, ) -> Option<&OutboundPrimaryStreamSender> { Some( @@ -785,7 +775,7 @@ impl StateContainerInner { v_conn, ticket, c2s_container.is_active.clone(), - self.hdp_server_remote.clone(), + self.node_remote.clone(), ); c2s_container.to_unordered_channel = Some(UnorderedChannelContainer { to_channel, @@ -808,7 +798,7 @@ impl StateContainerInner { v_conn, ticket, p2p_container.is_active.clone(), - self.hdp_server_remote.clone(), + self.node_remote.clone(), ); p2p_endpoint_container.to_unordered_channel = Some(UnorderedChannelContainer { to_channel, @@ -889,8 +879,8 @@ impl StateContainerInner { channel_ticket: Ticket, target_cid: u64, connection_type: VirtualConnectionType, - endpoint_crypto: PeerSessionCrypto, - sess: &CitadelSession, + endpoint_crypto: PeerSessionCrypto, + sess: &CitadelSession, file_transfer_compatible: bool, ) -> PeerChannel { let (channel_tx, channel_rx) = unbounded(); @@ -902,7 +892,7 @@ impl StateContainerInner { //let (tx, rx) = futures::channel::mpsc::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); let peer_channel = PeerChannel::new( - self.hdp_server_remote.clone(), + self.node_remote.clone(), target_cid, connection_type, channel_ticket, @@ -942,19 +932,19 @@ impl StateContainerInner { #[allow(unused_results)] pub fn init_new_c2s_virtual_connection( &mut self, - cnac: &ClientNetworkAccount, + cnac: &ClientNetworkAccount, security_level: SecurityLevel, channel_ticket: Ticket, - implicated_cid: u64, - session: &CitadelSession, + session_cid: u64, + session: &CitadelSession, ) -> PeerChannel { let (channel_tx, channel_rx) = unbounded(); let (tx, rx) = crate::proto::outbound_sender::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); let is_active = Arc::new(AtomicBool::new(true)); let peer_channel = PeerChannel::new( - self.hdp_server_remote.clone(), - implicated_cid, - VirtualConnectionType::LocalGroupServer { implicated_cid }, + self.node_remote.clone(), + session_cid, + VirtualConnectionType::LocalGroupServer { session_cid }, channel_ticket, security_level, is_active.clone(), @@ -1015,10 +1005,10 @@ impl StateContainerInner { log::warn!(target: "citadel", "Inserted a virtual connection. but overwrote one in the process. Report to developers"); } - log::trace!(target: "citadel", "Vconn {} -> {} established", connection_type.get_implicated_cid(), target_cid); + log::trace!(target: "citadel", "Vconn {} -> {} established", connection_type.get_session_cid(), target_cid); } - pub fn get_peer_session_crypto(&self, peer_cid: u64) -> Option<&PeerSessionCrypto> { + pub fn get_peer_session_crypto(&self, peer_cid: u64) -> Option<&PeerSessionCrypto> { Some( &self .active_virtual_connections @@ -1032,7 +1022,7 @@ impl StateContainerInner { pub fn get_peer_endpoint_container_mut( &mut self, target_cid: u64, - ) -> Result<&mut EndpointChannelContainer, NetworkError> { + ) -> Result<&mut EndpointChannelContainer, NetworkError> { if let Some(vconn) = self.active_virtual_connections.get_mut(&target_cid) { if let Some(endpoint_container) = vconn.endpoint_container.as_mut() { Ok(endpoint_container) @@ -1048,7 +1038,7 @@ impl StateContainerInner { } } - pub fn get_c2s_crypto(&self) -> Option<&PeerSessionCrypto> { + pub fn get_c2s_crypto(&self) -> Option<&PeerSessionCrypto> { Some(&self.c2s_channel_container.as_ref()?.peer_session_crypto) } @@ -1172,28 +1162,28 @@ impl StateContainerInner { /// This creates an entry in the inbound_files hashmap #[allow(unused_results)] #[allow(clippy::too_many_arguments)] - pub fn on_file_header_received( + pub fn on_file_header_received( &mut self, header: &Ref<&[u8], HdpHeader>, virtual_target: VirtualTargetType, metadata_orig: VirtualObjectMetadata, - pers: &PersistenceHandler, - state_container: StateContainer, - hyper_ratchet: StackedRatchet, + pers: &PersistenceHandler, + state_container: StateContainer, + stacked_ratchet: R, _target_cid: u64, v_target_flipped: VirtualTargetType, preferred_primary_stream: OutboundPrimaryStreamSender, local_encryption_level: Option, ) -> bool { let target_cid = v_target_flipped.get_target_cid(); - let implicated_cid = v_target_flipped.get_implicated_cid(); + let session_cid = v_target_flipped.get_session_cid(); let object_id = metadata_orig.object_id; let key = FileKey::new(object_id); let ticket = header.context_info.get().into(); let is_revfs_pull = local_encryption_level.is_some(); - log::trace!(target: "citadel", "File header {implicated_cid}: {key:?} | revfs_pull: {is_revfs_pull}"); + log::trace!(target: "citadel", "File header {session_cid}: {key:?} | revfs_pull: {is_revfs_pull}"); if let std::collections::hash_map::Entry::Vacant(e) = self.inbound_files.entry(key) { let (stream_to_hd, stream_to_hd_rx) = unbounded::>(); @@ -1229,7 +1219,7 @@ impl StateContainerInner { e.insert(entry); let (handle, tx_status) = ObjectTransferHandler::new( target_cid, - implicated_cid, + session_cid, metadata.clone(), ObjectTransferOrientation::Receiver { is_revfs_pull }, start_recv_tx, @@ -1242,7 +1232,7 @@ impl StateContainerInner { .unbounded_send(NodeResult::ObjectTransferHandle(ObjectTransferHandle { ticket, handle, - implicated_cid, + session_cid, })) { log::error!(target: "citadel", "Failed to send the ObjectTransferHandle to the kernel: {err:?}"); @@ -1264,7 +1254,7 @@ impl StateContainerInner { // first, send a rebound signal immediately to the sender // to ensure the sender knows if the user accepted or not let file_header_ack = packet_crafter::file::craft_file_header_ack_packet( - &hyper_ratchet, + &stacked_ratchet, accepted, object_id, target_cid, @@ -1301,7 +1291,7 @@ impl StateContainerInner { Ok(header) => { // write the header let wave_ack = packet_crafter::group::craft_wave_ack( - &hyper_ratchet, + &stacked_ratchet, object_id, get_resp_target_cid_from_header(&header), header.group.get(), @@ -1350,7 +1340,7 @@ impl StateContainerInner { Err(err) => { log::error!(target: "citadel", "Start_recv_rx failed: {:?}", err); let err_packet = packet_crafter::file::craft_file_header_ack_packet( - &hyper_ratchet, + &stacked_ratchet, false, object_id, target_cid, @@ -1376,7 +1366,7 @@ impl StateContainerInner { pub fn on_file_header_ack_received( &mut self, success: bool, - implicated_cid: u64, + session_cid: u64, ticket: Ticket, object_id: ObjectId, v_target: VirtualTargetType, @@ -1384,16 +1374,16 @@ impl StateContainerInner { ) -> Option<()> { let (key, receiver_cid) = match v_target { VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: _target_cid, } => { - let receiver_cid = implicated_cid; + let receiver_cid = session_cid; // since the order hasn't flipped yet, get the implicated cid (FileKey::new(object_id), receiver_cid) } - VirtualConnectionType::LocalGroupServer { implicated_cid } => { - (FileKey::new(object_id), implicated_cid) + VirtualConnectionType::LocalGroupServer { session_cid } => { + (FileKey::new(object_id), session_cid) } _ => { @@ -1409,7 +1399,7 @@ impl StateContainerInner { // start the async task pulling from the async cryptscrambler file_transfer.start.take()?.send(true).ok()?; let (handle, tx) = ObjectTransferHandler::new( - implicated_cid, + session_cid, receiver_cid, metadata, ObjectTransferOrientation::Sender, @@ -1424,7 +1414,7 @@ impl StateContainerInner { .unbounded_send(NodeResult::ObjectTransferHandle(ObjectTransferHandle { ticket, handle, - implicated_cid, + session_cid, })) .ok()?; } else { @@ -1443,7 +1433,7 @@ impl StateContainerInner { message: "The adjacent node did not accept the file transfer request" .into(), ticket_opt: Some(ticket), - cid_opt: Some(implicated_cid), + cid_opt: Some(session_cid), })); } else { log::error!(target: "citadel", "Attempted to remove OutboundFileTransfer for {:?}, but it didn't exist", key); @@ -1463,24 +1453,21 @@ impl StateContainerInner { #[allow(clippy::too_many_arguments)] pub fn on_group_header_ack_received( &mut self, - session: &CitadelSession, + session: &CitadelSession, base_session_secrecy_mode: SecrecyMode, peer_cid: u64, target_cid: u64, group_id: u64, object_id: ObjectId, next_window: Option>, - transfer: KemTransferStatus, + transfer: KemTransferStatus, fast_msg: bool, ) -> bool { let key = GroupKey::new(peer_cid, group_id, object_id); let constructor = if let Some(outbound_container) = self.outbound_transmitters.get_mut(&key) { - outbound_container - .ratchet_constructor - .take() - .map(ConstructorType::Default) + outbound_container.ratchet_constructor.take() } else { log::warn!(target: "citadel", "Key for outbound transmitter absent"); return false; @@ -1550,7 +1537,7 @@ impl StateContainerInner { &mut self, header: &HdpHeader, payload: Bytes, - hr: &StackedRatchet, + hr: &R, ) -> Result { let target_cid = header.session_cid.get(); let group_id = header.group.get(); @@ -1606,7 +1593,7 @@ impl StateContainerInner { header.wave_id.get(), src as u16, dest as u16, - hr.get_scramble_drill(), + hr.get_scramble_pqc_and_entropy_bank().1, ) .ok_or(( NetworkError::InvalidRequest("Unable to obtain true_sequence"), @@ -1637,7 +1624,7 @@ impl StateContainerInner { .cnac .as_ref() .unwrap() - .get_static_auxiliary_hyper_ratchet(); + .get_static_auxiliary_stacked_ratchet(); chunk = static_aux_hr .local_decrypt(chunk, local_encryption_level) @@ -1740,7 +1727,7 @@ impl StateContainerInner { #[allow(unused_results)] pub fn on_wave_ack_received( &mut self, - implicated_cid: u64, + session_cid: u64, header: &Ref<&[u8], HdpHeader>, ) -> bool { let object_id = header.context_info.get().into(); @@ -1790,7 +1777,7 @@ impl StateContainerInner { ObjectTransferStatus::TransferComplete }; - log::trace!(target: "citadel", "Transmitter {implicated_cid}: {file_key:?} received final wave ack. Sending status to local node: {:?}", status); + log::trace!(target: "citadel", "Transmitter {session_cid}: {file_key:?} received final wave ack. Sending status to local node: {:?}", status); if let Err(err) = tx.unbounded_send(status.clone()) { // if the server is using an accept-only policy with no further responses, this branch // will be reached @@ -1805,7 +1792,7 @@ impl StateContainerInner { let _ = self.file_transfer_handles.remove(&file_key); } } else { - log::error!(target: "citadel", "Unable to find ObjectTransferHandle for {:?} | Local is {implicated_cid} | FileKeys available: {:?}", file_key, self.file_transfer_handles.keys().copied().collect::>()); + log::error!(target: "citadel", "Unable to find ObjectTransferHandle for {:?} | Local is {session_cid} | FileKeys available: {:?}", file_key, self.file_transfer_handles.keys().copied().collect::>()); } delete_group = true; @@ -1874,7 +1861,7 @@ impl StateContainerInner { let update_in_progress = self .updates_in_progress .get(&target_cid) - .map(|r| r.fetch_nand(false, Ordering::SeqCst)) + .map(|r| r.toggle_on_if_untoggled() == CurrentToggleState::AlreadyToggled) .ok_or(NetworkError::InternalError( "Update state not loaded in hashmap!", ))?; @@ -1941,7 +1928,7 @@ impl StateContainerInner { ) -> Result<(), NetworkError> { let this = self; - if this.state.load(Ordering::Relaxed) != SessionState::Connected { + if !this.state.is_connected() { Err(NetworkError::Generic(format!( "Attempted to send data (ticket: {ticket}) outbound, but the session is not connected" ))) @@ -1959,7 +1946,7 @@ impl StateContainerInner { || this .updates_in_progress .get(&virtual_target.get_target_cid()) - .map(|r| r.load(Ordering::SeqCst)) + .map(|r| r.state() == CurrentToggleState::AlreadyToggled) .ok_or({ NetworkError::InternalError("Update in progress not loaded for client") })? @@ -1982,30 +1969,35 @@ impl StateContainerInner { // Drop this to ensure that it doesn't block other async closures from accessing the inner device // std::mem::drop(this); let (mut transmitter, group_id, target_cid) = match virtual_target { - VirtualTargetType::LocalGroupServer { implicated_cid } => { + VirtualTargetType::LocalGroupServer { session_cid } => { // if we are sending this just to the HyperLAN server (in the case of file uploads), - // then, we use this session's pqc, the cnac's latest drill, and 0 for target_cid + // then, we use this session's pqc, the cnac's latest entropy_bank, and 0 for target_cid let crypt_container = &mut this .c2s_channel_container .as_mut() .unwrap() .peer_session_crypto; - let latest_hyper_ratchet = - crypt_container.get_hyper_ratchet(None).cloned().unwrap(); - latest_hyper_ratchet.verify_level(Some(security_level)).map_err(|_err| NetworkError::Generic(format!("Invalid security level. The maximum security level for this session is {:?}", latest_hyper_ratchet.get_default_security_level())))?; + let latest_stacked_ratchet = + crypt_container.get_ratchet(None).cloned().unwrap(); + latest_stacked_ratchet.verify_level(Some(security_level)).map_err(|_err| NetworkError::Generic(format!("Invalid security level. The maximum security level for this session is {:?}", latest_stacked_ratchet.get_default_security_level())))?; let constructor = crypt_container.get_next_constructor(called_from_poll); let result = match secrecy_mode { SecrecyMode::BestEffort => { let group_id = crypt_container.get_and_increment_group_id(); - Either::Left((constructor, latest_hyper_ratchet, group_id, packet)) + Either::Left((constructor, latest_stacked_ratchet, group_id, packet)) } SecrecyMode::Perfect => { if constructor.is_some() { // we can perform a kex let group_id = crypt_container.get_and_increment_group_id(); - Either::Left((constructor, latest_hyper_ratchet, group_id, packet)) + Either::Left(( + constructor, + latest_stacked_ratchet, + group_id, + packet, + )) } else { // kex later Either::Right(packet) @@ -2016,7 +2008,7 @@ impl StateContainerInner { match result { Either::Left(( alice_constructor, - latest_hyper_ratchet, + latest_stacked_ratchet, group_id, packet, )) => { @@ -2026,7 +2018,7 @@ impl StateContainerInner { to_primary_stream, OBJECT_SINGLETON, RatchetPacketCrafterContainer::new( - latest_hyper_ratchet, + latest_stacked_ratchet, alice_constructor, ), packet, @@ -2041,7 +2033,7 @@ impl StateContainerInner { ) })?, group_id, - implicated_cid, + session_cid, ) } @@ -2061,10 +2053,10 @@ impl StateContainerInner { } VirtualConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: target_cid, } => { - log::trace!(target: "citadel", "Maybe sending HyperLAN peer ({}) <-> HyperLAN Peer ({})", implicated_cid, target_cid); + log::trace!(target: "citadel", "Maybe sending HyperLAN peer ({}) <-> HyperLAN Peer ({})", session_cid, target_cid); // here, we don't use the base session's PQC. Instead, we use the vconn's pqc and Toolset let default_primary_stream = this.get_primary_stream().cloned().unwrap(); @@ -2083,7 +2075,7 @@ impl StateContainerInner { //let to_primary_stream_preferred = this.to_primary_stream.clone().unwrap(); let latest_usable_ratchet = endpoint_container .endpoint_crypto - .get_hyper_ratchet(None) + .get_ratchet(None) .unwrap() .clone(); latest_usable_ratchet.verify_level(Some(security_level)).map_err(|_err| NetworkError::Generic(format!("Invalid security level. The maximum security level for this session is {:?}", latest_usable_ratchet.get_default_security_level())))?; @@ -2201,7 +2193,7 @@ impl StateContainerInner { let outbound_container = OutboundTransmitterContainer::new(None, transmitter, group_len, 1, 0, ticket); // The payload packets won't be sent until a GROUP_HEADER_ACK is received - // NOTE: Ever since using GroupKeys, we use either the implicated_cid (for client -> server conns) or target_cids (for peer conns) + // NOTE: Ever since using GroupKeys, we use either the session_cid (for client -> server conns) or target_cids (for peer conns) let key = GroupKey::new(target_cid, group_id, object_id); //inner_mut!(this.state_container).outbound_transmitters.insert(key, outbound_container); this.outbound_transmitters.insert(key, outbound_container); @@ -2234,7 +2226,7 @@ impl StateContainerInner { } #[allow(unused_results)] - pub(crate) fn initiate_drill_update( + pub(crate) fn initiate_entropy_bank_update( &mut self, timestamp: i64, virtual_target: VirtualTargetType, @@ -2243,23 +2235,18 @@ impl StateContainerInner { fn return_already_in_progress( kernel_tx: &UnboundedSender, ticket: Ticket, - implicated_cid: u64, + session_cid: u64, ) -> Result<(), NetworkError> { kernel_tx .unbounded_send(NodeResult::ReKeyResult(ReKeyResult { ticket, status: ReKeyReturnType::AlreadyInProgress, - implicated_cid, + session_cid, })) .map_err(|err| NetworkError::Generic(err.to_string())) } - /*if !self.meta_expiry_state.expired() { - log::trace!(target: "citadel", "Rekey will be omitted since packets are being sent"); - return Ok(()); - }*/ - - if self.state.load(Ordering::Relaxed) != SessionState::Connected { + if !self.state.is_connected() { return Err(NetworkError::InvalidRequest( "Cannot initiate rekey since the session is not connected", )); @@ -2273,7 +2260,7 @@ impl StateContainerInner { .ok_or(NetworkError::InternalError("Primary stream not loaded"))?); match virtual_target { - VirtualConnectionType::LocalGroupServer { implicated_cid } => { + VirtualConnectionType::LocalGroupServer { session_cid } => { let crypt_container = &mut self .c2s_channel_container .as_mut() @@ -2282,8 +2269,8 @@ impl StateContainerInner { match crypt_container.get_next_constructor(false) { Some(alice_constructor) => { - let ratchet = crypt_container.get_hyper_ratchet(None).unwrap(); - let stage0_packet = packet_crafter::do_drill_update::craft_stage0( + let ratchet = crypt_container.get_ratchet(None).unwrap(); + let stage0_packet = packet_crafter::do_entropy_bank_update::craft_stage0( ratchet, alice_constructor .stage0_alice() @@ -2292,7 +2279,7 @@ impl StateContainerInner { C2S_ENCRYPTION_ONLY, security_level, ); - self.ratchet_update_state.alice_hyper_ratchet = Some(alice_constructor); + self.ratchet_update_state.alice_stacked_ratchet = Some(alice_constructor); if let Some(ticket) = ticket { // this request requires tracking let _ = self @@ -2303,12 +2290,12 @@ impl StateContainerInner { let to_primary_stream = self.get_primary_stream().unwrap(); let kernel_tx = &self.kernel_tx; - CitadelSession::send_to_primary_stream_closure( + CitadelSession::::send_to_primary_stream_closure( to_primary_stream, kernel_tx, stage0_packet, ticket, - Some(implicated_cid), + Some(session_cid), ) } @@ -2318,7 +2305,7 @@ impl StateContainerInner { return_already_in_progress( &self.kernel_tx, ticket, - virtual_target.get_implicated_cid(), + virtual_target.get_session_cid(), ) } else { Ok(()) @@ -2328,7 +2315,7 @@ impl StateContainerInner { } VirtualConnectionType::LocalGroupPeer { - implicated_cid: _, + session_cid: _, peer_cid, } => { const MISSING: NetworkError = NetworkError::InvalidRequest("Peer not connected"); @@ -2341,8 +2328,8 @@ impl StateContainerInner { .ok_or(MISSING)?; let crypt = &mut endpoint_container.endpoint_crypto; let alice_constructor = crypt.get_next_constructor(false); - let latest_hyper_ratchet = crypt - .get_hyper_ratchet(None) + let latest_stacked_ratchet = crypt + .get_ratchet(None) .cloned() .ok_or(NetworkError::InternalError("Ratchet not loaded"))?; @@ -2352,8 +2339,8 @@ impl StateContainerInner { .get_direct_p2p_primary_stream() .unwrap_or(default_primary_stream); let stage0_packet = - packet_crafter::do_drill_update::craft_stage0( - &latest_hyper_ratchet, + packet_crafter::do_entropy_bank_update::craft_stage0( + &latest_stacked_ratchet, alice_constructor.stage0_alice().ok_or( NetworkError::InternalError("Alice constructor (2) failed"), )?, @@ -2376,14 +2363,12 @@ impl StateContainerInner { } if let Some(ticket) = ticket { - // this request requires tracking let _ = self .ratchet_update_state .current_local_requests .insert(virtual_target, ticket); } - // to_primary_stream_preferred.unbounded_send(stage0_packet).map_err(|err| NetworkError::Generic(err.to_string())) Ok(()) } @@ -2393,7 +2378,7 @@ impl StateContainerInner { return_already_in_progress( &self.kernel_tx, ticket, - virtual_target.get_implicated_cid(), + virtual_target.get_session_cid(), ) } else { Ok(()) @@ -2411,15 +2396,15 @@ impl StateContainerInner { ticket: Ticket, command: &GroupBroadcast, ) -> Result<(), NetworkError> { - if self.state.load(Ordering::Relaxed) != SessionState::Connected { + if !self.state.is_connected() { log::warn!(target: "citadel", "Unable to execute group command since session is not connected"); return Ok(()); } - let hyper_ratchet = self + let stacked_ratchet = self .get_c2s_crypto() .ok_or(NetworkError::InternalError("C2s not loaded"))? - .get_hyper_ratchet(None) + .get_ratchet(None) .unwrap(); let security_level = self .session_security_settings @@ -2440,7 +2425,7 @@ impl StateContainerInner { | GroupBroadcast::ListGroupsFor { .. } | GroupBroadcast::LeaveRoom { .. } => { packet_crafter::peer_cmd::craft_group_message_packet( - hyper_ratchet, + stacked_ratchet, command, ticket, C2S_ENCRYPTION_ONLY, @@ -2466,10 +2451,10 @@ impl StateContainerInner { &mut self, key: MessageGroupKey, ticket: Ticket, - session: &CitadelSession, + session: &CitadelSession, ) -> Result { let (tx, rx) = unbounded(); - let implicated_cid = self + let session_cid = self .cnac .as_ref() .map(|r| r.get_cid()) @@ -2489,11 +2474,10 @@ impl StateContainerInner { CitadelSession::spawn_message_sender_function(session.clone(), to_session_rx); Ok(GroupChannel::new( - self.hdp_server_remote.clone(), to_session_tx, key, ticket, - implicated_cid, + session_cid, rx, )) } diff --git a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs index 104ccd066..bacc53747 100644 --- a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs @@ -52,18 +52,18 @@ if state.local_is_initiator { use crate::prelude::PreSharedKey; use crate::proto::state_subcontainers::preconnect_state_container::UdpChannelSender; -use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::proto::SessionSecuritySettings; -pub struct PeerKemStateContainer { - pub(crate) constructor: Option, +pub struct PeerKemStateContainer { + pub(crate) constructor: Option, pub(crate) local_is_initiator: bool, pub(crate) session_security_settings: SessionSecuritySettings, pub(crate) udp_channel_sender: UdpChannelSender, pub(crate) session_password: PreSharedKey, } -impl PeerKemStateContainer { +impl PeerKemStateContainer { pub fn new( session_security_settings: SessionSecuritySettings, udp_enabled: bool, diff --git a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs index 960962e6c..7a60f3763 100644 --- a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs @@ -54,32 +54,31 @@ if let Some(ratchet) = state.generated_ratchet { use crate::proto::packet_processor::includes::Instant; use crate::proto::peer::channel::UdpChannel; use crate::proto::remote::Ticket; -use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_io::tokio::sync::oneshot::{channel, Receiver, Sender}; use citadel_wire::hypernode_type::NodeType; /// For keeping track of the pre-connect state -pub struct PreConnectState { +pub struct PreConnectState { pub(crate) last_stage: u8, #[allow(dead_code)] pub(crate) adjacent_node_type: Option, - // This drill should be turned .into() the next toolset once the other side updated - pub(crate) constructor: Option, + // This entropy_bank should be turned .into() the next toolset once the other side updated + pub(crate) constructor: Option, pub(crate) ticket: Option, pub(crate) last_packet_time: Option, pub(crate) udp_channel_oneshot_tx: UdpChannelSender, pub(crate) success: bool, - pub(crate) generated_ratchet: Option, + pub(crate) generated_ratchet: Option, } -impl PreConnectState { +impl PreConnectState { pub fn on_packet_received(&mut self) { self.last_packet_time = Some(Instant::now()); } } -impl Default for PreConnectState { +impl Default for PreConnectState { fn default() -> Self { Self { generated_ratchet: None, diff --git a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs index b82cf4e55..9400da0ba 100644 --- a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs @@ -50,20 +50,30 @@ let stage_state = RegisterState::from(stage_number); use citadel_io::tokio::time::Instant; use crate::proto::packet::packet_flags; -use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::stacked_ratchet::Ratchet; /// These values should correlate directly to the packet_flags::cmd::aux::do_register::* -#[derive(Default)] -pub struct RegisterState { +pub struct RegisterState { pub(crate) last_stage: u8, - pub(crate) constructor: Option, - pub(crate) created_hyper_ratchet: Option, + pub(crate) constructor: Option, + pub(crate) created_stacked_ratchet: Option, pub(crate) last_packet_time: Option, pub(crate) passwordless: Option, } -impl RegisterState { +impl Default for RegisterState { + fn default() -> Self { + Self { + last_stage: 0, + constructor: None, + created_stacked_ratchet: None, + last_packet_time: None, + passwordless: None, + } + } +} + +impl RegisterState { /// When the registration stage fails along any step, call this closure pub fn on_fail(&mut self) { self.last_stage = packet_flags::cmd::aux::do_register::FAILURE; @@ -77,7 +87,7 @@ impl RegisterState { } } -impl From for RegisterState { +impl From for RegisterState { fn from(stage: u8) -> Self { Self { last_stage: stage, diff --git a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs index 154b87c3b..48312e88c 100644 --- a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs @@ -47,19 +47,28 @@ use crate::error::NetworkError; use crate::prelude::{NodeResult, ReKeyResult, ReKeyReturnType, Ticket, VirtualTargetType}; use crate::proto::outbound_sender::UnboundedSender; use crate::proto::transfer_stats::TransferStats; -use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; +use citadel_crypt::stacked_ratchet::Ratchet; use std::collections::HashMap; -#[derive(Default)] -pub struct RatchetUpdateState { - pub alice_hyper_ratchet: Option, - pub p2p_updates: HashMap, +pub struct RatchetUpdateState { + pub alice_stacked_ratchet: Option, + pub p2p_updates: HashMap, // if this is present (in the case of manual mode), an alert will be sent // to the kernel once the re-key has finished pub current_local_requests: HashMap, } -impl RatchetUpdateState { +impl Default for RatchetUpdateState { + fn default() -> Self { + Self { + alice_stacked_ratchet: None, + p2p_updates: HashMap::new(), + current_local_requests: HashMap::new(), + } + } +} + +impl RatchetUpdateState { pub(crate) fn on_complete( &mut self, v_conn_type: VirtualTargetType, @@ -71,7 +80,7 @@ impl RatchetUpdateState { .unbounded_send(NodeResult::ReKeyResult(ReKeyResult { ticket, status, - implicated_cid: v_conn_type.get_implicated_cid(), + session_cid: v_conn_type.get_session_cid(), })) .map_err(|err| NetworkError::Generic(err.to_string())) } else { diff --git a/citadel_proto/src/proto/validation.rs b/citadel_proto/src/proto/validation.rs index 8b6cece6b..d816b965e 100644 --- a/citadel_proto/src/proto/validation.rs +++ b/citadel_proto/src/proto/validation.rs @@ -31,6 +31,7 @@ //! - [`crypto`]: Cryptographic operations //! pub(crate) mod do_connect { + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_user::client_account::ClientNetworkAccount; use crate::error::NetworkError; @@ -40,8 +41,8 @@ pub(crate) mod do_connect { use citadel_user::serialization::SyncIO; /// Here, Bob receives a payload of the encrypted username + password. We must verify the login data is valid - pub(crate) async fn validate_stage0_packet( - cnac: &ClientNetworkAccount, + pub(crate) async fn validate_stage0_packet( + cnac: &ClientNetworkAccount, payload: &[u8], ) -> Result { // Now, validate the username and password. The payload is already decrypted @@ -71,9 +72,8 @@ pub(crate) mod group { use crate::proto::packet_crafter::SecureProtocolPacket; use crate::proto::state_container::VirtualTargetType; - use citadel_crypt::endpoint_crypto_container::KemTransferStatus; - use citadel_crypt::stacked_ratchet::constructor::AliceToBobTransfer; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ObjectId; @@ -81,13 +81,13 @@ pub(crate) mod group { use serde::{Deserialize, Serialize}; /// First-pass validation. Ensures header integrity through AAD-services in AES-GCM - pub(crate) fn validate<'a, 'b: 'a>( - hyper_ratchet: &StackedRatchet, + pub(crate) fn validate<'a, 'b: 'a, R: Ratchet>( + stacked_ratchet: &R, security_level: SecurityLevel, header: &'b [u8], mut payload: BytesMut, ) -> Option { - hyper_ratchet + stacked_ratchet .validate_message_packet_in_place_split(Some(security_level), header, &mut payload) .ok()?; Some(payload.freeze()) @@ -115,9 +115,14 @@ pub(crate) mod group { Some(group_header) } - pub(crate) fn validate_message( + #[allow(clippy::type_complexity)] + pub(crate) fn validate_message( payload_orig: &mut BytesMut, - ) -> Option<(SecBuffer, Option, ObjectId)> { + ) -> Option<( + SecBuffer, + Option<>::AliceToBobWireTransfer>, + ObjectId, + )> { // Safely check that there are 8 bytes in length, then, split at the end - 8 if payload_orig.len() < std::mem::size_of::() { return None; @@ -132,11 +137,12 @@ pub(crate) mod group { #[derive(Serialize, Deserialize)] #[allow(variant_size_differences)] - pub enum GroupHeaderAck { + pub enum GroupHeaderAck { ReadyToReceive { fast_msg: bool, initial_window: Option>, - transfer: KemTransferStatus, + #[serde(bound = "")] + transfer: KemTransferStatus, object_id: ObjectId, }, NotReady { @@ -146,7 +152,7 @@ pub(crate) mod group { } /// Returns None if the packet is invalid. Returns Some(is_ready_to_accept) if the packet is valid - pub(crate) fn validate_header_ack(payload: &[u8]) -> Option { + pub(crate) fn validate_header_ack(payload: &[u8]) -> Option> { GroupHeaderAck::deserialize_from_vector(payload).ok() } @@ -168,26 +174,31 @@ pub(crate) mod do_register { use crate::proto::packet::HdpHeader; use crate::proto::packet_crafter::do_register::{DoRegisterStage0, DoRegisterStage2Packet}; use bytes::BytesMut; - use citadel_crypt::stacked_ratchet::constructor::AliceToBobTransfer; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_user::prelude::ConnectionInfo; use citadel_user::serialization::SyncIO; - pub(crate) fn validate_stage0(payload: &[u8]) -> Option<(AliceToBobTransfer, bool)> { - DoRegisterStage0::deserialize_from_vector(payload) + pub(crate) fn validate_stage0( + payload: &[u8], + ) -> Option<( + >::AliceToBobWireTransfer, + bool, + )> { + DoRegisterStage0::::deserialize_from_vector(payload) .ok() .map(|r| (r.transfer, r.passwordless)) } /// Returns the decrypted username, password, and full name - pub(crate) fn validate_stage2( - hyper_ratchet: &StackedRatchet, + pub(crate) fn validate_stage2( + stacked_ratchet: &R, header: &Ref<&[u8], HdpHeader>, payload: BytesMut, peer_addr: SocketAddr, ) -> Option<(DoRegisterStage2Packet, ConnectionInfo)> { let (_, plaintext_bytes) = - super::aead::validate_custom(hyper_ratchet, &header.bytes(), payload)?; + super::aead::validate_custom(stacked_ratchet, &header.bytes(), payload)?; let packet = DoRegisterStage2Packet::deserialize_from_vector(&plaintext_bytes[..]).ok()?; //let proposed_credentials = ProposedCredentials::new_from_hashed(full_name, username, SecVec::new(password.to_vec()), nonce); @@ -196,13 +207,13 @@ pub(crate) mod do_register { } /// Returns the decrypted Toolset text, as well as the welcome message - pub(crate) fn validate_success( - hyper_ratchet: &StackedRatchet, + pub(crate) fn validate_success( + stacked_ratchet: &R, header: &Ref<&[u8], HdpHeader>, payload: BytesMut, remote_addr: SocketAddr, ) -> Option<(Vec, ConnectionInfo)> { - let (_, payload) = super::aead::validate_custom(hyper_ratchet, &header.bytes(), payload)?; + let (_, payload) = super::aead::validate_custom(stacked_ratchet, &header.bytes(), payload)?; let adjacent_addr = ConnectionInfo { addr: remote_addr }; Some((payload.to_vec(), adjacent_addr)) } @@ -217,18 +228,21 @@ pub(crate) mod do_register { } } -pub(crate) mod do_drill_update { - use crate::proto::packet_crafter::do_drill_update::{ +pub(crate) mod do_stacked_ratchet_update { + use crate::proto::packet_crafter::do_entropy_bank_update::{ Stage1UpdatePacket, TruncateAckPacket, TruncatePacket, }; - use citadel_crypt::stacked_ratchet::constructor::AliceToBobTransfer; + use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_user::serialization::SyncIO; - pub(crate) fn validate_stage0(payload: &[u8]) -> Option { - AliceToBobTransfer::deserialize_from(payload as &[u8]) + pub(crate) fn validate_stage0( + payload: &[u8], + ) -> Option<>::AliceToBobWireTransfer> { + SyncIO::deserialize_from_vector(payload).ok() } - pub(crate) fn validate_stage1(payload: &[u8]) -> Option { + pub(crate) fn validate_stage1(payload: &[u8]) -> Option> { Stage1UpdatePacket::deserialize_from_vector(payload as &[u8]).ok() } @@ -242,7 +256,10 @@ pub(crate) mod do_drill_update { } pub(crate) mod pre_connect { - use citadel_crypt::toolset::{StaticAuxRatchet, Toolset}; + use citadel_crypt::endpoint_crypto_container::{ + AssociatedSecurityLevel, EndpointRatchetConstructor, + }; + use citadel_crypt::toolset::Toolset; use citadel_user::client_account::ClientNetworkAccount; use citadel_wire::hypernode_type::NodeType; @@ -252,10 +269,7 @@ pub(crate) mod pre_connect { use crate::proto::packet_crafter::pre_connect::{PreConnectStage0, SynPacket}; use crate::proto::packet_processor::includes::packet_crafter::pre_connect::SynAckPacket; use crate::proto::session_manager::CitadelSessionManager; - use citadel_crypt::stacked_ratchet::constructor::{ - BobToAliceTransfer, BobToAliceTransferType, StackedRatchetConstructor, - }; - use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; + use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::proto::ConnectMode; use citadel_types::proto::SessionSecuritySettings; use citadel_types::proto::UdpMode; @@ -263,25 +277,31 @@ pub(crate) mod pre_connect { use citadel_user::serialization::SyncIO; use citadel_wire::nat_identification::NatType; - pub(crate) type SynValidationResult = ( - StaticAuxRatchet, + pub(crate) type SynValidationResult = ( + R, BobToAliceTransfer, SessionSecuritySettings, ConnectProtocol, UdpMode, i64, NatType, - StackedRatchet, + R, ); - pub(crate) fn validate_syn( - cnac: &ClientNetworkAccount, + pub(crate) fn validate_syn( + cnac: &ClientNetworkAccount, packet: HdpPacket, - session_manager: &CitadelSessionManager, + session_manager: &CitadelSessionManager, session_password: &PreSharedKey, - ) -> Result { + ) -> Result< + SynValidationResult< + R, + >::BobToAliceWireTransfer, + >, + NetworkError, + > { // TODO: NOTE: This can interrupt any active session's. This should be moved up after checking the connect mode - let static_auxiliary_ratchet = cnac.refresh_static_hyper_ratchet(); + let static_auxiliary_ratchet = cnac.refresh_static_ratchet(); let (header, payload, _, _) = packet.decompose(); // After this point, we validate that the other end had the right static symmetric key. This proves device identity, thought not necessarily account identity let (header, payload) = @@ -289,7 +309,7 @@ pub(crate) mod pre_connect { NetworkError::InternalError("Unable to validate initial packet"), )?; - let transfer = SynPacket::deserialize_from_vector(&payload) + let transfer = SynPacket::::deserialize_from_vector(&payload) .map_err(|err| NetworkError::Generic(err.into_string()))?; // TODO: Consider adding connect_mode to the HdpSession to sync between both nodes. For now, there's no need @@ -319,9 +339,8 @@ pub(crate) mod pre_connect { .take((transfer.session_security_settings.security_level.value() + 1) as usize) .collect(); //let opts = ConstructorOpts::new_vec_init(Some(transfer.transfer.params), (transfer.transfer.security_level.value() + 1) as usize).into_i; - let bob_constructor = StackedRatchetConstructor::new_bob( + let mut bob_constructor = >::new_bob( header.session_cid.get(), - 0, opts, transfer.transfer, session_password.as_ref(), @@ -332,14 +351,17 @@ pub(crate) mod pre_connect { let transfer = bob_constructor .stage0_bob() .ok_or(NetworkError::InternalError("Unable to execute stage0_bob"))?; - let new_hyper_ratchet = bob_constructor.finish().ok_or(NetworkError::InternalError( + let new_stacked_ratchet = bob_constructor.finish().ok_or(NetworkError::InternalError( "Unable to finish bob constructor", ))?; - let _ = new_hyper_ratchet - .verify_level(transfer.security_level.into()) + let _ = new_stacked_ratchet + .verify_level(transfer.security_level().into()) .map_err(|err| NetworkError::Generic(err.into_string()))?; // below, we need to ensure the hyper ratchet stays constant throughout transformations - let toolset = Toolset::from((static_auxiliary_ratchet.clone(), new_hyper_ratchet.clone())); + let toolset = Toolset::from(( + static_auxiliary_ratchet.clone(), + new_stacked_ratchet.clone(), + )); cnac.replace_toolset(toolset); Ok(( @@ -350,46 +372,44 @@ pub(crate) mod pre_connect { udp_mode, kat, nat_type, - new_hyper_ratchet, + new_stacked_ratchet, )) } /// This returns an error if the packet is maliciously invalid (e.g., due to a false packet) /// This returns Ok(true) if the system was already synchronized, or Ok(false) if the system needed to synchronize toolsets - pub fn validate_syn_ack( + pub fn validate_syn_ack( session_password: &PreSharedKey, - cnac: &ClientNetworkAccount, - mut alice_constructor: StackedRatchetConstructor, + cnac: &ClientNetworkAccount, + mut alice_constructor: R::Constructor, packet: HdpPacket, - ) -> Option<(StackedRatchet, NatType)> { - let static_auxiliary_ratchet = cnac.get_static_auxiliary_hyper_ratchet(); + ) -> Option<(R, NatType)> { + let static_auxiliary_ratchet = cnac.get_static_auxiliary_stacked_ratchet(); let (header, payload, _, _) = packet.decompose(); let (_, payload) = super::aead::validate_custom(&static_auxiliary_ratchet, &header, payload)?; - let packet = SynAckPacket::deserialize_from_vector(&payload).ok()?; + let packet = SynAckPacket::::deserialize_from_vector(&payload).ok()?; - let lvl = packet.transfer.security_level; + let lvl = packet.transfer.security_level(); log::trace!(target: "citadel", "Session security level based-on returned transfer: {:?}", lvl); - if let Err(err) = alice_constructor.stage1_alice( - BobToAliceTransferType::Default(packet.transfer), - session_password.as_ref(), - ) { + if let Err(err) = alice_constructor.stage1_alice(packet.transfer, session_password.as_ref()) + { log::error!(target: "citadel", "Error on stage1_alice: {:?}", err); return None; } - let new_hyper_ratchet = alice_constructor.finish()?; - let _ = new_hyper_ratchet.verify_level(lvl.into()).ok()?; - let toolset = Toolset::from((static_auxiliary_ratchet, new_hyper_ratchet.clone())); + let new_stacked_ratchet = alice_constructor.finish()?; + let _ = new_stacked_ratchet.verify_level(lvl.into()).ok()?; + let toolset = Toolset::from((static_auxiliary_ratchet, new_stacked_ratchet.clone())); cnac.replace_toolset(toolset); - Some((new_hyper_ratchet, packet.nat_type)) + Some((new_stacked_ratchet, packet.nat_type)) } // Returns the adjacent node type, wave ports, and external IP. Serverside, we do not update the CNAC's toolset until this point // because we want to make sure the client passes the challenge - pub fn validate_stage0(hyper_ratchet: &StackedRatchet, packet: HdpPacket) -> Option { + pub fn validate_stage0(stacked_ratchet: &R, packet: HdpPacket) -> Option { let (header, payload, _, _) = packet.decompose(); - let (_header, payload) = super::aead::validate_custom(hyper_ratchet, &header, payload)?; + let (_header, payload) = super::aead::validate_custom(stacked_ratchet, &header, payload)?; let packet = PreConnectStage0::deserialize_from_vector(&payload).ok()?; Some(packet.node_type) } @@ -460,16 +480,16 @@ pub(crate) mod aead { use zerocopy::Ref; use crate::proto::packet::HdpHeader; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::stacked_ratchet::Ratchet; - pub(crate) type AeadValidationResult<'a> = (Ref<&'a [u8], HdpHeader>, Bytes, StackedRatchet); + pub(crate) type AeadValidationResult<'a, R> = (Ref<&'a [u8], HdpHeader>, Bytes, R); /// First-pass validation. Ensures header integrity through AAD-services in AES-GCM or chacha-poly - pub(crate) fn validate<'a, 'b: 'a, H: AsRef<[u8]> + 'b>( - proper_hr: StackedRatchet, + pub(crate) fn validate<'a, 'b: 'a, H: AsRef<[u8]> + 'b, R: Ratchet>( + proper_hr: R, header: &'b H, mut payload: BytesMut, - ) -> Option> { + ) -> Option> { let header_bytes = header.as_ref(); let header = Ref::new(header_bytes)? as Ref<&[u8], HdpHeader>; proper_hr @@ -483,19 +503,19 @@ pub(crate) mod aead { } /// First-pass validation. Ensures header integrity through AAD-services in AES-GCM - pub(crate) fn validate_custom<'a, 'b: 'a, H: AsRef<[u8]> + 'b>( - hyper_ratchet: &StackedRatchet, + pub(crate) fn validate_custom<'a, 'b: 'a, H: AsRef<[u8]> + 'b, R: Ratchet>( + stacked_ratchet: &R, header: &'b H, mut payload: BytesMut, ) -> Option<(Ref<&'a [u8], HdpHeader>, BytesMut)> { let header_bytes = header.as_ref(); let header = Ref::new(header_bytes)? as Ref<&[u8], HdpHeader>; - if let Err(err) = hyper_ratchet.validate_message_packet_in_place_split( + if let Err(err) = stacked_ratchet.validate_message_packet_in_place_split( Some(header.security_level.into()), header_bytes, &mut payload, ) { - log::error!(target: "citadel", "AES-GCM stage failed: {:?}. Supplied Ratchet Version: {} | Expected Ratchet Version: {} | Header CID: {} | Target CID: {}\nPacket: {:?}\nPayload len: {}", err, hyper_ratchet.version(), header.drill_version.get(), header.session_cid.get(), header.target_cid.get(), &header, payload.len()); + log::error!(target: "citadel", "AES-GCM stage failed: {:?}. Supplied Ratchet Version: {} | Expected Ratchet Version: {} | Header CID: {} | Target CID: {}\nPacket: {:?}\nPayload len: {}", err, stacked_ratchet.version(), header.entropy_bank_version.get(), header.session_cid.get(), header.target_cid.get(), &header, payload.len()); return None; } diff --git a/citadel_proto/tests/connections.rs b/citadel_proto/tests/connections.rs index 85df2dfb1..05519212a 100644 --- a/citadel_proto/tests/connections.rs +++ b/citadel_proto/tests/connections.rs @@ -75,7 +75,8 @@ pub mod tests { for proto in protocols { log::trace!(target: "citadel", "Testing proto {:?} @ {:?}", &proto, addr); - let res = Node::server_create_primary_listen_socket(proto.clone(), addr); + let res = + Node::::server_create_primary_listen_socket(proto.clone(), addr); if let Err(err) = res.as_ref() { log::error!(target: "citadel", "Error creating primary socket: {:?}", err); @@ -92,9 +93,10 @@ pub mod tests { }; let client = async move { - let (stream, _) = Node::c2s_connect_defaults(None, addr, client_config) - .await - .unwrap(); + let (stream, _) = + Node::::c2s_connect_defaults(None, addr, client_config) + .await + .unwrap(); on_client_received_stream(stream).await }; @@ -131,7 +133,8 @@ pub mod tests { log::trace!(target: "citadel", "Testing proto {:?}", &proto); let cnt = &AtomicUsize::new(0); - let res = Node::server_create_primary_listen_socket(proto.clone(), addr); + let res = + Node::::server_create_primary_listen_socket(proto.clone(), addr); if let Err(err) = res.as_ref() { log::error!(target: "citadel", "Error creating primary socket w/mode {proto:?}: {err:?}"); @@ -159,7 +162,9 @@ pub mod tests { for _ in 0..count { client.push(async move { - let (stream, _) = Node::c2s_connect_defaults(None, addr, client_config).await?; + let (stream, _) = + Node::::c2s_connect_defaults(None, addr, client_config) + .await?; on_client_received_stream(stream).await?; let _ = cnt.fetch_add(1, Ordering::SeqCst); Ok(()) diff --git a/citadel_sdk/examples/client.rs b/citadel_sdk/examples/client.rs index cb9f51f0b..2cb11fa30 100644 --- a/citadel_sdk/examples/client.rs +++ b/citadel_sdk/examples/client.rs @@ -1,5 +1,5 @@ use citadel_io::tokio; -use citadel_sdk::prefabs::client::ServerConnectionSettingsBuilder; +use citadel_sdk::prefabs::client::DefaultServerConnectionSettingsBuilder; use citadel_sdk::prelude::*; use std::sync::atomic::{AtomicBool, Ordering}; @@ -12,15 +12,16 @@ async fn main() { let stun2 = get_env("STUN_2_ADDR"); let finished = &AtomicBool::new(false); - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - addr, - "test-username", - "Test user", - "notsecurepassword", - ) - .with_udp_mode(UdpMode::Enabled) - .build() - .unwrap(); + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + addr, + "test-username", + "Test user", + "notsecurepassword", + ) + .with_udp_mode(UdpMode::Enabled) + .build() + .unwrap(); let client = citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel::new( @@ -39,7 +40,7 @@ async fn main() { }, ); - let _ = NodeBuilder::default() + let _ = DefaultNodeBuilder::default() .with_node_type(NodeType::Peer) .with_stun_servers([stun0, stun1, stun2]) .build(client) diff --git a/citadel_sdk/examples/peer.rs b/citadel_sdk/examples/peer.rs index 1b46f93b8..38398f693 100644 --- a/citadel_sdk/examples/peer.rs +++ b/citadel_sdk/examples/peer.rs @@ -19,14 +19,15 @@ async fn main() { .ensure_registered() .add(); - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - addr, - my_peer_id, - "dunny name", - "password", - ) - .build() - .unwrap(); + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + addr, + my_peer_id, + "dunny name", + "password", + ) + .build() + .unwrap(); let finished = &AtomicBool::new(false); let peer = citadel_sdk::prefabs::client::peer_connection::PeerConnectionKernel::new( @@ -49,7 +50,7 @@ async fn main() { }, ); - let _ = NodeBuilder::default() + let _ = DefaultNodeBuilder::default() .with_node_type(NodeType::Peer) .with_stun_servers([stun0, stun1, stun2]) .build(peer) diff --git a/citadel_sdk/examples/server.rs b/citadel_sdk/examples/server.rs index 84f180794..b83a09805 100644 --- a/citadel_sdk/examples/server.rs +++ b/citadel_sdk/examples/server.rs @@ -15,7 +15,8 @@ async fn main() { .unwrap_or(false); let server = if empty_kernel { - Box::::default() as Box + Box::>::default() + as Box> } else { Box::new( citadel_sdk::prefabs::server::client_connect_listener::ClientConnectListenerKernel::new( @@ -33,7 +34,7 @@ async fn main() { ) }; - let _ = NodeBuilder::default() + let _ = DefaultNodeBuilder::default() .with_node_type(NodeType::Server( SocketAddr::from_str(addr.as_str()).unwrap(), )) diff --git a/citadel_sdk/src/backend_kv_store.rs b/citadel_sdk/src/backend_kv_store.rs index 22c11c5cc..2d1741bbc 100644 --- a/citadel_sdk/src/backend_kv_store.rs +++ b/citadel_sdk/src/backend_kv_store.rs @@ -14,7 +14,7 @@ //! ```rust //! use citadel_sdk::prelude::*; //! -//! async fn store_data(handler: &T) -> Result<(), NetworkError> { +//! async fn store_data, R: Ratchet>(handler: &T) -> Result<(), NetworkError> { //! // Store a value //! handler.set("my_key", b"my_value".to_vec()).await?; //! @@ -45,7 +45,7 @@ const DATA_MAP_KEY: &str = "_INTERNAL_DATA_MAP"; #[async_trait] /// Contains a trait for persisting application-level data in a K,V store that is unique /// for this particular connection -pub trait BackendHandler: TargetLockedRemote { +pub trait BackendHandler: TargetLockedRemote { /// Gets a value from the backend async fn get(&self, key: &str) -> Result>, NetworkError> { let (session_cid, peer_cid) = self.get_cids(); @@ -99,11 +99,8 @@ pub trait BackendHandler: TargetLockedRemote { #[doc(hidden)] fn get_cids(&self) -> (u64, u64) { - ( - self.user().get_implicated_cid(), - self.user().get_target_cid(), - ) + (self.user().get_session_cid(), self.user().get_target_cid()) } } -impl BackendHandler for T {} +impl, R: Ratchet> BackendHandler for T {} diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs index 6732387c2..b224f6c03 100644 --- a/citadel_sdk/src/builder/node_builder.rs +++ b/citadel_sdk/src/builder/node_builder.rs @@ -19,7 +19,7 @@ //! use std::str::FromStr; //! //! // Create a basic server node -//! let builder = NodeBuilder::default() +//! let builder = DefaultNodeBuilder::default() //! .with_node_type(NodeType::Server(SocketAddr::from_str("0.0.0.0:25021").unwrap())) //! .with_backend(BackendType::InMemory); //! ``` @@ -38,7 +38,7 @@ use citadel_proto::prelude::*; use citadel_proto::kernel::KernelExecutorArguments; -use citadel_proto::macros::LocalContextRequirements; +use citadel_proto::macros::{ContextRequirements, LocalContextRequirements}; use citadel_proto::re_imports::RustlsClientConfig; use futures::Future; use std::fmt::{Debug, Formatter}; @@ -48,9 +48,8 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; -#[derive(Default)] /// Used to construct a running client/peer or server instance -pub struct NodeBuilder { +pub struct NodeBuilder { hypernode_type: Option, underlying_protocol: Option, backend_type: Option, @@ -62,6 +61,31 @@ pub struct NodeBuilder { kernel_executor_settings: Option, stun_servers: Option>, server_session_password: Option, + _ratchet: PhantomData, +} + +/// Default node builder type +pub type DefaultNodeBuilder = NodeBuilder; + +pub type LightweightNodeBuilder = NodeBuilder; + +impl Default for NodeBuilder { + fn default() -> Self { + Self { + hypernode_type: None, + underlying_protocol: None, + backend_type: None, + server_argon_settings: None, + #[cfg(feature = "google-services")] + services: None, + server_misc_settings: None, + client_tls_config: None, + kernel_executor_settings: None, + stun_servers: None, + server_session_password: None, + _ratchet: Default::default(), + } + } } /// An awaitable future whose return value propagates any internal protocol or kernel-level errors @@ -106,9 +130,9 @@ impl Future for NodeFuture<'_, K> { } } -impl NodeBuilder { +impl NodeBuilder { /// Returns a future that represents the both the protocol and kernel execution - pub fn build<'a, 'b: 'a, K: NetKernel + 'b>( + pub fn build<'a, 'b: 'a, K: NetKernel + 'b>( &'a mut self, kernel: K, ) -> anyhow::Result> { @@ -172,7 +196,7 @@ impl NodeBuilder { }; log::trace!(target: "citadel", "[NodeBuilder] Creating KernelExecutor ..."); - let kernel_executor = KernelExecutor::new(args).await?; + let kernel_executor = KernelExecutor::<_, R>::new(args).await?; log::trace!(target: "citadel", "[NodeBuilder] Executing kernel"); kernel_executor.execute().await }), @@ -183,10 +207,10 @@ impl NodeBuilder { /// ``` /// use std::net::SocketAddr; /// use std::str::FromStr; - /// use citadel_sdk::prelude::NodeBuilder; + /// use citadel_sdk::prelude::DefaultNodeBuilder; /// use citadel_proto::prelude::NodeType; /// - /// NodeBuilder::default().with_node_type(NodeType::Server(SocketAddr::from_str("0.0.0.0:25021").unwrap())); + /// DefaultNodeBuilder::default().with_node_type(NodeType::Server(SocketAddr::from_str("0.0.0.0:25021").unwrap())); /// ``` pub fn with_node_type(&mut self, node_type: NodeType) -> &mut Self { self.hypernode_type = Some(node_type); @@ -313,7 +337,7 @@ impl NodeBuilder { } /// Specifies custom STUN servers. If left unspecified, will use the defaults (twilio and Google STUN servers) - pub fn with_stun_servers, R: Into>>(&mut self, servers: R) -> &mut Self { + pub fn with_stun_servers, S: Into>>(&mut self, servers: S) -> &mut Self { self.stun_servers = Some(servers.into().into_iter().map(|t| t.into()).collect()); self } @@ -351,7 +375,7 @@ impl NodeBuilder { #[cfg(test)] mod tests { - use crate::builder::node_builder::NodeBuilder; + use crate::builder::node_builder::DefaultNodeBuilder; use crate::prefabs::server::empty::EmptyKernel; use crate::prelude::{BackendType, NodeType}; use citadel_io::tokio; @@ -362,7 +386,7 @@ mod tests { #[test] #[cfg(feature = "google-services")] fn okay_config() { - let _ = NodeBuilder::default() + let _ = DefaultNodeBuilder::default() .with_google_realtime_database_config("123", "456") .with_google_services_json_path("abc") .build(EmptyKernel::default()) @@ -372,7 +396,7 @@ mod tests { #[test] #[cfg(feature = "google-services")] fn bad_config() { - assert!(NodeBuilder::default() + assert!(DefaultNodeBuilder::default() .with_google_realtime_database_config("123", "456") .build(EmptyKernel::default()) .is_err()); @@ -380,9 +404,9 @@ mod tests { #[test] fn bad_config2() { - assert!(NodeBuilder::default() + assert!(DefaultNodeBuilder::default() .with_stun_servers(["dummy1", "dummy2"]) - .build(EmptyKernel) + .build(EmptyKernel::default()) .is_err()); } @@ -403,7 +427,7 @@ mod tests { #[values(BackendType::InMemory, BackendType::new("file:/hello_world/path/").unwrap())] backend_type: BackendType, ) { - let mut builder = NodeBuilder::default(); + let mut builder = DefaultNodeBuilder::default(); let _ = builder .with_underlying_protocol(underlying_protocol.clone()) .with_backend(backend_type.clone()) @@ -423,6 +447,6 @@ mod tests { builder.kernel_executor_settings.clone().unwrap() ); - drop(builder.build(EmptyKernel).unwrap()); + drop(builder.build(EmptyKernel::default()).unwrap()); } } diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index 0f9ea309c..fe0eade7f 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -17,7 +17,7 @@ //! use citadel_sdk::prelude::*; //! use citadel_sdk::fs; //! -//! async fn store_file(remote: &impl TargetLockedRemote) -> Result<(), NetworkError> { +//! async fn store_file(remote: &impl TargetLockedRemote) -> Result<(), NetworkError> { //! // Write a file to RE-VFS //! fs::write(remote, "/local/file.txt", "/virtual/file.txt").await?; //! @@ -44,27 +44,27 @@ //! - [`NetworkError`]: Error handling //! -use crate::prelude::{ObjectSource, ProtocolRemoteTargetExt, TargetLockedRemote}; +use crate::prelude::{ObjectSource, ProtocolRemoteTargetExt, Ratchet, TargetLockedRemote}; use citadel_proto::prelude::NetworkError; use citadel_types::crypto::SecurityLevel; use std::path::PathBuf; /// Writes a file or BytesSource to the Remote Encrypted Virtual Filesystem -pub async fn write + Send>( - remote: &impl TargetLockedRemote, +pub async fn write + Send, R: Ratchet>( + remote: &impl TargetLockedRemote, source: T, - virtual_path: R, + virtual_path: P, ) -> Result<(), NetworkError> { write_with_security_level(remote, source, Default::default(), virtual_path).await } /// Writes a file or BytesSource to the Remote Encrypted Virtual Filesystem with a custom security level. -pub async fn write_with_security_level + Send>( - remote: &impl TargetLockedRemote, +pub async fn write_with_security_level + Send, R: Ratchet>( + remote: &impl TargetLockedRemote, source: T, security_level: SecurityLevel, - virtual_path: R, + virtual_path: P, ) -> Result<(), NetworkError> { remote .remote_encrypted_virtual_filesystem_push(source, virtual_path, security_level) @@ -72,18 +72,18 @@ pub async fn write_with_security_level + Send> } /// Reads a file from the Remote Encrypted Virtual Filesystem -pub async fn read + Send>( - remote: &impl TargetLockedRemote, - virtual_path: R, +pub async fn read + Send, R: Ratchet>( + remote: &impl TargetLockedRemote, + virtual_path: P, ) -> Result { read_with_security_level(remote, Default::default(), virtual_path).await } /// Reads a file from the Remote Encrypted Virtual Filesystem with a custom transport security level -pub async fn read_with_security_level + Send>( - remote: &impl TargetLockedRemote, +pub async fn read_with_security_level + Send, R: Ratchet>( + remote: &impl TargetLockedRemote, transfer_security_level: SecurityLevel, - virtual_path: R, + virtual_path: P, ) -> Result { remote .remote_encrypted_virtual_filesystem_pull(virtual_path, transfer_security_level, false) @@ -91,9 +91,9 @@ pub async fn read_with_security_level + Send>( } /// Takes a file from the Remote Encrypted Virtual Filesystem -pub async fn take + Send>( - remote: &impl TargetLockedRemote, - virtual_path: R, +pub async fn take + Send, R: Ratchet>( + remote: &impl TargetLockedRemote, + virtual_path: P, ) -> Result { remote .remote_encrypted_virtual_filesystem_pull(virtual_path, Default::default(), true) @@ -101,10 +101,10 @@ pub async fn take + Send>( } /// Takes a file from the Remote Encrypted Virtual Filesystem with a custom security level. -pub async fn take_with_security_level + Send>( - remote: &impl TargetLockedRemote, +pub async fn take_with_security_level + Send, R: Ratchet>( + remote: &impl TargetLockedRemote, transfer_security_level: SecurityLevel, - virtual_path: R, + virtual_path: P, ) -> Result { remote .remote_encrypted_virtual_filesystem_pull(virtual_path, transfer_security_level, true) @@ -112,9 +112,9 @@ pub async fn take_with_security_level + Send>( } /// Deletes a file from the Remote Encrypted Virtual Filesystem -pub async fn delete + Send>( - remote: &impl TargetLockedRemote, - virtual_path: R, +pub async fn delete + Send, R: Ratchet>( + remote: &impl TargetLockedRemote, + virtual_path: P, ) -> Result<(), NetworkError> { remote .remote_encrypted_virtual_filesystem_delete(virtual_path) @@ -127,7 +127,7 @@ mod tests { use crate::prefabs::server::accept_file_transfer_kernel::AcceptFileTransferKernel; use crate::prefabs::client::peer_connection::{FileTransferHandleRx, PeerConnectionKernel}; - use crate::prefabs::client::ServerConnectionSettingsBuilder; + use crate::prefabs::client::DefaultServerConnectionSettingsBuilder; use crate::prelude::*; use crate::test_common::wait_for_peers; use citadel_io::tokio; @@ -139,8 +139,9 @@ mod tests { use std::time::Duration; use uuid::Uuid; - pub fn server_info<'a>() -> (NodeFuture<'a, AcceptFileTransferKernel>, SocketAddr) { - crate::test_common::server_test_node(AcceptFileTransferKernel, |_| {}) + pub fn server_info<'a, R: Ratchet>() -> (NodeFuture<'a, AcceptFileTransferKernel>, SocketAddr) + { + crate::test_common::server_test_node(AcceptFileTransferKernel::::default(), |_| {}) } #[rstest] @@ -164,7 +165,7 @@ mod tests { ) { citadel_logging::setup_log(); let client_success = &AtomicBool::new(false); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let uuid = Uuid::new_v4(); let source_dir = PathBuf::from("../resources/TheBridge.pdf"); @@ -176,7 +177,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .disable_udp() .with_session_security_settings(session_security_settings) .build() @@ -209,7 +210,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let result = citadel_io::tokio::select! { res0 = client => res0.map(|_| ()), @@ -237,7 +238,7 @@ mod tests { ) { citadel_logging::setup_log(); let client_success = &AtomicBool::new(false); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let uuid = Uuid::new_v4(); let source_dir = PathBuf::from("../resources/TheBridge.pdf"); @@ -249,7 +250,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .disable_udp() .with_session_security_settings(session_security_settings) .build() @@ -284,7 +285,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let result = citadel_io::tokio::select! { res0 = client => res0.map(|_| ()), @@ -312,7 +313,7 @@ mod tests { ) { citadel_logging::setup_log(); let client_success = &AtomicBool::new(false); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let uuid = Uuid::new_v4(); let source_dir = PathBuf::from("../resources/TheBridge.pdf"); @@ -324,7 +325,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .disable_udp() .with_session_security_settings(session_security_settings) .build() @@ -360,7 +361,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let result = citadel_io::tokio::select! { res0 = client => res0.map(|_| ()), @@ -386,7 +387,7 @@ mod tests { let client0_success = &AtomicBool::new(false); let client1_success = &AtomicBool::new(false); - let (server, server_addr) = crate::test_common::server_info(); + let (server, server_addr) = crate::test_common::server_info::(); let uuid0 = Uuid::new_v4(); let uuid1 = Uuid::new_v4(); @@ -401,7 +402,7 @@ mod tests { let source_dir = &PathBuf::from("../resources/TheBridge.pdf"); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) .disable_udp() .with_session_security_settings(session_security) .build() @@ -421,7 +422,7 @@ mod tests { move |mut connection, remote_outer| async move { wait_for_peers().await; let mut connection = connection.recv().await.unwrap()?; - let cid = connection.channel.get_implicated_cid(); + let cid = connection.channel.get_session_cid(); wait_for_peers().await; // The other peer will send the file first log::info!(target: "citadel", "***CLIENT A {cid} LOGIN SUCCESS :: File transfer next ***"); @@ -456,7 +457,7 @@ mod tests { ); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) .disable_udp() .with_session_security_settings(session_security) .build() @@ -475,7 +476,7 @@ mod tests { move |mut connection, remote_outer| async move { wait_for_peers().await; let mut connection = connection.recv().await.unwrap()?; - let cid = connection.channel.get_implicated_cid(); + let cid = connection.channel.get_session_cid(); wait_for_peers().await; let remote = connection.remote.clone(); let handle_orig = connection.incoming_object_transfer_handles.take().unwrap(); @@ -509,8 +510,8 @@ mod tests { }, ); - let client0 = NodeBuilder::default().build(client_kernel0).unwrap(); - let client1 = NodeBuilder::default().build(client_kernel1).unwrap(); + let client0 = DefaultNodeBuilder::default().build(client_kernel0).unwrap(); + let client1 = DefaultNodeBuilder::default().build(client_kernel1).unwrap(); let clients = futures::future::try_join(client0, client1); let task = async move { diff --git a/citadel_sdk/src/lib.rs b/citadel_sdk/src/lib.rs index f4e136fb3..4b3d7499e 100644 --- a/citadel_sdk/src/lib.rs +++ b/citadel_sdk/src/lib.rs @@ -114,7 +114,7 @@ //! use citadel_sdk::prefabs::server::empty::EmptyKernel; //! //! // this server will listen on 127.0.0.1:25021, and will use the built-in defaults. When calling 'build', a NetKernel is specified -//! let server = NodeBuilder::default() +//! let server = DefaultNodeBuilder::default() //! .with_node_type(NodeType::server("127.0.0.1:25021")?) //! .build(EmptyKernel::default())?; //! @@ -132,7 +132,7 @@ //! use futures::StreamExt; //! use citadel_sdk::prelude::*; //! -//! let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?; +//! let server_connection_settings = DefaultServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?; //! //! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |connect_success, mut remote| async move { //! // handle program logic here @@ -144,7 +144,7 @@ //! Ok(()) //! }); //! -//! let client = NodeBuilder::default().build(client_kernel)?; +//! let client = DefaultNodeBuilder::default().build(client_kernel)?; //! # async move { //! let result = client.await; //! # }; @@ -171,7 +171,7 @@ //! //! // this server will listen on 127.0.0.1:25021, and will use the built-in defaults with a kernel //! // that auto-accepts inbound file transfer requests -//! let server = NodeBuilder::default() +//! let server = DefaultNodeBuilder::default() //! .with_node_type(NodeType::server("127.0.0.1:25021")?) //! .build(AcceptFileTransferKernel::default())?; //! @@ -188,7 +188,7 @@ //! use futures::StreamExt; //! use citadel_sdk::prelude::*; //! -//! let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?; +//! let server_connection_settings = DefaultServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?; //! //! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |connect_success, mut remote| async move { //! let virtual_path = "/home/virtual_user/output.pdf"; @@ -200,7 +200,7 @@ //! Ok(()) //! }); //! -//! let client = NodeBuilder::default().build(client_kernel)?; +//! let client = DefaultNodeBuilder::default().build(client_kernel)?; //! # async move { //! let result = client.await; //! # }; @@ -236,7 +236,9 @@ pub mod prelude { pub use crate::builder::node_builder::*; pub use crate::prefabs::client::peer_connection::PeerConnectionSetupAggregator; pub use crate::prefabs::client::PrefabFunctions; - pub use crate::prefabs::client::{ServerConnectionSettings, ServerConnectionSettingsBuilder}; + pub use crate::prefabs::client::{ + DefaultServerConnectionSettingsBuilder, ServerConnectionSettings, + }; pub use crate::remote_ext::user_ids::*; pub use crate::remote_ext::*; pub use crate::responses; diff --git a/citadel_sdk/src/macros.rs b/citadel_sdk/src/macros.rs index a2adb443a..92fb3de91 100644 --- a/citadel_sdk/src/macros.rs +++ b/citadel_sdk/src/macros.rs @@ -15,8 +15,8 @@ //! use citadel_sdk::impl_remote; //! //! #[derive(Clone)] -//! struct MyRemote { -//! inner: NodeRemote, +//! struct MyRemote { +//! inner: NodeRemote, //! } //! //! impl_remote!(MyRemote); @@ -35,9 +35,9 @@ #[macro_export] macro_rules! impl_remote { - ($item:ty) => { + ($item:ident) => { #[$crate::async_trait] - impl Remote for $item { + impl Remote for $item { async fn send_with_custom_ticket( &self, ticket: Ticket, @@ -56,7 +56,7 @@ macro_rules! impl_remote { self.inner.send_callback_subscription(request).await } - fn account_manager(&self) -> &AccountManager { + fn account_manager(&self) -> &AccountManager { self.inner.account_manager() } diff --git a/citadel_sdk/src/prefabs/client/broadcast.rs b/citadel_sdk/src/prefabs/client/broadcast.rs index cd9272fef..edb216d48 100644 --- a/citadel_sdk/src/prefabs/client/broadcast.rs +++ b/citadel_sdk/src/prefabs/client/broadcast.rs @@ -28,7 +28,7 @@ //! accept_registrations: true, //! }; //! -//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! let settings = DefaultServerConnectionSettingsBuilder::transient("127.0.0.1:25021") //! .build()?; //! //! let kernel = BroadcastKernel::new( @@ -77,8 +77,8 @@ use uuid::Uuid; /// to the owner alone. The owner thus serves as an "axis of consent", where each member /// trusts the owner, and through this trust, transitivity of trust flows to all other /// future members that connect to the group. -pub struct BroadcastKernel<'a, F, Fut> { - inner_kernel: Box, +pub struct BroadcastKernel<'a, F, Fut, R: Ratchet> { + inner_kernel: Box + 'a>, shared: Arc, _pd: PhantomData (F, Fut)>, } @@ -124,9 +124,10 @@ pub enum GroupInitRequestType { } #[async_trait] -impl<'a, F, Fut> PrefabFunctions<'a, GroupInitRequestType> for BroadcastKernel<'a, F, Fut> +impl<'a, F, Fut, R: Ratchet> PrefabFunctions<'a, GroupInitRequestType, R> + for BroadcastKernel<'a, F, Fut, R> where - F: FnOnce(GroupChannel, ClientServerRemote) -> Fut + Send + 'a, + F: FnOnce(GroupChannel, ClientServerRemote) -> Fut + Send + 'a, Fut: Future> + Send + 'a, { type UserLevelInputFunction = F; @@ -143,12 +144,12 @@ where )] async fn on_c2s_channel_received( connect_success: ConnectionSuccess, - remote: ClientServerRemote, + remote: ClientServerRemote, arg: GroupInitRequestType, fx: Self::UserLevelInputFunction, shared: Arc, ) -> Result<(), NetworkError> { - let implicated_cid = connect_success.cid; + let session_cid = connect_success.cid; wait_for_peers().await; let mut creator_only_accept_inbound_registers = false; @@ -166,7 +167,7 @@ where for peer in &invite_list { let peer = peer - .search_peer(implicated_cid, remote.inner.account_manager()) + .search_peer(session_cid, remote.inner.account_manager()) .await? .ok_or_else(|| { NetworkError::msg(format!( @@ -198,7 +199,7 @@ where // ensure local is registered to owner let owner_orig = owner; let owner_find = owner_orig - .search_peer(implicated_cid, remote.inner.account_manager()) + .search_peer(session_cid, remote.inner.account_manager()) .await?; let owner = if let Some(owner) = owner_find { @@ -211,7 +212,7 @@ where let _ = handle.register_to_peer().await?; // wait_for_peers().await; owner_orig - .search_peer(implicated_cid, remote.inner.account_manager()) + .search_peer(session_cid, remote.inner.account_manager()) .await? } else { None @@ -261,7 +262,7 @@ where }; let request = NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand { - implicated_cid, + session_cid, command: request, }); @@ -269,7 +270,7 @@ where remote.inner.send_callback_subscription(request).await?, )); - log::trace!(target: "citadel", "Peer {implicated_cid} is attempting to join group"); + log::trace!(target: "citadel", "Peer {session_cid} is attempting to join group"); let acceptor_task = if creator_only_accept_inbound_registers { shared.route_registers.store(true, Ordering::Relaxed); let mut reg_rx = shared.register_rx.lock().take().unwrap(); @@ -294,7 +295,7 @@ where } }; - log::trace!(target: "citadel", "ACCEPTOR {implicated_cid} RECV reg_request: {:?}", post_register); + log::trace!(target: "citadel", "ACCEPTOR {session_cid} RECV reg_request: {:?}", post_register); if let PeerSignal::PostRegister { peer_conn_type: peer_conn, inviter_username: _, @@ -304,7 +305,7 @@ where } = &post_register { let cid = peer_conn.get_original_target_cid(); - if cid != implicated_cid { + if cid != session_cid { log::warn!(target: "citadel", "Received the wrong CID. Will not accept request"); continue; } @@ -357,7 +358,7 @@ where NodeResult::GroupChannelCreated(GroupChannelCreated { ticket: _, channel, - implicated_cid: _, + session_cid: _, }) => { // in either case, whether owner or not, we get a channel // Drop the lock to allow the acceptor task to gain access to the subscription @@ -370,7 +371,7 @@ where } NodeResult::GroupEvent(GroupEvent { - implicated_cid: _, + session_cid: _, ticket: _, event: GroupBroadcast::CreateResponse { key: None }, }) => { @@ -386,7 +387,7 @@ where Ok(()) } - fn construct(kernel: Box) -> Self { + fn construct(kernel: Box + 'a>) -> Self { let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); Self { shared: Arc::new(BroadcastShared { @@ -401,8 +402,8 @@ where } #[async_trait] -impl NetKernel for BroadcastKernel<'_, F, Fut> { - fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError> { +impl NetKernel for BroadcastKernel<'_, F, Fut, R> { + fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError> { self.inner_kernel.load_remote(node_remote) } @@ -438,7 +439,7 @@ impl NetKernel for BroadcastKernel<'_, F, Fut> { mod tests { use crate::prefabs::client::broadcast::{BroadcastKernel, GroupInitRequestType}; use crate::prefabs::client::peer_connection::PeerConnectionKernel; - use crate::prefabs::client::ServerConnectionSettingsBuilder; + use crate::prefabs::client::DefaultServerConnectionSettingsBuilder; use crate::prelude::*; use crate::test_common::{server_info, wait_for_peers, TestBarrier}; use citadel_io::tokio; @@ -456,7 +457,7 @@ mod tests { TestBarrier::setup(peer_count); let client_success = &AtomicUsize::new(0); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -485,7 +486,7 @@ mod tests { }; let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -494,7 +495,7 @@ mod tests { request, move |channel, remote| async move { wait_for_peers().await; - log::trace!(target: "citadel", "***GROUP PEER {}={}={} CONNECT SUCCESS***", idx, uuid, remote.conn_type.get_implicated_cid()); + log::trace!(target: "citadel", "***GROUP PEER {}={}={} CONNECT SUCCESS***", idx, uuid, remote.conn_type.get_session_cid()); let owned_groups = remote.list_owned_groups().await.unwrap(); @@ -504,7 +505,7 @@ mod tests { assert_eq!(owned_groups.len(), 0); } - log::trace!(target: "citadel", "Peer {idx}={} is COMPLETE!", remote.conn_type.get_implicated_cid()); + log::trace!(target: "citadel", "Peer {idx}={} is COMPLETE!", remote.conn_type.get_session_cid()); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; @@ -513,7 +514,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -550,7 +551,7 @@ mod tests { let client_success = &AtomicBool::new(false); let receiver_success = &AtomicBool::new(false); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -567,7 +568,7 @@ mod tests { .collect::>(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -575,7 +576,7 @@ mod tests { server_connection_settings, peers, move |mut results, remote| async move { - let _sender = remote.conn_type.get_implicated_cid(); + let _sender = remote.conn_type.get_session_cid(); let mut signals = remote.get_unprocessed_signals_receiver().unwrap(); wait_for_peers().await; @@ -598,7 +599,7 @@ mod tests { log::info!(target: "citadel", "Received unprocessed signal: {:?}", evt); match evt { NodeResult::GroupEvent(GroupEvent { - implicated_cid: _, + session_cid: _, ticket: _, event: GroupBroadcast::Invitation { @@ -614,7 +615,7 @@ mod tests { NodeResult::GroupChannelCreated(GroupChannelCreated { ticket: _, channel: _chan, - implicated_cid: _, + session_cid: _, }) => { receiver_success.store(true, Ordering::Relaxed); log::trace!(target: "citadel", "***PEER {} CONNECT***", uuid); @@ -635,7 +636,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } diff --git a/citadel_sdk/src/prefabs/client/mod.rs b/citadel_sdk/src/prefabs/client/mod.rs index a0309b88a..d2ba3aa5c 100644 --- a/citadel_sdk/src/prefabs/client/mod.rs +++ b/citadel_sdk/src/prefabs/client/mod.rs @@ -20,7 +20,7 @@ //! //! # fn main() -> Result<(), NetworkError> { //! // Create transient connection settings -//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! let settings = DefaultServerConnectionSettingsBuilder::transient("127.0.0.1:25021") //! .with_udp_mode(UdpMode::Enabled) //! .build()?; //! # Ok(()) @@ -43,6 +43,7 @@ use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; use crate::prefabs::ClientServerRemote; use crate::prelude::*; +use std::marker::PhantomData; use std::net::{SocketAddr, ToSocketAddrs}; use uuid::Uuid; @@ -54,7 +55,7 @@ pub mod peer_connection; pub mod single_connection; #[async_trait] -pub trait PrefabFunctions<'a, Arg: Send + 'a>: Sized + 'a { +pub trait PrefabFunctions<'a, Arg: Send + 'a, R: Ratchet>: Sized + 'a { type UserLevelInputFunction: Send + 'a; /// Shared between the kernel and the on_c2s_channel_received function type SharedBundle: Send + 'a; @@ -63,25 +64,25 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a>: Sized + 'a { async fn on_c2s_channel_received( connect_success: ConnectionSuccess, - remote: ClientServerRemote, + remote: ClientServerRemote, arg: Arg, fx: Self::UserLevelInputFunction, shared: Self::SharedBundle, ) -> Result<(), NetworkError>; - fn construct(kernel: Box) -> Self; + fn construct(kernel: Box + 'a>) -> Self; /// Creates a new connection with a central server entailed by the user information fn new( - server_connection_settings: ServerConnectionSettings, + server_connection_settings: ServerConnectionSettings, arg: Arg, on_channel_received: Self::UserLevelInputFunction, ) -> Self { let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); - let server_conn_kernel = SingleClientServerConnectionKernel::new( + let server_conn_kernel = SingleClientServerConnectionKernel::<_, _, R>::new( server_connection_settings, |connect_success, remote| { - on_channel_received_fn::<_, Self>( + on_channel_received_fn::<_, Self, R>( connect_success, remote, rx, @@ -97,9 +98,9 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a>: Sized + 'a { } } -async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg>>( +async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg, R>, R: Ratchet>( connect_success: ConnectionSuccess, - remote: ClientServerRemote, + remote: ClientServerRemote, rx_bundle: citadel_io::tokio::sync::oneshot::Receiver, arg: Arg, on_channel_received: T::UserLevelInputFunction, @@ -111,7 +112,7 @@ async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg>> } /// Used to instantiate a client to server connection -pub struct ServerConnectionSettingsBuilder { +pub struct ServerConnectionSettingsBuilder { password: Option, username: Option, name: Option, @@ -121,9 +122,13 @@ pub struct ServerConnectionSettingsBuilder { session_security_settings: Option, transient_uuid: Option, is_connect: bool, + _ratchet: PhantomData, } -impl ServerConnectionSettingsBuilder { +pub type DefaultServerConnectionSettingsBuilder = + ServerConnectionSettingsBuilder; + +impl ServerConnectionSettingsBuilder { /// Creates a new connection to a central server that does not persist client metadata and account information /// after the connection is dropped to the server. This is ideal for applications that do not require /// persistence. @@ -143,6 +148,7 @@ impl ServerConnectionSettingsBuilder { transient_uuid: Some(id.into()), address: Some(addr), is_connect: false, + _ratchet: PhantomData, } } @@ -164,6 +170,7 @@ impl ServerConnectionSettingsBuilder { udp_mode: None, session_security_settings: None, is_connect: false, + _ratchet: PhantomData, } } @@ -183,6 +190,7 @@ impl ServerConnectionSettingsBuilder { udp_mode: None, session_security_settings: None, is_connect: true, + _ratchet: PhantomData, } } @@ -217,7 +225,7 @@ impl ServerConnectionSettingsBuilder { } /// Builds the client-to-server connection settings - pub fn build(self) -> Result { + pub fn build(self) -> Result, NetworkError> { let server_addr = if let Some(addr) = self.address { let addr = addr .to_socket_addrs() @@ -230,16 +238,17 @@ impl ServerConnectionSettingsBuilder { }; if let Some(uuid) = self.transient_uuid { - Ok(ServerConnectionSettings::Transient { + Ok(ServerConnectionSettings::::Transient { server_addr: server_addr .ok_or(NetworkError::Generic("No address found".to_string()))?, uuid, udp_mode: self.udp_mode.unwrap_or_default(), session_security_settings: self.session_security_settings.unwrap_or_default(), pre_shared_key: self.psk, + _ratchet: PhantomData, }) } else if self.is_connect { - Ok(ServerConnectionSettings::CredentialedConnect { + Ok(ServerConnectionSettings::::CredentialedConnect { username: self .username .ok_or(NetworkError::Generic("No username found".to_string()))?, @@ -249,9 +258,10 @@ impl ServerConnectionSettingsBuilder { udp_mode: self.udp_mode.unwrap_or_default(), session_security_settings: self.session_security_settings.unwrap_or_default(), pre_shared_key: self.psk, + _ratchet: PhantomData, }) } else { - Ok(ServerConnectionSettings::CredentialedRegister { + Ok(ServerConnectionSettings::::CredentialedRegister { address: server_addr .ok_or(NetworkError::Generic("No address found".to_string()))?, username: self @@ -266,19 +276,21 @@ impl ServerConnectionSettingsBuilder { pre_shared_key: self.psk, udp_mode: self.udp_mode.unwrap_or_default(), session_security_settings: self.session_security_settings.unwrap_or_default(), + _ratchet: PhantomData, }) } } } /// The settings for a client-to-server connection -pub enum ServerConnectionSettings { +pub enum ServerConnectionSettings { Transient { server_addr: SocketAddr, uuid: Uuid, udp_mode: UdpMode, session_security_settings: SessionSecuritySettings, pre_shared_key: Option, + _ratchet: PhantomData, }, CredentialedConnect { username: String, @@ -286,6 +298,7 @@ pub enum ServerConnectionSettings { udp_mode: UdpMode, session_security_settings: SessionSecuritySettings, pre_shared_key: Option, + _ratchet: PhantomData, }, CredentialedRegister { address: SocketAddr, @@ -295,10 +308,11 @@ pub enum ServerConnectionSettings { pre_shared_key: Option, udp_mode: UdpMode, session_security_settings: SessionSecuritySettings, + _ratchet: PhantomData, }, } -impl ServerConnectionSettings { +impl ServerConnectionSettings { pub(crate) fn udp_mode(&self) -> UdpMode { match self { Self::Transient { udp_mode, .. } => *udp_mode, diff --git a/citadel_sdk/src/prefabs/client/peer_connection.rs b/citadel_sdk/src/prefabs/client/peer_connection.rs index 8e97e0748..11948299b 100644 --- a/citadel_sdk/src/prefabs/client/peer_connection.rs +++ b/citadel_sdk/src/prefabs/client/peer_connection.rs @@ -29,7 +29,7 @@ //! .with_session_security_settings(Default::default()) //! .add(); //! -//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! let settings = DefaultServerConnectionSettingsBuilder::transient("127.0.0.1:25021") //! .build()?; //! //! let kernel = PeerConnectionKernel::new( @@ -78,8 +78,8 @@ use uuid::Uuid; /// After establishing a connection to the central node, this kernel begins connecting to the desired /// peer(s) -pub struct PeerConnectionKernel<'a, F, Fut> { - inner_kernel: Box, +pub struct PeerConnectionKernel<'a, F, Fut, R: Ratchet> { + inner_kernel: Box + 'a>, shared: Shared, // by using fn() -> Fut, the future does not need to be Sync _pd: PhantomData (F, Fut)>, @@ -145,8 +145,8 @@ impl Drop for FileTransferHandleRx { } #[async_trait] -impl NetKernel for PeerConnectionKernel<'_, F, Fut> { - fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { +impl NetKernel for PeerConnectionKernel<'_, F, Fut, R> { + fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { self.inner_kernel.load_remote(server_remote) } @@ -160,7 +160,7 @@ impl NetKernel for PeerConnectionKernel<'_, F, Fut> { NodeResult::ObjectTransferHandle(ObjectTransferHandle { ticket: _, handle, - implicated_cid, + session_cid, }) => { let is_revfs = matches!( handle.metadata.transfer_type, @@ -168,13 +168,13 @@ impl NetKernel for PeerConnectionKernel<'_, F, Fut> { ); let active_peers = self.shared.active_peer_conns.lock(); let v_conn = if is_revfs { - let peer_cid = if implicated_cid != handle.source { + let peer_cid = if session_cid != handle.source { handle.source } else { handle.receiver }; PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid, } } else { @@ -183,12 +183,12 @@ impl NetKernel for PeerConnectionKernel<'_, F, Fut> { ObjectTransferOrientation::Receiver { .. } ) { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: handle.source, } } else { PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: handle.receiver, } } @@ -403,10 +403,10 @@ impl From for PeerConnectionSetupAggregator { } #[async_trait] -impl<'a, F, Fut, T: Into + Send + 'a> PrefabFunctions<'a, T> - for PeerConnectionKernel<'a, F, Fut> +impl<'a, F, Fut, T: Into + Send + 'a, R: Ratchet> + PrefabFunctions<'a, T, R> for PeerConnectionKernel<'a, F, Fut, R> where - F: FnOnce(Receiver>, ClientServerRemote) -> Fut + F: FnOnce(Receiver, NetworkError>>, ClientServerRemote) -> Fut + Send + 'a, Fut: Future> + Send + 'a, @@ -425,13 +425,13 @@ where )] async fn on_c2s_channel_received( connect_success: ConnectionSuccess, - cls_remote: ClientServerRemote, + cls_remote: ClientServerRemote, peers_to_connect: T, f: Self::UserLevelInputFunction, shared: Shared, ) -> Result<(), NetworkError> { let shared = &shared; - let implicated_cid = connect_success.cid; + let session_cid = connect_success.cid; let mut peers_already_registered = vec![]; wait_for_peers().await; @@ -441,7 +441,7 @@ where // TODO: optimize this into a single concurrent operation peers_already_registered.push( peer.id - .search_peer(implicated_cid, cls_remote.inner.account_manager()) + .search_peer(session_cid, cls_remote.inner.account_manager()) .await?, ) } @@ -469,11 +469,11 @@ where let (file_transfer_tx, file_transfer_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let handle = if let Some(_already_registered) = mutually_registered { - remote.find_target(implicated_cid, id).await? + remote.find_target(session_cid, id).await? } else { // TODO: optimize peer registration + connection in one go - log::info!(target: "citadel", "{implicated_cid} proposing target {id:?} to central node"); - let handle = remote.propose_target(implicated_cid, id.clone()).await?; + log::info!(target: "citadel", "{session_cid} proposing target {id:?} to central node"); + let handle = remote.propose_target(session_cid, id.clone()).await?; // if the peer is not yet registered to the central node, wait for it to become registered // this is useful especially for testing purposes if ensure_registered { @@ -488,9 +488,9 @@ where } } - log::info!(target: "citadel", "{implicated_cid} registering to peer {id:?}"); + log::info!(target: "citadel", "{session_cid} registering to peer {id:?}"); let _reg_success = handle.register_to_peer().await?; - log::info!(target: "citadel", "{implicated_cid} registered to peer {id:?} registered || success -> now connecting"); + log::info!(target: "citadel", "{session_cid} registered to peer {id:?} registered || success -> now connecting"); handle }; @@ -534,7 +534,7 @@ where citadel_io::tokio::try_join!(collection_task, f(rx, cls_remote)).map(|_| ()) } - fn construct(kernel: Box) -> Self { + fn construct(kernel: Box + 'a>) -> Self { Self { inner_kernel: kernel, shared: Shared { @@ -548,7 +548,7 @@ where #[cfg(test)] mod tests { use crate::prefabs::client::peer_connection::PeerConnectionKernel; - use crate::prefabs::client::ServerConnectionSettingsBuilder; + use crate::prefabs::client::DefaultServerConnectionSettingsBuilder; use crate::prelude::*; use crate::remote_ext::results::PeerConnectSuccess; use crate::test_common::{server_info, wait_for_peers, TestBarrier}; @@ -583,7 +583,7 @@ mod tests { TestBarrier::setup(peer_count); let client_success = &AtomicUsize::new(0); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -611,7 +611,7 @@ mod tests { } let server_connection_settings = - ServerConnectionSettingsBuilder::credentialed_registration( + DefaultServerConnectionSettingsBuilder::credentialed_registration( server_addr, username, full_name, @@ -627,20 +627,20 @@ mod tests { agg.clone(), move |results, mut remote| async move { log::info!(target: "citadel", "***PEER {username} CONNECTED ***"); - let implicated_cid = remote.conn_type.get_implicated_cid(); - let check = move |conn: PeerConnectSuccess| async move { - let implicated_cid = conn.channel.get_implicated_cid(); + let session_cid = remote.conn_type.get_session_cid(); + let check = move |conn: PeerConnectSuccess<_>| async move { + let session_cid = conn.channel.get_session_cid(); let _mutual_peers = conn .remote .remote() - .get_local_group_mutual_peers(implicated_cid) + .get_local_group_mutual_peers(session_cid) .await .unwrap(); conn }; let p2p_remotes = handle_peer_connect_successes( results, - implicated_cid, + session_cid, peer_count, udp_mode, check, @@ -660,10 +660,10 @@ mod tests { } // test to make sure the mutuals are valid - let implicated_cid = remote.conn_type.get_implicated_cid(); + let session_cid = remote.conn_type.get_session_cid(); let mutual_peers = remote .inner - .get_local_group_mutual_peers(implicated_cid) + .get_local_group_mutual_peers(session_cid) .await .unwrap(); for (peer_cid, _) in p2p_remotes { @@ -677,7 +677,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -704,7 +704,7 @@ mod tests { let do_deregister = peer_count == 2; let client_success = &AtomicUsize::new(0); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -732,7 +732,7 @@ mod tests { } let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -741,9 +741,9 @@ mod tests { agg, move |results, remote| async move { log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); - let implicated_cid = remote.conn_type.get_implicated_cid(); + let session_cid = remote.conn_type.get_session_cid(); - let check = move |conn: PeerConnectSuccess| async move { + let check = move |conn: PeerConnectSuccess<_>| async move { if do_deregister { conn.remote .deregister() @@ -754,7 +754,7 @@ mod tests { .inner .account_manager() .get_persistence_handler() - .hyperlan_peer_exists(implicated_cid, conn.channel.get_peer_cid()) + .hyperlan_peer_exists(session_cid, conn.channel.get_peer_cid()) .await .unwrap()); } @@ -763,7 +763,7 @@ mod tests { let _ = handle_peer_connect_successes( results, - implicated_cid, + session_cid, peer_count, udp_mode, check, @@ -777,7 +777,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -809,7 +809,7 @@ mod tests { let sender_success = &AtomicBool::new(false); let receiver_success = &AtomicBool::new(false); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -847,7 +847,7 @@ mod tests { } let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -857,9 +857,9 @@ mod tests { move |results, remote| async move { log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); wait_for_peers().await; - let implicated_cid = remote.conn_type.get_implicated_cid(); + let session_cid = remote.conn_type.get_session_cid(); let is_sender = idx == 0; // the first peer is the sender, the rest are receivers - let check = move |mut conn: PeerConnectSuccess| async move { + let check = move |mut conn: PeerConnectSuccess<_>| async move { if is_sender { conn.remote .send_file_with_custom_opts( @@ -919,7 +919,7 @@ mod tests { let peer_count = if idx == 0 { peer_count } else { 2 }; let _ = handle_peer_connect_successes( results, - implicated_cid, + session_cid, peer_count, udp_mode, check, @@ -939,7 +939,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -970,7 +970,7 @@ mod tests { let udp_mode = UdpMode::Enabled; let client_success = &AtomicUsize::new(0); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -998,7 +998,7 @@ mod tests { } let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -1007,9 +1007,9 @@ mod tests { agg, move |results, remote| async move { log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); - let implicated_cid = remote.conn_type.get_implicated_cid(); + let session_cid = remote.conn_type.get_session_cid(); - let check = move |conn: PeerConnectSuccess| async move { + let check = move |conn: PeerConnectSuccess<_>| async move { if idx == 0 { for x in 1..10 { assert_eq!( @@ -1023,7 +1023,7 @@ mod tests { }; let _ = handle_peer_connect_successes( results, - implicated_cid, + session_cid, peer_count, udp_mode, check, @@ -1037,7 +1037,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -1067,7 +1067,7 @@ mod tests { let udp_mode = UdpMode::Enabled; let client_success = &AtomicUsize::new(0); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -1095,7 +1095,7 @@ mod tests { } let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -1104,9 +1104,9 @@ mod tests { agg, move |results, remote| async move { log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); - let implicated_cid = remote.conn_type.get_implicated_cid(); + let session_cid = remote.conn_type.get_session_cid(); - let check = move |conn: PeerConnectSuccess| async move { + let check = move |conn: PeerConnectSuccess<_>| async move { conn.remote .disconnect() .await @@ -1115,7 +1115,7 @@ mod tests { }; let _ = handle_peer_connect_successes( results, - implicated_cid, + session_cid, peer_count, udp_mode, check, @@ -1129,7 +1129,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -1158,7 +1158,7 @@ mod tests { ) { citadel_logging::setup_log_no_panic_hook(); TestBarrier::setup(2); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let peer_0_error_received = &AtomicBool::new(false); let peer_1_error_received = &AtomicBool::new(false); @@ -1193,14 +1193,14 @@ mod tests { let peer1_connection = peer1_agg.add(); let server_connection_settings0 = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() .unwrap(); let server_connection_settings1 = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -1212,7 +1212,7 @@ mod tests { move |mut connection, remote| async move { wait_for_peers().await; let conn = connection.recv().await.unwrap(); - log::trace!(target: "citadel", "Peer 0 {} received: {:?}", remote.conn_type.get_implicated_cid(), conn); + log::trace!(target: "citadel", "Peer 0 {} received: {:?}", remote.conn_type.get_session_cid(), conn); if conn.is_ok() { peer_0_error_received.store(true, Ordering::SeqCst); } @@ -1227,7 +1227,7 @@ mod tests { move |mut connection, remote| async move { wait_for_peers().await; let conn = connection.recv().await.unwrap(); - log::trace!(target: "citadel", "Peer 1 {} received: {:?}", remote.conn_type.get_implicated_cid(), conn); + log::trace!(target: "citadel", "Peer 1 {} received: {:?}", remote.conn_type.get_session_cid(), conn); if conn.is_ok() { peer_1_error_received.store(true, Ordering::SeqCst); } @@ -1236,8 +1236,8 @@ mod tests { }, ); - let client0 = NodeBuilder::default().build(client_kernel0).unwrap(); - let client1 = NodeBuilder::default().build(client_kernel1).unwrap(); + let client0 = DefaultNodeBuilder::default().build(client_kernel0).unwrap(); + let client1 = DefaultNodeBuilder::default().build(client_kernel1).unwrap(); let clients = futures::future::try_join(client0, client1); let task = async move { @@ -1256,16 +1256,16 @@ mod tests { assert!(!peer_1_error_received.load(Ordering::SeqCst)); } - async fn handle_peer_connect_successes( - mut conn_rx: Receiver>, - implicated_cid: u64, + async fn handle_peer_connect_successes( + mut conn_rx: Receiver, NetworkError>>, + session_cid: u64, peer_count: usize, udp_mode: UdpMode, checks: F, - ) -> Vec + ) -> Vec> where - F: for<'a> Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, - Fut: Future + Send, + F: for<'a> Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, + Fut: Future> + Send, { let (finished_tx, finished_rx) = tokio::sync::oneshot::channel(); @@ -1284,7 +1284,7 @@ mod tests { handle_peer_connect_success( conn, done_tx.clone(), - implicated_cid, + session_cid, udp_mode, checks.clone(), ); @@ -1313,18 +1313,18 @@ mod tests { ret } - fn handle_peer_connect_success( - mut conn: PeerConnectSuccess, - done_tx: UnboundedSender, - implicated_cid: u64, + fn handle_peer_connect_success( + mut conn: PeerConnectSuccess, + done_tx: UnboundedSender>, + session_cid: u64, udp_mode: UdpMode, checks: F, ) where - F: Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, - Fut: Future + Send, + F: Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, + Fut: Future> + Send, { let task = async move { - crate::test_common::p2p_assertions(implicated_cid, &conn).await; + crate::test_common::p2p_assertions(session_cid, &conn).await; crate::test_common::udp_mode_assertions(udp_mode, conn.udp_channel_rx.take()).await; let conn = checks(conn).await; done_tx diff --git a/citadel_sdk/src/prefabs/client/single_connection.rs b/citadel_sdk/src/prefabs/client/single_connection.rs index 1256d528d..68cf819e7 100644 --- a/citadel_sdk/src/prefabs/client/single_connection.rs +++ b/citadel_sdk/src/prefabs/client/single_connection.rs @@ -19,7 +19,7 @@ //! //! # fn main() -> Result<(), NetworkError> { //! async fn connect_to_server() -> Result<(), NetworkError> { -//! let settings = ServerConnectionSettingsBuilder::transient("127.0.0.1:25021") +//! let settings = DefaultServerConnectionSettingsBuilder::transient("127.0.0.1:25021") //! .with_udp_mode(UdpMode::Enabled) //! .build()?; //! @@ -65,14 +65,14 @@ use uuid::Uuid; /// This [`SingleClientServerConnectionKernel`] is the base kernel type for other built-in implementations of [`NetKernel`]. /// It establishes connections to a central node for purposes of NAT traversal and peer discovery, and depending on the application layer, /// can leverage the client to server connection for other purposes that require communication between the two. -pub struct SingleClientServerConnectionKernel { +pub struct SingleClientServerConnectionKernel { handler: Mutex>, udp_mode: UdpMode, auth_info: Mutex>, session_security_settings: SessionSecuritySettings, unprocessed_signal_filter_tx: Mutex>>, - remote: Option, + remote: Option>, server_password: Option, rx_incoming_object_transfer_handle: Mutex>, tx_incoming_object_transfer_handle: @@ -99,9 +99,9 @@ pub(crate) enum ConnectionType { }, } -impl SingleClientServerConnectionKernel +impl SingleClientServerConnectionKernel where - F: FnOnce(ConnectionSuccess, ClientServerRemote) -> Fut + Send, + F: FnOnce(ConnectionSuccess, ClientServerRemote) -> Fut + Send, Fut: Future> + Send, { fn generate_object_transfer_handle() -> ( @@ -111,14 +111,14 @@ where let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let rx = FileTransferHandleRx { inner: rx, - conn_type: VirtualTargetType::LocalGroupServer { implicated_cid: 0 }, + conn_type: VirtualTargetType::LocalGroupServer { session_cid: 0 }, }; (tx, Mutex::new(Some(rx))) } /// Creates a new [`SingleClientServerConnectionKernel`] with the given settings. /// The [`ServerConnectionSettings`] must be provided, and the `on_channel_received` function will be called when the connection is established. - pub fn new(settings: ServerConnectionSettings, on_channel_received: F) -> Self { + pub fn new(settings: ServerConnectionSettings, on_channel_received: F) -> Self { let (udp_mode, session_security_settings) = (settings.udp_mode(), settings.session_security_settings()); let server_password = settings.pre_shared_key().cloned(); @@ -169,12 +169,12 @@ where } #[async_trait] -impl NetKernel for SingleClientServerConnectionKernel +impl NetKernel for SingleClientServerConnectionKernel where - F: FnOnce(ConnectionSuccess, ClientServerRemote) -> Fut + Send, + F: FnOnce(ConnectionSuccess, ClientServerRemote) -> Fut + Send, Fut: Future> + Send, { - fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { + fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { self.remote = Some(server_remote); Ok(()) } @@ -242,7 +242,7 @@ where ) .await?; let conn_type = VirtualTargetType::LocalGroupServer { - implicated_cid: connect_success.cid, + session_cid: connect_success.cid, }; let mut handle = { @@ -250,7 +250,7 @@ where lock.take().expect("Should not have been called before") }; - handle.conn_type.set_implicated_cid(connect_success.cid); + handle.conn_type.set_session_cid(connect_success.cid); let (reroute_tx, reroute_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); *self.unprocessed_signal_filter_tx.lock() = Some(reroute_tx); @@ -297,7 +297,7 @@ where #[cfg(test)] mod tests { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; - use crate::prefabs::client::ServerConnectionSettingsBuilder; + use crate::prefabs::client::DefaultServerConnectionSettingsBuilder; use crate::prefabs::ClientServerRemote; use crate::prelude::*; use crate::test_common::{server_info_reactive, wait_for_peers, TestBarrier}; @@ -313,7 +313,6 @@ mod tests { async fn on_server_received_conn( udp_mode: UdpMode, conn: ConnectionSuccess, - _remote: ClientServerRemote, ) -> Result<(), NetworkError> { crate::test_common::udp_mode_assertions(udp_mode, conn.udp_channel_rx).await; Ok(()) @@ -323,14 +322,14 @@ mod tests { feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, err(Debug)) )] - async fn default_server_harness( + async fn default_server_harness( udp_mode: UdpMode, conn: ConnectionSuccess, - remote: ClientServerRemote, + remote: ClientServerRemote, server_success: &AtomicBool, ) -> Result<(), NetworkError> { wait_for_peers().await; - on_server_received_conn(udp_mode, conn, remote.clone()).await?; + on_server_received_conn(udp_mode, conn).await?; server_success.store(true, Ordering::SeqCst); wait_for_peers().await; remote.shutdown_kernel().await @@ -357,7 +356,7 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); - let (server, server_addr) = server_info_reactive( + let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( move |conn, remote| async move { default_server_harness(udp_mode, conn, remote, server_success).await }, @@ -366,7 +365,7 @@ mod tests { }, ); - let client_settings = ServerConnectionSettingsBuilder::credentialed_registration( + let client_settings = DefaultServerConnectionSettingsBuilder::credentialed_registration( server_addr, "nologik", "Some Alias", @@ -388,7 +387,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let joined = futures::future::try_join(server, client); @@ -412,7 +411,7 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); - let (server, server_addr) = server_info_reactive( + let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( |conn, remote| async move { default_server_harness(udp_mode, conn, remote, server_success).await }, @@ -426,7 +425,7 @@ mod tests { let uuid = Uuid::new_v4(); let mut server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode); if let Some(server_password) = server_password { @@ -449,7 +448,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let joined = futures::future::try_join(server, client); @@ -470,7 +469,7 @@ mod tests { citadel_logging::setup_log(); TestBarrier::setup(2); - let (server, server_addr) = server_info_reactive( + let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( |_conn, _remote| async move { panic!("Server should not have connected") }, |opts| { if let Some(password) = server_password { @@ -482,7 +481,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .with_session_password("wrong-password") .build() @@ -493,7 +492,7 @@ mod tests { |_channel, _remote| async move { panic!("Client should not have connected") }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); tokio::select! { _res0 = server => { @@ -521,7 +520,7 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); - let (server, server_addr) = server_info_reactive( + let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( |conn, remote| async move { default_server_harness(udp_mode, conn, remote, server_success).await }, @@ -531,7 +530,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .build() .unwrap(); @@ -549,7 +548,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let joined = futures::future::try_join(server, client); @@ -570,7 +569,7 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); - let (server, server_addr) = server_info_reactive( + let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( |conn, remote| async move { default_server_harness(udp_mode, conn, remote, server_success).await }, @@ -580,7 +579,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .build() .unwrap(); @@ -626,7 +625,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let joined = futures::future::try_join(server, client); @@ -647,7 +646,7 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); - let (server, server_addr) = server_info_reactive( + let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( |conn, remote| async move { default_server_harness(udp_mode, conn, remote, server_success).await }, @@ -657,7 +656,7 @@ mod tests { let uuid = Uuid::new_v4(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(udp_mode) .build() .unwrap(); @@ -679,7 +678,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let joined = futures::future::try_join(server, client); diff --git a/citadel_sdk/src/prefabs/mod.rs b/citadel_sdk/src/prefabs/mod.rs index 57fca1202..e330dbcbb 100644 --- a/citadel_sdk/src/prefabs/mod.rs +++ b/citadel_sdk/src/prefabs/mod.rs @@ -18,7 +18,7 @@ //! use citadel_sdk::prelude::*; //! use citadel_sdk::prefabs::ClientServerRemote; //! -//! async fn handle_remote(mut remote: ClientServerRemote) -> Result<(), NetworkError> { +//! async fn handle_remote(mut remote: ClientServerRemote) -> Result<(), NetworkError> { //! // Get list of connected peers //! let peers = remote.get_peers(None).await?; //! @@ -65,8 +65,8 @@ use crate::remote_ext::ProtocolRemoteExt; /// A limited version of the [`NodeRemote`] designed to only allow shutdown calls to the protocol /// as well as several other functions #[derive(Clone)] -pub struct ClientServerRemote { - pub(crate) inner: NodeRemote, +pub struct ClientServerRemote { + pub(crate) inner: NodeRemote, pub(crate) unprocessed_signals_rx: Arc>>>, pub(crate) file_transfer_handle_rx: Arc>>, conn_type: VirtualTargetType, @@ -75,11 +75,11 @@ pub struct ClientServerRemote { impl_remote!(ClientServerRemote); -impl ClientServerRemote { +impl ClientServerRemote { /// constructs a new [`ClientServerRemote`] from a [`NodeRemote`] and a [`VirtualTargetType`] pub fn new( conn_type: VirtualTargetType, - remote: NodeRemote, + remote: NodeRemote, session_security_settings: SessionSecuritySettings, unprocessed_signals_rx: Option>, file_transfer_handle_rx: Option, @@ -111,11 +111,11 @@ impl ClientServerRemote { } } -impl TargetLockedRemote for ClientServerRemote { +impl TargetLockedRemote for ClientServerRemote { fn user(&self) -> &VirtualTargetType { &self.conn_type } - fn remote(&self) -> &NodeRemote { + fn remote(&self) -> &NodeRemote { &self.inner } fn target_username(&self) -> Option<&str> { @@ -130,7 +130,7 @@ impl TargetLockedRemote for ClientServerRemote { } } -impl ClientServerRemote { +impl ClientServerRemote { /// Gracefully closes the protocol and kernel executor pub async fn shutdown_kernel(self) -> Result<(), NetworkError> { self.inner.shutdown().await @@ -140,11 +140,8 @@ impl ClientServerRemote { &mut self, limit: Option, ) -> Result, NetworkError> { - let implicated_cid = self.conn_type.get_implicated_cid(); - let peer_info = self - .inner - .get_local_group_peers(implicated_cid, limit) - .await?; + let session_cid = self.conn_type.get_session_cid(); + let peer_info = self.inner.get_local_group_peers(session_cid, limit).await?; Ok(peer_info .iter() .map(|info| LocalGroupPeer { diff --git a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs index a8bb1bd2c..2cd51df83 100644 --- a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs +++ b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs @@ -35,13 +35,19 @@ //! use crate::prelude::*; +use std::marker::PhantomData; -#[derive(Default)] -pub struct AcceptFileTransferKernel; +pub struct AcceptFileTransferKernel(PhantomData); + +impl Default for AcceptFileTransferKernel { + fn default() -> Self { + Self(Default::default()) + } +} #[async_trait] -impl NetKernel for AcceptFileTransferKernel { - fn load_remote(&mut self, _node_remote: NodeRemote) -> Result<(), NetworkError> { +impl NetKernel for AcceptFileTransferKernel { + fn load_remote(&mut self, _node_remote: NodeRemote) -> Result<(), NetworkError> { Ok(()) } diff --git a/citadel_sdk/src/prefabs/server/client_connect_listener.rs b/citadel_sdk/src/prefabs/server/client_connect_listener.rs index b7b3dfce2..834cf1993 100644 --- a/citadel_sdk/src/prefabs/server/client_connect_listener.rs +++ b/citadel_sdk/src/prefabs/server/client_connect_listener.rs @@ -47,15 +47,15 @@ use std::marker::PhantomData; /// A kernel that executes a user-provided function each time /// a client makes a connection -pub struct ClientConnectListenerKernel { +pub struct ClientConnectListenerKernel { on_channel_received: F, - node_remote: Option, + node_remote: Option>, _pd: PhantomData, } -impl ClientConnectListenerKernel +impl ClientConnectListenerKernel where - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync, + F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync, Fut: Future> + Send + Sync, { pub fn new(on_channel_received: F) -> Self { @@ -68,12 +68,12 @@ where } #[async_trait] -impl NetKernel for ClientConnectListenerKernel +impl NetKernel for ClientConnectListenerKernel where - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync, + F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync, Fut: Future> + Send + Sync, { - fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { + fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { self.node_remote = Some(server_remote); Ok(()) } @@ -86,7 +86,7 @@ where match message { NodeResult::ConnectSuccess(ConnectSuccess { ticket: _, - implicated_cid: cid, + session_cid: cid, remote_addr: _, is_personal: _, v_conn_type: conn_type, diff --git a/citadel_sdk/src/prefabs/server/empty.rs b/citadel_sdk/src/prefabs/server/empty.rs index 25b7e9ec9..460a7437a 100644 --- a/citadel_sdk/src/prefabs/server/empty.rs +++ b/citadel_sdk/src/prefabs/server/empty.rs @@ -36,15 +36,21 @@ //! use citadel_proto::prelude::*; +use std::marker::PhantomData; /// A kernel that does nothing to events in the protocol, nor does it cause any requests. A server that allows any and all connections with no special handlers would benefit from the use of this kernel. /// This should never be used for interacting with peers/clients from the server, since to do so would deny the possibility of interacting with channels. -#[derive(Default)] -pub struct EmptyKernel; +pub struct EmptyKernel(PhantomData); + +impl Default for EmptyKernel { + fn default() -> Self { + Self(Default::default()) + } +} #[async_trait] -impl NetKernel for EmptyKernel { - fn load_remote(&mut self, _server_remote: NodeRemote) -> Result<(), NetworkError> { +impl NetKernel for EmptyKernel { + fn load_remote(&mut self, _server_remote: NodeRemote) -> Result<(), NetworkError> { Ok(()) } diff --git a/citadel_sdk/src/prefabs/server/internal_service.rs b/citadel_sdk/src/prefabs/server/internal_service.rs index 2b769d6e1..bbf28f4b1 100644 --- a/citadel_sdk/src/prefabs/server/internal_service.rs +++ b/citadel_sdk/src/prefabs/server/internal_service.rs @@ -68,12 +68,12 @@ use crate::prelude::*; use std::future::Future; use std::marker::PhantomData; -pub struct InternalServiceKernel<'a, F, Fut> { - inner_kernel: Box, +pub struct InternalServiceKernel<'a, F, Fut, R: Ratchet = StackedRatchet> { + inner_kernel: Box + 'a>, _pd: PhantomData (&'a F, Fut)>, } -impl InternalServiceKernel<'_, F, Fut> +impl InternalServiceKernel<'_, F, Fut, R> where F: Send + Copy + Sync + FnOnce(InternalServerCommunicator) -> Fut, Fut: Send + Sync + Future>, @@ -98,8 +98,8 @@ where } #[async_trait] -impl NetKernel for InternalServiceKernel<'_, F, Fut> { - fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError> { +impl NetKernel for InternalServiceKernel<'_, F, Fut, R> { + fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError> { self.inner_kernel.load_remote(node_remote) } @@ -119,7 +119,7 @@ impl NetKernel for InternalServiceKernel<'_, F, Fut> { #[cfg(test)] mod test { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; - use crate::prefabs::client::ServerConnectionSettingsBuilder; + use crate::prefabs::client::DefaultServerConnectionSettingsBuilder; use crate::prefabs::server::internal_service::InternalServiceKernel; use crate::prefabs::shared::internal_service::InternalServerCommunicator; use crate::prelude::*; @@ -134,7 +134,6 @@ mod test { use std::convert::Infallible; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Duration; - use uuid::Uuid; #[derive(serde::Serialize, serde::Deserialize)] struct TestPacket { @@ -198,7 +197,7 @@ mod test { }); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_bind_addr, Uuid::new_v4()) + DefaultServerConnectionSettingsBuilder::transient(server_bind_addr) .build() .unwrap(); @@ -222,12 +221,12 @@ mod test { }, ); - let client = NodeBuilder::default() + let client = DefaultNodeBuilder::default() .with_node_type(NodeType::Peer) .build(client_kernel) .unwrap(); - let server = NodeBuilder::default() + let server = DefaultNodeBuilder::default() .with_node_type(NodeType::Server(server_bind_addr)) .with_underlying_protocol( ServerUnderlyingProtocol::from_tokio_tcp_listener(server_listener).unwrap(), @@ -279,7 +278,7 @@ mod test { }); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_bind_addr, Uuid::new_v4()) + DefaultServerConnectionSettingsBuilder::transient(server_bind_addr) .build() .unwrap(); @@ -331,12 +330,12 @@ mod test { }, ); - let client = NodeBuilder::default() + let client = DefaultNodeBuilder::default() .with_node_type(NodeType::Peer) .build(client_kernel) .unwrap(); - let server = NodeBuilder::default() + let server = DefaultNodeBuilder::default() .with_node_type(NodeType::Server(server_bind_addr)) .with_underlying_protocol( ServerUnderlyingProtocol::from_tokio_tcp_listener(server_listener).unwrap(), diff --git a/citadel_sdk/src/prefabs/shared/internal_service.rs b/citadel_sdk/src/prefabs/shared/internal_service.rs index 466898600..7e9ef5539 100644 --- a/citadel_sdk/src/prefabs/shared/internal_service.rs +++ b/citadel_sdk/src/prefabs/shared/internal_service.rs @@ -38,7 +38,7 @@ //! - [`SecBuffer`]: Secure data handling //! -use crate::prelude::{ConnectionSuccess, TargetLockedRemote}; +use crate::prelude::{ConnectionSuccess, Ratchet, TargetLockedRemote}; use bytes::Bytes; use citadel_io::tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use citadel_proto::prelude::NetworkError; @@ -49,7 +49,7 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -pub async fn internal_service( +pub async fn internal_service( remote: R, connect_success: ConnectionSuccess, service: F, @@ -57,7 +57,7 @@ pub async fn internal_service( where F: Send + Copy + Sync + FnOnce(InternalServerCommunicator) -> Fut, Fut: Send + Sync + Future>, - R: TargetLockedRemote, + R: TargetLockedRemote, { let (tx_to_service, rx_from_kernel) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let (tx_to_kernel, mut rx_from_service) = citadel_io::tokio::sync::mpsc::unbounded_channel(); diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index 19675f366..0c83ff724 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -17,7 +17,7 @@ //! ```rust //! use citadel_sdk::prelude::*; //! -//! async fn example(remote: NodeRemote) -> Result<(), NetworkError> { +//! async fn example(remote: NodeRemote) -> Result<(), NetworkError> { //! // Register a new user //! let reg = remote.register_with_defaults( //! "127.0.0.1:25021", @@ -71,14 +71,14 @@ pub(crate) mod user_ids { #[derive(Debug)] /// A reference to a user identifier - pub struct SymmetricIdentifierHandleRef<'a> { + pub struct SymmetricIdentifierHandleRef<'a, R: Ratchet> { pub(crate) user: VirtualTargetType, - pub(crate) remote: &'a NodeRemote, + pub(crate) remote: &'a NodeRemote, pub(crate) target_username: Option, } - impl SymmetricIdentifierHandleRef<'_> { - pub fn into_owned(self) -> SymmetricIdentifierHandle { + impl SymmetricIdentifierHandleRef<'_, R> { + pub fn into_owned(self) -> SymmetricIdentifierHandle { SymmetricIdentifierHandle { user: self.user, remote: self.remote.clone(), @@ -89,25 +89,25 @@ pub(crate) mod user_ids { #[derive(Clone, Debug)] /// A convenience structure for executing commands that depend on a specific registered user - pub struct SymmetricIdentifierHandle { + pub struct SymmetricIdentifierHandle { user: VirtualTargetType, - remote: NodeRemote, + remote: NodeRemote, target_username: Option, } - pub trait TargetLockedRemote: Send + Sync { + pub trait TargetLockedRemote: Send + Sync { fn user(&self) -> &VirtualTargetType; - fn remote(&self) -> &NodeRemote; + fn remote(&self) -> &NodeRemote; fn target_username(&self) -> Option<&str>; fn user_mut(&mut self) -> &mut VirtualTargetType; fn session_security_settings(&self) -> Option<&SessionSecuritySettings>; } - impl TargetLockedRemote for SymmetricIdentifierHandleRef<'_> { + impl TargetLockedRemote for SymmetricIdentifierHandleRef<'_, R> { fn user(&self) -> &VirtualTargetType { &self.user } - fn remote(&self) -> &NodeRemote { + fn remote(&self) -> &NodeRemote { self.remote } fn target_username(&self) -> Option<&str> { @@ -122,11 +122,11 @@ pub(crate) mod user_ids { } } - impl TargetLockedRemote for SymmetricIdentifierHandle { + impl TargetLockedRemote for SymmetricIdentifierHandle { fn user(&self) -> &VirtualTargetType { &self.user } - fn remote(&self) -> &NodeRemote { + fn remote(&self) -> &NodeRemote { &self.remote } fn target_username(&self) -> Option<&str> { @@ -141,22 +141,22 @@ pub(crate) mod user_ids { } } - impl From> for SymmetricIdentifierHandle { - fn from(this: SymmetricIdentifierHandleRef<'_>) -> Self { + impl From> for SymmetricIdentifierHandle { + fn from(this: SymmetricIdentifierHandleRef<'_, R>) -> Self { this.into_owned() } } - impl Deref for SymmetricIdentifierHandle { - type Target = NodeRemote; + impl Deref for SymmetricIdentifierHandle { + type Target = NodeRemote; fn deref(&self) -> &Self::Target { &self.remote } } - impl Deref for SymmetricIdentifierHandleRef<'_> { - type Target = NodeRemote; + impl Deref for SymmetricIdentifierHandleRef<'_, R> { + type Target = NodeRemote; fn deref(&self) -> &Self::Target { self.remote @@ -183,18 +183,18 @@ pub struct RegisterSuccess { #[async_trait] /// Endows the [NodeRemote](NodeRemote) with additional functions -pub trait ProtocolRemoteExt: Remote { +pub trait ProtocolRemoteExt: Remote { /// Registers with custom settings /// Returns a ticket which is used to uniquely identify the request in the protocol async fn register< T: std::net::ToSocketAddrs + Send, - R: Into + Send, + P: Into + Send, V: Into + Send, K: Into + Send, >( &self, addr: T, - full_name: R, + full_name: P, username: V, proposed_password: K, default_security_settings: SessionSecuritySettings, @@ -216,10 +216,8 @@ pub trait ProtocolRemoteExt: Remote { let mut subscription = self.send_callback_subscription(register_request).await?; while let Some(status) = subscription.next().await { match map_errors(status)? { - NodeResult::RegisterOkay(RegisterOkay { cnac, .. }) => { - return Ok(RegisterSuccess { - cid: cnac.get_cid(), - }); + NodeResult::RegisterOkay(RegisterOkay { cid, .. }) => { + return Ok(RegisterSuccess { cid }); } NodeResult::RegisterFailure(err) => { return Err(NetworkError::Generic(err.error_message)); @@ -242,13 +240,13 @@ pub trait ProtocolRemoteExt: Remote { /// Returns a ticket which is used to uniquely identify the request in the protocol async fn register_with_defaults< T: std::net::ToSocketAddrs + Send, - R: Into + Send, + P: Into + Send, V: Into + Send, K: Into + Send, >( &self, addr: T, - full_name: R, + full_name: P, username: V, proposed_password: K, ) -> Result { @@ -294,7 +292,7 @@ pub trait ProtocolRemoteExt: Remote { return match map_errors(status)? { NodeResult::ConnectSuccess(ConnectSuccess { ticket: _, - implicated_cid: cid, + session_cid: cid, remote_addr: _, is_personal: _, v_conn_type: _, @@ -346,18 +344,18 @@ pub trait ProtocolRemoteExt: Remote { /// use citadel_sdk::prelude::*; /// # use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel; /// - /// let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_login("127.0.0.1:25021", "john.doe", "password").build().unwrap(); + /// let server_connection_settings = DefaultServerConnectionSettingsBuilder::credentialed_login("127.0.0.1:25021", "john.doe", "password").build().unwrap(); /// /// # SingleClientServerConnectionKernel::new(server_connection_settings, |_, mut remote| async move { /// remote.find_target("my_account", "my_peer").await?.send_file("/path/to/file.pdf").await /// // or: remote.find_target(1234, "my_peer").await? [...] /// # }); /// ``` - async fn find_target + Send, R: Into + Send>( + async fn find_target + Send, P: Into + Send>( &self, local_user: T, - peer: R, - ) -> Result, NetworkError> { + peer: P, + ) -> Result, NetworkError> { let account_manager = self.account_manager(); account_manager .find_target_information(local_user, peer) @@ -366,7 +364,7 @@ pub trait ProtocolRemoteExt: Remote { if peer.parent_icid != 0 { SymmetricIdentifierHandleRef { user: VirtualTargetType::ExternalGroupPeer { - implicated_cid: cid, + session_cid: cid, interserver_cid: peer.parent_icid, peer_cid: peer.cid, }, @@ -376,7 +374,7 @@ pub trait ProtocolRemoteExt: Remote { } else { SymmetricIdentifierHandleRef { user: VirtualTargetType::LocalGroupPeer { - implicated_cid: cid, + session_cid: cid, peer_cid: peer.cid, }, remote: self.remote_ref(), @@ -393,12 +391,12 @@ pub trait ProtocolRemoteExt: Remote { &self, local_user: T, peer: P, - ) -> Result, NetworkError> { - let local_cid = self.get_implicated_cid(local_user).await?; + ) -> Result, NetworkError> { + let local_cid = self.get_session_cid(local_user).await?; match peer.into() { UserIdentifier::ID(peer_cid) => Ok(SymmetricIdentifierHandleRef { user: VirtualTargetType::LocalGroupPeer { - implicated_cid: local_cid, + session_cid: local_cid, peer_cid, }, remote: self.remote_ref(), @@ -414,7 +412,7 @@ pub trait ProtocolRemoteExt: Remote { .unwrap_or(0); Ok(SymmetricIdentifierHandleRef { user: VirtualTargetType::LocalGroupPeer { - implicated_cid: local_cid, + session_cid: local_cid, peer_cid, }, remote: self.remote_ref(), @@ -431,9 +429,9 @@ pub trait ProtocolRemoteExt: Remote { local_user: T, limit: Option, ) -> Result, NetworkError> { - let local_cid = self.get_implicated_cid(local_user).await?; + let local_cid = self.get_session_cid(local_user).await?; let command = NodeRequest::PeerCommand(PeerCommand { - implicated_cid: local_cid, + session_cid: local_cid, command: PeerSignal::GetRegisteredPeers { peer_conn_type: NodeConnectionType::LocalGroupPeerToLocalGroupServer(local_cid), response: None, @@ -480,9 +478,9 @@ pub trait ProtocolRemoteExt: Remote { &self, local_user: T, ) -> Result, NetworkError> { - let local_cid = self.get_implicated_cid(local_user).await?; + let local_cid = self.get_session_cid(local_user).await?; let command = NodeRequest::PeerCommand(PeerCommand { - implicated_cid: local_cid, + session_cid: local_cid, command: PeerSignal::GetMutuals { v_conn_type: NodeConnectionType::LocalGroupPeerToLocalGroupServer(local_cid), response: None, @@ -523,10 +521,10 @@ pub trait ProtocolRemoteExt: Remote { } #[doc(hidden)] - fn remote_ref(&self) -> &NodeRemote; + fn remote_ref(&self) -> &NodeRemote; #[doc(hidden)] - async fn get_implicated_cid + Send>( + async fn get_session_cid + Send>( &self, local_user: T, ) -> Result { @@ -568,21 +566,21 @@ pub fn map_errors(result: NodeResult) -> Result { } } -impl ProtocolRemoteExt for NodeRemote { - fn remote_ref(&self) -> &NodeRemote { +impl ProtocolRemoteExt for NodeRemote { + fn remote_ref(&self) -> &NodeRemote { self } } -impl ProtocolRemoteExt for ClientServerRemote { - fn remote_ref(&self) -> &NodeRemote { +impl ProtocolRemoteExt for ClientServerRemote { + fn remote_ref(&self) -> &NodeRemote { &self.inner } } #[async_trait] /// Some functions require that a target exists -pub trait ProtocolRemoteTargetExt: TargetLockedRemote { +pub trait ProtocolRemoteTargetExt: TargetLockedRemote { /// Sends a file with a custom size. The smaller the chunks, the higher the degree of scrambling, but the higher the performance cost. A chunk size of zero will use the default async fn send_file_with_custom_opts( &self, @@ -595,7 +593,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { } else { Some(chunk_size) }; - let implicated_cid = self.user().get_implicated_cid(); + let session_cid = self.user().get_session_cid(); let user = *self.user(); let remote = self.remote(); @@ -603,7 +601,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { .send_callback_subscription(NodeRequest::SendObject(SendObject { source: Box::new(source), chunk_size, - implicated_cid, + session_cid, v_conn_type: user, transfer_type, })) @@ -642,11 +640,11 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { /// Only this local node may decrypt the information send to the adjacent node. async fn remote_encrypted_virtual_filesystem_push_custom_chunking< T: ObjectSource, - R: Into + Send, + P: Into + Send, >( &self, source: T, - virtual_directory: R, + virtual_directory: P, chunk_size: usize, security_level: SecurityLevel, ) -> Result<(), NetworkError> { @@ -665,10 +663,10 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { /// Sends a file to the provided target using the default chunking size with local encryption. /// Only this local node may decrypt the information send to the adjacent node. - async fn remote_encrypted_virtual_filesystem_push + Send>( + async fn remote_encrypted_virtual_filesystem_push + Send>( &self, source: T, - virtual_directory: R, + virtual_directory: P, security_level: SecurityLevel, ) -> Result<(), NetworkError> { self.remote_encrypted_virtual_filesystem_push_custom_chunking( @@ -682,9 +680,9 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { /// Pulls a virtual file from the RE-VFS. If `delete_on_pull` is true, then, the virtual file /// will be taken from the RE-VFS - async fn remote_encrypted_virtual_filesystem_pull + Send>( + async fn remote_encrypted_virtual_filesystem_pull + Send>( &self, - virtual_directory: R, + virtual_directory: P, transfer_security_level: SecurityLevel, delete_on_pull: bool, ) -> Result { @@ -729,9 +727,9 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { /// Deletes the file from the RE-VFS. If the contents are desired on delete, /// consider calling `Self::remote_encrypted_virtual_filesystem_pull` with the delete /// parameter set to true - async fn remote_encrypted_virtual_filesystem_delete + Send>( + async fn remote_encrypted_virtual_filesystem_delete + Send>( &self, - virtual_directory: R, + virtual_directory: P, ) -> Result<(), NetworkError> { self.can_use_revfs()?; let request = NodeRequest::DeleteObject(DeleteObject { @@ -766,14 +764,14 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { session_security_settings: SessionSecuritySettings, udp_mode: UdpMode, peer_session_password: Option, - ) -> Result { - let implicated_cid = self.user().get_implicated_cid(); + ) -> Result, NetworkError> { + let session_cid = self.user().get_session_cid(); let peer_target = self.try_as_peer_connection().await?; let mut stream = self .remote() .send_callback_subscription(NodeRequest::PeerCommand(PeerCommand { - implicated_cid, + session_cid, command: PeerSignal::PostConnect { peer_conn_type: peer_target, ticket_opt: None, @@ -834,20 +832,20 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { } /// Connects to the target peer with default settings - async fn connect_to_peer(&self) -> Result { + async fn connect_to_peer(&self) -> Result, NetworkError> { self.connect_to_peer_custom(Default::default(), Default::default(), Default::default()) .await } /// Posts a registration request to a peer async fn register_to_peer(&self) -> Result { - let implicated_cid = self.user().get_implicated_cid(); + let session_cid = self.user().get_session_cid(); let peer_target = self.try_as_peer_connection().await?; // TODO: Get rid of this step. Should be handled by the protocol let local_username = self .remote() .account_manager() - .get_username_by_cid(implicated_cid) + .get_username_by_cid(session_cid) .await? .ok_or_else(|| NetworkError::msg("Unable to find username for local user"))?; let peer_username_opt = self.target_username().map(ToString::to_string); @@ -855,7 +853,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { let mut stream = self .remote() .send_callback_subscription(NodeRequest::PeerCommand(PeerCommand { - implicated_cid, + session_cid, command: PeerSignal::PostRegister { peer_conn_type: peer_target, inviter_username: local_username, @@ -903,9 +901,9 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { let peer_request = PeerSignal::Deregister { peer_conn_type: peer_conn, }; - let implicated_cid = self.user().get_implicated_cid(); + let session_cid = self.user().get_session_cid(); let request = NodeRequest::PeerCommand(PeerCommand { - implicated_cid, + session_cid, command: peer_request, }); @@ -922,21 +920,21 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { } } else { // c2s conn - let cid = self.user().get_implicated_cid(); + let cid = self.user().get_session_cid(); let request = NodeRequest::DeregisterFromHypernode(DeregisterFromHypernode { - implicated_cid: cid, + session_cid: cid, v_conn_type: *self.user(), }); let mut subscription = self.remote().send_callback_subscription(request).await?; while let Some(result) = subscription.next().await { match map_errors(result)? { NodeResult::DeRegistration(DeRegistration { - implicated_cid: _, + session_cid: _, ticket_opt: _, success: true, }) => return Ok(()), NodeResult::DeRegistration(DeRegistration { - implicated_cid: _, + session_cid: _, ticket_opt: _, success: false, }) => { @@ -956,12 +954,12 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { async fn disconnect(&self) -> Result<(), NetworkError> { if let Ok(peer_conn) = self.try_as_peer_connection().await { if let PeerConnectionType::LocalGroupPeer { - implicated_cid, + session_cid, peer_cid: _, } = peer_conn { let request = NodeRequest::PeerCommand(PeerCommand { - implicated_cid, + session_cid, command: PeerSignal::Disconnect { peer_conn_type: peer_conn, disconnect_response: None, @@ -995,10 +993,9 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { } } else { //c2s conn - let cid = self.user().get_implicated_cid(); - let request = NodeRequest::DisconnectFromHypernode(DisconnectFromHypernode { - implicated_cid: cid, - }); + let cid = self.user().get_session_cid(); + let request = + NodeRequest::DisconnectFromHypernode(DisconnectFromHypernode { session_cid: cid }); let mut subscription = self.remote().send_callback_subscription(request).await?; while let Some(event) = subscription.next().await { @@ -1024,7 +1021,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { &self, initial_users_to_invite: Option>, ) -> Result { - let implicated_cid = self.user().get_implicated_cid(); + let session_cid = self.user().get_session_cid(); let mut initial_users = vec![]; // TODO: allow for custom message group options. For now, don't @@ -1037,12 +1034,12 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { initial_users.push( self.remote() .account_manager() - .find_target_information(implicated_cid, user.clone()) + .find_target_information(session_cid, user.clone()) .await .map_err(|err| NetworkError::msg(err.into_string()))? .ok_or_else(|| { NetworkError::msg(format!( - "Account {user:?} not found for local user {implicated_cid:?}" + "Account {user:?} not found for local user {session_cid:?}" )) }) .map(|r| r.1.cid)?, @@ -1055,7 +1052,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { options, }; let request = NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand { - implicated_cid, + session_cid, command: group_request, }); let mut subscription = self.remote().send_callback_subscription(request).await?; @@ -1065,7 +1062,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { if let NodeResult::GroupChannelCreated(GroupChannelCreated { ticket: _, channel, - implicated_cid: _, + session_cid: _, }) = evt { return Ok(channel); @@ -1079,16 +1076,16 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { /// Lists all groups that which the current peer owns async fn list_owned_groups(&self) -> Result, NetworkError> { - let implicated_cid = self.user().get_implicated_cid(); + let session_cid = self.user().get_session_cid(); let cid_to_check_for = match self.try_as_peer_connection().await { Ok(res) => res.get_original_target_cid(), - _ => implicated_cid, + _ => session_cid, }; let group_request = GroupBroadcast::ListGroupsFor { cid: cid_to_check_for, }; let request = NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand { - implicated_cid, + session_cid, command: group_request, }); @@ -1096,7 +1093,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { while let Some(evt) = subscription.next().await { if let NodeResult::GroupEvent(GroupEvent { - implicated_cid: _, + session_cid: _, ticket: _, event: GroupBroadcast::ListResponse { groups }, }) = map_errors(evt)? @@ -1138,7 +1135,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { async fn is_peer_registered(&self) -> Result { let target = self.try_as_peer_connection().await?; if let PeerConnectionType::LocalGroupPeer { - implicated_cid: local_cid, + session_cid: local_cid, peer_cid, } = target { @@ -1165,7 +1162,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { let peer_username = self .target_username() .ok_or_else(|| NetworkError::msg("target_cid=0, yet, no username was provided"))?; - let implicated_cid = self.user().get_implicated_cid(); + let session_cid = self.user().get_session_cid(); let expected_peer_cid = self .remote() .account_manager() @@ -1176,7 +1173,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { let peer_cid = self .remote() .account_manager() - .find_target_information(implicated_cid, peer_username) + .find_target_information(session_cid, peer_username) .await .map_err(|err| NetworkError::Generic(err.into_string()))? .map(|r| r.1.cid) @@ -1208,26 +1205,35 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { } } -impl ProtocolRemoteTargetExt for T {} +impl, R: Ratchet> ProtocolRemoteTargetExt for T {} pub mod results { use crate::prefabs::client::peer_connection::FileTransferHandleRx; use crate::prelude::{PeerChannel, UdpChannel}; use crate::remote_ext::remote_specialization::PeerRemote; use citadel_io::tokio::sync::oneshot::Receiver; - use citadel_proto::prelude::NetworkError; + use citadel_proto::prelude::{NetworkError, Ratchet}; + use std::fmt::Debug; - #[derive(Debug)] - pub struct PeerConnectSuccess { + pub struct PeerConnectSuccess { pub channel: PeerChannel, pub udp_channel_rx: Option>, - pub remote: PeerRemote, + pub remote: PeerRemote, /// Receives incoming file/object transfer requests. The handles must be /// .accepted() before the file/object transfer is allowed to proceed pub(crate) incoming_object_transfer_handles: Option, } - impl PeerConnectSuccess { + impl Debug for PeerConnectSuccess { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PeerConnectSuccess") + .field("channel", &self.channel) + .field("udp_channel_rx", &self.udp_channel_rx) + .finish() + } + } + + impl PeerConnectSuccess { /// Obtains a receiver which yields incoming file/object transfer handles pub fn get_incoming_file_transfer_handle( &mut self, @@ -1265,18 +1271,18 @@ pub mod remote_specialization { use crate::prelude::*; #[derive(Debug, Clone)] - pub struct PeerRemote { - pub(crate) inner: NodeRemote, + pub struct PeerRemote { + pub(crate) inner: NodeRemote, pub(crate) peer: VirtualTargetType, pub(crate) username: Option, pub(crate) session_security_settings: SessionSecuritySettings, } - impl TargetLockedRemote for PeerRemote { + impl TargetLockedRemote for PeerRemote { fn user(&self) -> &VirtualTargetType { &self.peer } - fn remote(&self) -> &NodeRemote { + fn remote(&self) -> &NodeRemote { &self.inner } fn target_username(&self) -> Option<&str> { @@ -1295,7 +1301,7 @@ pub mod remote_specialization { #[cfg(test)] mod tests { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; - use crate::prefabs::client::ServerConnectionSettingsBuilder; + use crate::prefabs::client::DefaultServerConnectionSettingsBuilder; use crate::prelude::*; use citadel_io::tokio; use rstest::rstest; @@ -1304,11 +1310,14 @@ mod tests { use std::sync::Arc; use uuid::Uuid; - pub struct ReceiverFileTransferKernel(pub Option, pub Arc); + pub struct ReceiverFileTransferKernel( + pub Option>, + pub Arc, + ); #[async_trait] - impl NetKernel for ReceiverFileTransferKernel { - fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError> { + impl NetKernel for ReceiverFileTransferKernel { + fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError> { self.0 = Some(node_remote); Ok(()) } @@ -1366,9 +1375,9 @@ mod tests { } } - pub fn server_info<'a>( + pub fn server_info<'a, R: Ratchet>( switch: Arc, - ) -> (NodeFuture<'a, ReceiverFileTransferKernel>, SocketAddr) { + ) -> (NodeFuture<'a, ReceiverFileTransferKernel>, SocketAddr) { crate::test_common::server_test_node(ReceiverFileTransferKernel(None, switch), |_| {}) } @@ -1393,7 +1402,7 @@ mod tests { citadel_logging::setup_log(); let client_success = &AtomicBool::new(false); let server_success = &Arc::new(AtomicBool::new(false)); - let (server, server_addr) = server_info(server_success.clone()); + let (server, server_addr) = server_info::(server_success.clone()); let uuid = Uuid::new_v4(); let session_security_settings = SessionSecuritySettingsBuilder::default() @@ -1402,7 +1411,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_session_security_settings(session_security_settings) .disable_udp() .build() @@ -1426,7 +1435,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let joined = futures::future::try_join(server, client); diff --git a/citadel_sdk/src/responses.rs b/citadel_sdk/src/responses.rs index 751ffa07a..7d0b73cce 100644 --- a/citadel_sdk/src/responses.rs +++ b/citadel_sdk/src/responses.rs @@ -17,9 +17,9 @@ //! use citadel_sdk::prelude::*; //! use citadel_sdk::responses; //! -//! async fn handle_peer_request( +//! async fn handle_peer_request( //! signal: PeerSignal, -//! remote: &impl Remote +//! remote: &impl Remote //! ) -> Result<(), NetworkError> { //! // Accept a peer registration request //! let ticket = responses::peer_register(signal, true, remote).await?; @@ -44,10 +44,10 @@ use crate::prelude::*; /// Given the `input_signal` from the peer, this function sends a register response to the target peer -pub async fn peer_register( +pub async fn peer_register( input_signal: PeerSignal, accept: bool, - remote: &impl Remote, + remote: &impl Remote, ) -> Result { if let PeerSignal::PostRegister { peer_conn_type: v_conn, @@ -85,7 +85,7 @@ pub async fn peer_register( .send_with_custom_ticket( ticket, NodeRequest::PeerCommand(PeerCommand { - implicated_cid: this_cid, + session_cid: this_cid, command: signal, }), ) @@ -99,10 +99,10 @@ pub async fn peer_register( } /// Given the `input_signal` from the peer, this function sends a connect response to the target peer -pub async fn peer_connect( +pub async fn peer_connect( input_signal: PeerSignal, accept: bool, - remote: &impl Remote, + remote: &impl Remote, peer_session_password: Option, ) -> Result { if let PeerSignal::PostConnect { @@ -124,7 +124,7 @@ pub async fn peer_connect( }; let signal = NodeRequest::PeerCommand(PeerCommand { - implicated_cid: this_cid, + session_cid: this_cid, command: PeerSignal::PostConnect { peer_conn_type: v_conn.reverse(), ticket_opt: Some(ticket), @@ -146,13 +146,13 @@ pub async fn peer_connect( } /// Given a group invite signal, this function sends a response to the server -pub async fn group_invite( +pub async fn group_invite( invite_signal: NodeResult, accept: bool, - remote: &impl Remote, + remote: &impl Remote, ) -> Result { if let NodeResult::GroupEvent(GroupEvent { - implicated_cid: cid, + session_cid: cid, ticket, event: GroupBroadcast::Invitation { sender: _, key }, }) = invite_signal @@ -164,7 +164,7 @@ pub async fn group_invite( }; let request = NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand { - implicated_cid: cid, + session_cid: cid, command: resp, }); remote diff --git a/citadel_sdk/src/test_common.rs b/citadel_sdk/src/test_common.rs index 4e3f38f5c..844f38e2a 100644 --- a/citadel_sdk/src/test_common.rs +++ b/citadel_sdk/src/test_common.rs @@ -32,7 +32,7 @@ //! - P2P assertions validate peer connections //! //! # Related Components -//! - [`NodeBuilder`]: Server configuration builder +//! - [`DefaultNodeBuilder`]: Server configuration builder //! - [`EmptyKernel`]: Basic test server kernel //! - [`UdpMode`]: UDP connection configuration //! - [`PeerConnectSuccess`]: Peer connection validation @@ -52,9 +52,9 @@ use std::str::FromStr; use std::time::Duration; #[allow(dead_code)] -pub fn server_test_node<'a, K: NetKernel + 'a>( +pub fn server_test_node<'a, K: NetKernel + 'a, R: Ratchet>( kernel: K, - opts: impl FnOnce(&mut NodeBuilder), + opts: impl FnOnce(&mut NodeBuilder), ) -> (NodeFuture<'a, K>, SocketAddr) { let mut builder = NodeBuilder::default(); let tcp_listener = citadel_wire::socket_helpers::get_tcp_listener("127.0.0.1:0") @@ -68,33 +68,33 @@ pub fn server_test_node<'a, K: NetKernel + 'a>( (opts)(builder); - (builder.build(kernel).unwrap(), bind_addr) + (builder.build::(kernel).unwrap(), bind_addr) } #[allow(dead_code)] #[cfg(feature = "localhost-testing")] -pub fn server_info<'a>() -> (NodeFuture<'a, EmptyKernel>, SocketAddr) { - crate::test_common::server_test_node(EmptyKernel, |_| {}) +pub fn server_info<'a, R: Ratchet>() -> (NodeFuture<'a, EmptyKernel>, SocketAddr) { + crate::test_common::server_test_node(EmptyKernel::::default(), |_| {}) } #[allow(dead_code)] #[cfg(not(feature = "localhost-testing"))] -pub fn server_info<'a>() -> (NodeFuture<'a, EmptyKernel>, SocketAddr) { +pub fn server_info<'a, R: Ratchet>() -> (NodeFuture<'a, EmptyKernel>, SocketAddr) { panic!("Function server_info is not available without the localhost-testing feature"); } #[allow(dead_code)] #[cfg(feature = "localhost-testing")] -pub fn server_info_reactive<'a, F, Fut>( +pub fn server_info_reactive<'a, F, Fut, R: Ratchet>( f: F, - opts: impl FnOnce(&mut NodeBuilder), -) -> (NodeFuture<'a, Box>, SocketAddr) + opts: impl FnOnce(&mut NodeBuilder), +) -> (NodeFuture<'a, Box + 'a>>, SocketAddr) where - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync + 'a, + F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync + 'a, Fut: Future> + Send + Sync + 'a, { server_test_node( - Box::new(ClientConnectListenerKernel::new(f)) as Box, + Box::new(ClientConnectListenerKernel::<_, _, R>::new(f)) as Box>, opts, ) } @@ -103,12 +103,13 @@ where #[cfg(not(feature = "localhost-testing"))] pub fn server_info_reactive< 'a, - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync + 'a, + F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync + 'a, Fut: Future> + Send + Sync + 'a, + R: Ratchet, >( _f: F, - _opts: impl FnOnce(&mut NodeBuilder), -) -> (NodeFuture<'a, Box>, SocketAddr) { + _opts: impl FnOnce(&mut NodeBuilder), +) -> (NodeFuture<'a, Box + 'a>>, SocketAddr) { panic!("Function server_info_reactive is not available without the localhost-testing feature"); } @@ -256,18 +257,18 @@ pub async fn udp_mode_assertions( #[cfg(feature = "localhost-testing")] #[allow(dead_code)] -pub async fn p2p_assertions(implicated_cid: u64, conn_success: &PeerConnectSuccess) { +pub async fn p2p_assertions(session_cid: u64, conn_success: &PeerConnectSuccess) { log::info!(target: "citadel", "Inside p2p assertions ..."); let peer_cid = conn_success.channel.get_peer_cid(); - assert_eq!(implicated_cid, conn_success.channel.get_implicated_cid()); - assert_ne!(implicated_cid, peer_cid); + assert_eq!(session_cid, conn_success.channel.get_session_cid()); + assert_ne!(session_cid, peer_cid); assert!(conn_success .remote .inner .account_manager() .get_persistence_handler() - .hyperlan_peer_exists(implicated_cid, peer_cid) + .hyperlan_peer_exists(session_cid, peer_cid) .await .unwrap()); @@ -276,4 +277,4 @@ pub async fn p2p_assertions(implicated_cid: u64, conn_success: &PeerConnectSucce #[cfg(not(feature = "localhost-testing"))] #[allow(dead_code)] -pub async fn p2p_assertions(_implicated_cid: u64, _conn_success: &PeerConnectSuccess) {} +pub async fn p2p_assertions(_session_cid: u64, _conn_success: &PeerConnectSuccess) {} diff --git a/citadel_sdk/tests/stress_tests.rs b/citadel_sdk/tests/stress_tests.rs index 28b955bf6..731b4338b 100644 --- a/citadel_sdk/tests/stress_tests.rs +++ b/citadel_sdk/tests/stress_tests.rs @@ -6,7 +6,9 @@ mod tests { use citadel_sdk::prefabs::client::broadcast::{BroadcastKernel, GroupInitRequestType}; use citadel_sdk::prefabs::client::peer_connection::PeerConnectionKernel; use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel; - use citadel_sdk::prefabs::client::ServerConnectionSettingsBuilder; + use citadel_sdk::prefabs::client::{ + DefaultServerConnectionSettingsBuilder, ServerConnectionSettingsBuilder, + }; use citadel_sdk::prelude::*; use citadel_sdk::test_common::{server_info, wait_for_peers}; use citadel_types::crypto::{EncryptionAlgorithm, KemAlgorithm}; @@ -224,16 +226,17 @@ mod tests { let spawner = TestSpawner::new(); - let (server, server_addr) = citadel_sdk::test_common::server_info_reactive( - move |conn, remote| async move { - log::trace!(target: "citadel", "*** SERVER RECV CHANNEL ***"); - handle_send_receive_e2e(get_barrier(), conn.channel, message_count).await?; - log::trace!(target: "citadel", "***SERVER TEST SUCCESS***"); - SERVER_SUCCESS.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await - }, - |_| {}, - ); + let (server, server_addr) = + citadel_sdk::test_common::server_info_reactive::<_, _, StackedRatchet>( + move |conn, remote| async move { + log::trace!(target: "citadel", "*** SERVER RECV CHANNEL ***"); + handle_send_receive_e2e(get_barrier(), conn.channel, message_count).await?; + log::trace!(target: "citadel", "***SERVER TEST SUCCESS***"); + SERVER_SUCCESS.store(true, Ordering::Relaxed); + remote.shutdown_kernel().await + }, + |_| {}, + ); let uuid = Uuid::new_v4(); let session_security = SessionSecuritySettingsBuilder::default() @@ -243,7 +246,7 @@ mod tests { .unwrap(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -260,7 +263,7 @@ mod tests { }, ); - let client = spawner.spawn(NodeBuilder::default().build(client_kernel).unwrap()); + let client = spawner.spawn(DefaultNodeBuilder::default().build(client_kernel).unwrap()); let server = spawner.spawn(server); let maybe_localset = spawner.local_set(); @@ -293,20 +296,21 @@ mod tests { let spawner = TestSpawner::new(); - let (server, server_addr) = citadel_sdk::test_common::server_info_reactive( - move |conn, remote| async move { - log::trace!(target: "citadel", "*** SERVER RECV CHANNEL ***"); - handle_send_receive_e2e(get_barrier(), conn.channel, message_count).await?; - log::trace!(target: "citadel", "***SERVER TEST SUCCESS***"); - SERVER_SUCCESS.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await - }, - |node| { - if let Some(password) = server_password { - node.with_server_password(password); - } - }, - ); + let (server, server_addr) = + citadel_sdk::test_common::server_info_reactive::<_, _, StackedRatchet>( + move |conn, remote| async move { + log::trace!(target: "citadel", "*** SERVER RECV CHANNEL ***"); + handle_send_receive_e2e(get_barrier(), conn.channel, message_count).await?; + log::trace!(target: "citadel", "***SERVER TEST SUCCESS***"); + SERVER_SUCCESS.store(true, Ordering::Relaxed); + remote.shutdown_kernel().await + }, + |node| { + if let Some(password) = server_password { + node.with_server_password(password); + } + }, + ); let uuid = Uuid::new_v4(); let session_security = SessionSecuritySettingsBuilder::default() @@ -316,7 +320,7 @@ mod tests { .unwrap(); let mut connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security); @@ -337,7 +341,7 @@ mod tests { }, ); - let client = spawner.spawn(NodeBuilder::default().build(client_kernel).unwrap()); + let client = spawner.spawn(DefaultNodeBuilder::default().build(client_kernel).unwrap()); let server = spawner.spawn(server); let maybe_local_set = spawner.local_set(); @@ -365,13 +369,44 @@ mod tests { EncryptionAlgorithm::Ascon80pq )] enx: EncryptionAlgorithm, + ) { + stress_test_p2p_messaging_with_ratchet::( + message_count, + secrecy_mode, + p2p_password, + kem, + enx, + ) + .await; + } + + #[rstest] + #[timeout(std::time::Duration::from_secs(240))] + #[citadel_io::tokio::test(flavor = "multi_thread")] + async fn stress_test_p2p_messaging_thin_ratchet() { + stress_test_p2p_messaging_with_ratchet::( + 500, + SecrecyMode::Perfect, + None, + KemAlgorithm::Kyber, + EncryptionAlgorithm::AES_GCM_256, + ) + .await; + } + + async fn stress_test_p2p_messaging_with_ratchet( + message_count: usize, + secrecy_mode: SecrecyMode, + p2p_password: Option<&'static str>, + kem: KemAlgorithm, + enx: EncryptionAlgorithm, ) { citadel_logging::setup_log(); citadel_sdk::test_common::TestBarrier::setup(2); let client0_success = &AtomicBool::new(false); let client1_success = &AtomicBool::new(false); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let uuid0 = Uuid::new_v4(); let uuid1 = Uuid::new_v4(); @@ -402,7 +437,7 @@ mod tests { let peer1_connection = peer1_agg.add(); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid0) + ServerConnectionSettingsBuilder::::transient_with_id(server_addr, uuid0) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -425,7 +460,7 @@ mod tests { ); let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid1) + ServerConnectionSettingsBuilder::::transient_with_id(server_addr, uuid1) .with_udp_mode(UdpMode::Enabled) .with_session_security_settings(session_security) .build() @@ -476,7 +511,7 @@ mod tests { static CLIENT_SUCCESS: AtomicUsize = AtomicUsize::new(0); CLIENT_SUCCESS.store(0, Ordering::Relaxed); - let (server, server_addr) = server_info(); + let (server, server_addr) = server_info::(); let client_kernels = FuturesUnordered::new(); let total_peers = (0..peer_count) @@ -506,7 +541,7 @@ mod tests { }; let server_connection_settings = - ServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) + DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) .build() .unwrap(); @@ -525,7 +560,7 @@ mod tests { }, ); - let client = NodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); let task = async move { client.await.map(|_| ()) }; client_kernels.push(task); diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs index 04e5b6250..22d19cac3 100644 --- a/citadel_user/src/account_manager.rs +++ b/citadel_user/src/account_manager.rs @@ -102,7 +102,7 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::ConnectionInfo; use crate::server_misc_settings::ServerMiscSettings; use citadel_crypt::argon::argon_container::{ArgonDefaultServerSettings, ArgonSettings}; -use citadel_crypt::fcm::fcm_ratchet::ThinRatchet; +use citadel_crypt::fcm::ratchet::ThinRatchet; use citadel_crypt::stacked_ratchet::Ratchet; use citadel_crypt::stacked_ratchet::StackedRatchet; use citadel_types::prelude::PeerInfo; @@ -206,7 +206,7 @@ impl AccountManager { &self, conn_info: ConnectionInfo, creds: ProposedCredentials, - init_hyper_ratchet: R, + init_stacked_ratchet: R, ) -> Result, AccountError> { let reserved_cid = self .persistence_handler @@ -239,7 +239,7 @@ impl AccountManager { false, conn_info, auth_store, - init_hyper_ratchet, + init_stacked_ratchet, ) .await?; log::trace!(target: "citadel", "Created impersonal CNAC ..."); @@ -252,7 +252,7 @@ impl AccountManager { /// HyperLAN Client (Alice) runs this function below pub async fn register_personal_hyperlan_server( &self, - hyper_ratchet: R, + stacked_ratchet: R, creds: ProposedCredentials, conn_info: ConnectionInfo, ) -> Result, AccountError> { @@ -262,7 +262,7 @@ impl AccountManager { let client_auth_store = creds.into_auth_store(); let cnac = ClientNetworkAccount::::new_from_network_personal( valid_cid, - hyper_ratchet, + stacked_ratchet, client_auth_store, conn_info, ) @@ -357,14 +357,14 @@ impl AccountManager { /// returns true if success, false otherwise pub async fn register_hyperlan_p2p_at_endpoints>( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, adjacent_username: T, ) -> Result<(), AccountError> { let adjacent_username = adjacent_username.into(); - log::trace!(target: "citadel", "Registering {} ({}) to {} (local/endpoints)", &adjacent_username, peer_cid, implicated_cid); + log::trace!(target: "citadel", "Registering {} ({}) to {} (local/endpoints)", &adjacent_username, peer_cid, session_cid); self.persistence_handler - .register_p2p_as_client(implicated_cid, peer_cid, adjacent_username) + .register_p2p_as_client(session_cid, peer_cid, adjacent_username) .await } @@ -388,10 +388,10 @@ impl AccountManager { /// Gets a list of hyperlan peers for the given peer pub async fn get_hyperlan_peer_list( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result>, AccountError> { self.persistence_handler - .get_hyperlan_peer_list(implicated_cid) + .get_hyperlan_peer_list(session_cid) .await } @@ -401,7 +401,7 @@ impl AccountManager { implicated_user: impl Into, target_user: impl Into, ) -> Result, AccountError> { - let implicated_cid = match implicated_user.into() { + let session_cid = match implicated_user.into() { UserIdentifier::ID(id) => id, UserIdentifier::Username(uname) => { @@ -412,15 +412,15 @@ impl AccountManager { match target_user.into() { UserIdentifier::ID(peer_cid) => Ok(self .persistence_handler - .get_hyperlan_peer_by_cid(implicated_cid, peer_cid) + .get_hyperlan_peer_by_cid(session_cid, peer_cid) .await? - .map(|r| (implicated_cid, r))), + .map(|r| (session_cid, r))), UserIdentifier::Username(uname) => Ok(self .persistence_handler - .get_hyperlan_peer_by_username(implicated_cid, &uname) + .get_hyperlan_peer_by_username(session_cid, &uname) .await? - .map(|r| (implicated_cid, r))), + .map(|r| (session_cid, r))), } } diff --git a/citadel_user/src/backend/filesystem_backend.rs b/citadel_user/src/backend/filesystem_backend.rs index 9a7b06d77..73fb8e317 100644 --- a/citadel_user/src/backend/filesystem_backend.rs +++ b/citadel_user/src/backend/filesystem_backend.rs @@ -219,14 +219,14 @@ impl BackendConnection for FilesystemBackend Result<(), AccountError> { self.memory_backend - .register_p2p_as_client(implicated_cid, peer_cid, peer_username) + .register_p2p_as_client(session_cid, peer_cid, peer_username) .await?; - self.save_cnac_by_cid(implicated_cid).await + self.save_cnac_by_cid(session_cid).await } /// Deregisters a peer-to-peer relationship between two clients. @@ -258,14 +258,14 @@ impl BackendConnection for FilesystemBackend Result, AccountError> { let res = self .memory_backend - .deregister_p2p_as_client(implicated_cid, peer_cid) + .deregister_p2p_as_client(session_cid, peer_cid) .await?; - self.save_cnac_by_cid(implicated_cid).await.map(|_| res) + self.save_cnac_by_cid(session_cid).await.map(|_| res) } /// Retrieves a list of hyperlan peers for a client. @@ -273,10 +273,10 @@ impl BackendConnection for FilesystemBackend Result>, AccountError> { self.memory_backend - .get_hyperlan_peer_list(implicated_cid) + .get_hyperlan_peer_list(session_cid) .await } @@ -285,11 +285,9 @@ impl BackendConnection for FilesystemBackend Result, AccountError> { - self.memory_backend - .get_client_metadata(implicated_cid) - .await + self.memory_backend.get_client_metadata(session_cid).await } /// Retrieves a list of metadata for all clients. @@ -307,11 +305,11 @@ impl BackendConnection for FilesystemBackend Result, AccountError> { self.memory_backend - .get_hyperlan_peer_by_cid(implicated_cid, peer_cid) + .get_hyperlan_peer_by_cid(session_cid, peer_cid) .await } @@ -320,11 +318,11 @@ impl BackendConnection for FilesystemBackend Result { self.memory_backend - .hyperlan_peer_exists(implicated_cid, peer_cid) + .hyperlan_peer_exists(session_cid, peer_cid) .await } @@ -333,11 +331,11 @@ impl BackendConnection for FilesystemBackend Result, AccountError> { self.memory_backend - .hyperlan_peers_are_mutuals(implicated_cid, peers) + .hyperlan_peers_are_mutuals(session_cid, peers) .await } @@ -346,11 +344,11 @@ impl BackendConnection for FilesystemBackend Result, AccountError> { self.memory_backend - .get_hyperlan_peers(implicated_cid, peers) + .get_hyperlan_peers(session_cid, peers) .await } @@ -359,10 +357,10 @@ impl BackendConnection for FilesystemBackend Result>, AccountError> { self.memory_backend - .get_hyperlan_peer_list_as_server(implicated_cid) + .get_hyperlan_peer_list_as_server(session_cid) .await } @@ -385,13 +383,13 @@ impl BackendConnection for FilesystemBackend Result>, AccountError> { self.memory_backend - .get_byte_map_value(implicated_cid, peer_cid, key, sub_key) + .get_byte_map_value(session_cid, peer_cid, key, sub_key) .await } @@ -400,16 +398,16 @@ impl BackendConnection for FilesystemBackend Result>, AccountError> { let res = self .memory_backend - .remove_byte_map_value(implicated_cid, peer_cid, key, sub_key) + .remove_byte_map_value(session_cid, peer_cid, key, sub_key) .await?; - self.save_cnac_by_cid(implicated_cid).await.map(|_| res) + self.save_cnac_by_cid(session_cid).await.map(|_| res) } /// Stores a byte map value for a client. @@ -417,7 +415,7 @@ impl BackendConnection for FilesystemBackend BackendConnection for FilesystemBackend Result>, AccountError> { let res = self .memory_backend - .store_byte_map_value(implicated_cid, peer_cid, key, sub_key, value) + .store_byte_map_value(session_cid, peer_cid, key, sub_key, value) .await?; - self.save_cnac_by_cid(implicated_cid).await.map(|_| res) + self.save_cnac_by_cid(session_cid).await.map(|_| res) } /// Retrieves a list of byte map values for a client by key. @@ -435,15 +433,15 @@ impl BackendConnection for FilesystemBackend Result>, AccountError> { let res = self .memory_backend - .get_byte_map_values_by_key(implicated_cid, peer_cid, key) + .get_byte_map_values_by_key(session_cid, peer_cid, key) .await?; - self.save_cnac_by_cid(implicated_cid).await.map(|_| res) + self.save_cnac_by_cid(session_cid).await.map(|_| res) } /// Removes a list of byte map values for a client by key. @@ -451,15 +449,15 @@ impl BackendConnection for FilesystemBackend Result>, AccountError> { let res = self .memory_backend - .remove_byte_map_values_by_key(implicated_cid, peer_cid, key) + .remove_byte_map_values_by_key(session_cid, peer_cid, key) .await?; - self.save_cnac_by_cid(implicated_cid).await.map(|_| res) + self.save_cnac_by_cid(session_cid).await.map(|_| res) } /// Streams an object to the backend. diff --git a/citadel_user/src/backend/memory.rs b/citadel_user/src/backend/memory.rs index ef6e749a3..a50d26ba6 100644 --- a/citadel_user/src/backend/memory.rs +++ b/citadel_user/src/backend/memory.rs @@ -157,14 +157,14 @@ impl BackendConnection for MemoryBackend Result<(), AccountError> { self.clients .read() - .get(&implicated_cid) - .ok_or(AccountError::ClientNonExists(implicated_cid))? + .get(&session_cid) + .ok_or(AccountError::ClientNonExists(session_cid))? .insert_hyperlan_peer(peer_cid, peer_username); Ok(()) @@ -180,23 +180,23 @@ impl BackendConnection for MemoryBackend Result, AccountError> { Ok(self .clients .read() - .get(&implicated_cid) - .ok_or(AccountError::ClientNonExists(implicated_cid))? + .get(&session_cid) + .ok_or(AccountError::ClientNonExists(session_cid))? .remove_hyperlan_peer(peer_cid)) } async fn get_hyperlan_peer_list( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result>, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { Ok(cnac.get_hyperlan_peer_list()) } else { Ok(None) @@ -205,10 +205,10 @@ impl BackendConnection for MemoryBackend Result, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { Ok(Some(cnac.get_metadata())) } else { Ok(None) @@ -233,11 +233,11 @@ impl BackendConnection for MemoryBackend Result, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { Ok(cnac.get_hyperlan_peer(peer_cid)) } else { Ok(None) @@ -246,17 +246,17 @@ impl BackendConnection for MemoryBackend Result { - self.get_hyperlan_peer_by_cid(implicated_cid, peer_cid) + self.get_hyperlan_peer_by_cid(session_cid, peer_cid) .await .map(|r| r.is_some()) } async fn hyperlan_peers_are_mutuals( &self, - implicated_cid: u64, + session_cid: u64, peers: &[u64], ) -> Result, AccountError> { if peers.is_empty() { @@ -264,7 +264,7 @@ impl BackendConnection for MemoryBackend BackendConnection for MemoryBackend Result, AccountError> { if peers.is_empty() { @@ -281,7 +281,7 @@ impl BackendConnection for MemoryBackend BackendConnection for MemoryBackend Result>, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { Ok(cnac.get_hyperlan_peer_mutuals()) } else { Ok(Default::default()) @@ -311,13 +311,13 @@ impl BackendConnection for MemoryBackend Result>, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { let mut lock = cnac.write(); Ok(lock .byte_map @@ -334,13 +334,13 @@ impl BackendConnection for MemoryBackend Result>, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { let mut lock = cnac.write(); Ok(lock .byte_map @@ -356,14 +356,14 @@ impl BackendConnection for MemoryBackend, ) -> Result>, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { let mut lock = cnac.write(); Ok(lock .byte_map @@ -379,12 +379,12 @@ impl BackendConnection for MemoryBackend Result>, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { let mut lock = cnac.write(); let map = lock .byte_map @@ -401,12 +401,12 @@ impl BackendConnection for MemoryBackend Result>, AccountError> { let read = self.clients.read(); - if let Some(cnac) = read.get(&implicated_cid) { + if let Some(cnac) = read.get(&session_cid) { let mut lock = cnac.write(); let submap = lock .byte_map diff --git a/citadel_user/src/backend/mod.rs b/citadel_user/src/backend/mod.rs index 8cda6f69b..4c518a94c 100644 --- a/citadel_user/src/backend/mod.rs +++ b/citadel_user/src/backend/mod.rs @@ -44,7 +44,7 @@ use std::sync::Arc; use async_trait::async_trait; -use citadel_crypt::fcm::fcm_ratchet::ThinRatchet; +use citadel_crypt::fcm::ratchet::ThinRatchet; use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; #[cfg(all(feature = "redis", not(coverage)))] @@ -216,7 +216,7 @@ pub trait BackendConnection: Send + Sync { /// registers p2p as client async fn register_p2p_as_client( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, peer_username: String, ) -> Result<(), AccountError>; @@ -225,18 +225,18 @@ pub trait BackendConnection: Send + Sync { /// Deregisters two peers from each other async fn deregister_p2p_as_client( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result, AccountError>; /// Returns a list of hyperlan peers for the client async fn get_hyperlan_peer_list( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result>, AccountError>; /// Returns the metadata for a client async fn get_client_metadata( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result, AccountError>; /// Gets all the metadata for many clients async fn get_clients_metadata( @@ -246,40 +246,40 @@ pub trait BackendConnection: Send + Sync { /// Gets hyperlan peer async fn get_hyperlan_peer_by_cid( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result, AccountError>; /// Determines if the peer exists or not async fn hyperlan_peer_exists( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result; /// Determines if the input cids are mutual to the implicated cid in order async fn hyperlan_peers_are_mutuals( &self, - implicated_cid: u64, + session_cid: u64, peers: &[u64], ) -> Result, AccountError>; /// Returns a set of PeerMutual containers async fn get_hyperlan_peers( &self, - implicated_cid: u64, + session_cid: u64, peers: &[u64], ) -> Result, AccountError>; /// Gets hyperland peer by username async fn get_hyperlan_peer_by_username( &self, - implicated_cid: u64, + session_cid: u64, username: &str, ) -> Result, AccountError> { - self.get_hyperlan_peer_by_cid(implicated_cid, user::username_to_cid(username)) + self.get_hyperlan_peer_by_cid(session_cid, user::username_to_cid(username)) .await } /// Gets all peers for client async fn get_hyperlan_peer_list_as_server( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result>, AccountError>; /// Synchronizes the list locally. Returns true if needs to be saved async fn synchronize_hyperlan_peer_list_as_client( @@ -290,7 +290,7 @@ pub trait BackendConnection: Send + Sync { /// Returns a vector of bytes from the byte map async fn get_byte_map_value( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, sub_key: &str, @@ -298,7 +298,7 @@ pub trait BackendConnection: Send + Sync { /// Removes a value from the byte map, returning the previous value async fn remove_byte_map_value( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, sub_key: &str, @@ -306,7 +306,7 @@ pub trait BackendConnection: Send + Sync { /// Stores a value in the byte map, either creating or overwriting any pre-existing value async fn store_byte_map_value( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, sub_key: &str, @@ -315,14 +315,14 @@ pub trait BackendConnection: Send + Sync { /// Obtains a list of K,V pairs such that they reside inside `key` async fn get_byte_map_values_by_key( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, ) -> Result>, AccountError>; /// Obtains a list of K,V pairs such that `needle` is a subset of the K value async fn remove_byte_map_values_by_key( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, ) -> Result>, AccountError>; diff --git a/citadel_user/src/backend/redis_backend.rs b/citadel_user/src/backend/redis_backend.rs index 36a08e9cb..e5f70b7f1 100644 --- a/citadel_user/src/backend/redis_backend.rs +++ b/citadel_user/src/backend/redis_backend.rs @@ -324,20 +324,16 @@ impl BackendConnection for RedisBackend Result<(), AccountError> { let mut conn = self.get_conn().await?; redis_base::pipe() .atomic() - .hset( - get_peer_username_key(implicated_cid), - peer_cid, - &peer_username, - ) + .hset(get_peer_username_key(session_cid), peer_cid, &peer_username) .ignore() - .hset(get_peer_cid_key(implicated_cid), &peer_username, peer_cid) + .hset(get_peer_cid_key(session_cid), &peer_username, peer_cid) .ignore() .query_async(&mut conn) .await @@ -372,7 +368,7 @@ impl BackendConnection for RedisBackend Result, AccountError> { let mut conn = self.get_conn().await?; @@ -384,10 +380,10 @@ impl BackendConnection for RedisBackend BackendConnection for RedisBackend Result>, AccountError> { self.get_conn() .await? - .hkeys(get_peer_username_key(implicated_cid)) + .hkeys(get_peer_username_key(session_cid)) .await .map(Some) .map_err(|err| AccountError::msg(err.to_string())) @@ -414,10 +410,10 @@ impl BackendConnection for RedisBackend Result, AccountError> { Ok(self - .fetch_cnac(implicated_cid) + .fetch_cnac(session_cid) .await? .map(|r| r.get_metadata())) } @@ -455,12 +451,12 @@ impl BackendConnection for RedisBackend Result, AccountError> { self.get_conn() .await? - .hget(get_peer_username_key(implicated_cid), peer_cid) + .hget(get_peer_username_key(session_cid), peer_cid) .await .map_err(|err| AccountError::msg(err.to_string())) .map(|peer_username: Option| { @@ -474,12 +470,12 @@ impl BackendConnection for RedisBackend Result { self.get_conn() .await? - .hexists(get_peer_username_key(implicated_cid), peer_cid) + .hexists(get_peer_username_key(session_cid), peer_cid) .await .map_err(|err| AccountError::msg(err.to_string())) } @@ -487,7 +483,7 @@ impl BackendConnection for RedisBackend Result, AccountError> { let mut conn = self.get_conn().await?; @@ -505,7 +501,7 @@ impl BackendConnection for RedisBackend BackendConnection for RedisBackend Result, AccountError> { let mut conn = self.get_conn().await?; @@ -538,7 +534,7 @@ impl BackendConnection for RedisBackend BackendConnection for RedisBackend Result>, AccountError> { let usernames_map: HashMap = self .get_conn() .await? - .hgetall(get_peer_username_key(implicated_cid)) // get all (peer_cid, username) + .hgetall(get_peer_username_key(session_cid)) // get all (peer_cid, username) .await .map_err(|err| AccountError::msg(err.to_string()))?; @@ -591,9 +587,9 @@ impl BackendConnection for RedisBackend Result<(), AccountError> { let mut conn = self.get_conn().await?; let mut pipe = redis_base::pipe(); - let implicated_cid = cnac.get_cid(); - let peer_cid_key = get_peer_cid_key(implicated_cid); - let peer_username_key = get_peer_username_key(implicated_cid); + let session_cid = cnac.get_cid(); + let peer_cid_key = get_peer_cid_key(session_cid); + let peer_username_key = get_peer_username_key(session_cid); pipe.atomic(); @@ -621,27 +617,27 @@ impl BackendConnection for RedisBackend Result>, AccountError> { self.get_conn() .await? - .hget(get_byte_map_key(implicated_cid, peer_cid, key), sub_key) + .hget(get_byte_map_key(session_cid, peer_cid, key), sub_key) .await .map_err(|err| AccountError::msg(err.to_string())) } async fn remove_byte_map_value( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, sub_key: &str, ) -> Result>, AccountError> { let mut conn = self.get_conn().await?; - let key = get_byte_map_key(implicated_cid, peer_cid, key); + let key = get_byte_map_key(session_cid, peer_cid, key); redis_base::Script::new( r" local ret = redis.call('hget', KEYS[1], KEYS[2]) @@ -658,14 +654,14 @@ impl BackendConnection for RedisBackend, ) -> Result>, AccountError> { let mut conn = self.get_conn().await?; - let key = get_byte_map_key(implicated_cid, peer_cid, key); + let key = get_byte_map_key(session_cid, peer_cid, key); redis_base::Script::new( r" local ret = redis.call('hget', KEYS[1], KEYS[2]) @@ -683,25 +679,25 @@ impl BackendConnection for RedisBackend Result>, AccountError> { self.get_conn() .await? - .hgetall(get_byte_map_key(implicated_cid, peer_cid, key)) + .hgetall(get_byte_map_key(session_cid, peer_cid, key)) .await .map_err(|err| AccountError::msg(err.to_string())) } async fn remove_byte_map_values_by_key( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, ) -> Result>, AccountError> { let mut conn = self.get_conn().await?; - let key = get_byte_map_key(implicated_cid, peer_cid, key); + let key = get_byte_map_key(session_cid, peer_cid, key); redis_base::Script::new( r" local ret = redis.call('hgetall', KEYS[1]) @@ -811,16 +807,16 @@ fn get_cid_to_username_key(cid: u64) -> String { format!("{LOCAL_CID_TO_USERNAME}.{cid}") } -fn get_peer_cid_key(implicated_cid: u64) -> String { - format!("{PEER_CID_PREFIX}.{implicated_cid}") +fn get_peer_cid_key(session_cid: u64) -> String { + format!("{PEER_CID_PREFIX}.{session_cid}") } -fn get_peer_username_key(implicated_cid: u64) -> String { - format!("{PEER_USERNAME_PREFIX}.{implicated_cid}") +fn get_peer_username_key(session_cid: u64) -> String { + format!("{PEER_USERNAME_PREFIX}.{session_cid}") } -fn get_byte_map_key(implicated_cid: u64, peer_cid: u64, key: &str) -> String { - format!("{BYTE_MAP_PREFIX}.{implicated_cid}.{peer_cid}.{key}",) +fn get_byte_map_key(session_cid: u64, peer_cid: u64, key: &str) -> String { + format!("{BYTE_MAP_PREFIX}.{session_cid}.{peer_cid}.{key}",) } fn get_impersonal_status_key() -> &'static str { diff --git a/citadel_user/src/backend/sql_backend.rs b/citadel_user/src/backend/sql_backend.rs index 6b3025b89..d23c5e3d7 100644 --- a/citadel_user/src/backend/sql_backend.rs +++ b/citadel_user/src/backend/sql_backend.rs @@ -37,7 +37,7 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX}; use crate::serialization::SyncIO; use async_trait::async_trait; -use citadel_crypt::fcm::fcm_ratchet::ThinRatchet; +use citadel_crypt::fcm::ratchet::ThinRatchet; use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; @@ -397,14 +397,14 @@ impl BackendConnection for SqlBackend // We must update the CNAC && the sql database async fn register_p2p_as_client( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, peer_username: String, ) -> Result<(), AccountError> { let conn = &(self.get_conn().await?); - log::trace!(target: "citadel", "Registering p2p ({} <-> {}) as client", implicated_cid, peer_cid); + log::trace!(target: "citadel", "Registering p2p ({} <-> {}) as client", session_cid, peer_cid); let query = self.format("INSERT INTO peers (peer_cid, cid, username) VALUES (?, ?, ?)"); - let _query = gen_query!(sqlx::query(&query), self, peer_cid, implicated_cid) + let _query = gen_query!(sqlx::query(&query), self, peer_cid, session_cid) .bind(peer_username) .execute(conn) .await?; @@ -426,20 +426,20 @@ impl BackendConnection for SqlBackend async fn deregister_p2p_as_client( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result, AccountError> { let conn = &(self.get_conn().await?); let mut tx = conn.begin().await?; let query = self.format("SELECT username FROM peers WHERE peer_cid = ? AND cid = ?"); - let row: Option = gen_query!(sqlx::query(&query), self, peer_cid, implicated_cid) + let row: Option = gen_query!(sqlx::query(&query), self, peer_cid, session_cid) .fetch_optional(tx.deref_mut()) .await?; if let Some(row) = row { let peer_username = try_get_blob_as_utf8("username", &row)?; let query = self.format("DELETE FROM peers WHERE peer_cid = ? AND cid = ?"); - let _query = gen_query!(sqlx::query(&query), self, peer_cid, implicated_cid) + let _query = gen_query!(sqlx::query(&query), self, peer_cid, session_cid) .execute(tx.deref_mut()) .await?; tx.commit().await?; @@ -456,11 +456,11 @@ impl BackendConnection for SqlBackend async fn get_hyperlan_peer_list( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result>, AccountError> { let conn = &(self.get_conn().await?); let query = self.format("SELECT peer_cid FROM peers WHERE cid = ?"); - let query: Vec = gen_query!(sqlx::query(&query), self, implicated_cid) + let query: Vec = gen_query!(sqlx::query(&query), self, session_cid) .fetch_all(conn) .await?; @@ -477,12 +477,12 @@ impl BackendConnection for SqlBackend async fn get_client_metadata( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result, AccountError> { let conn = &(self.get_conn().await?); // cnacs(cid VARCHAR(20) NOT NULL, is_connected BOOL, is_personal BOOL, username VARCHAR({}) UNIQUE, full_name TEXT, creation_date TEXT, bin LONGTEXT, PRIMARY KEY (cid)) let query = self.format("SELECT is_personal, username, full_name, creation_date FROM cnacs WHERE cid = ? LIMIT 1"); - let query: Option = gen_query!(sqlx::query(&query), self, implicated_cid) + let query: Option = gen_query!(sqlx::query(&query), self, session_cid) .fetch_optional(conn) .await?; @@ -492,7 +492,7 @@ impl BackendConnection for SqlBackend let full_name = try_get_blob_as_utf8("full_name", &query)?; let creation_date = try_get_blob_as_utf8("creation_date", &query)?; Ok(Some(CNACMetadata { - cid: implicated_cid, + cid: session_cid, is_personal, username, full_name, @@ -540,13 +540,13 @@ impl BackendConnection for SqlBackend async fn get_hyperlan_peer_by_cid( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result, AccountError> { let conn = &(self.get_conn().await?); let query = self.format("SELECT username FROM peers WHERE cid = ? AND peer_cid = ? LIMIT 1"); - let query: Option = gen_query!(sqlx::query(&query), self, implicated_cid, peer_cid) + let query: Option = gen_query!(sqlx::query(&query), self, session_cid, peer_cid) .fetch_optional(conn) .await?; @@ -567,13 +567,13 @@ impl BackendConnection for SqlBackend async fn hyperlan_peer_exists( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result { let conn = &(self.get_conn().await?); let query = self .format("SELECT COUNT(*) as count FROM peers WHERE peer_cid = ? AND cid = ? LIMIT 1"); - let query: AnyRow = gen_query!(sqlx::query(&query), self, peer_cid, implicated_cid) + let query: AnyRow = gen_query!(sqlx::query(&query), self, peer_cid, session_cid) .fetch_one(conn) .await?; @@ -582,7 +582,7 @@ impl BackendConnection for SqlBackend async fn hyperlan_peers_are_mutuals( &self, - implicated_cid: u64, + session_cid: u64, peers: &[u64], ) -> Result, AccountError> { if peers.is_empty() { @@ -595,7 +595,7 @@ impl BackendConnection for SqlBackend let insert = self.construct_arg_insert_any(peers); let query = self.format(format!("WITH input(peer_cid) AS (VALUES {insert}) SELECT peers.peer_cid FROM input INNER JOIN peers ON input.peer_cid = peers.peer_cid WHERE peers.cid = ? LIMIT {limit}")); - let query: Vec = gen_query!(sqlx::query(&query), self, implicated_cid) + let query: Vec = gen_query!(sqlx::query(&query), self, session_cid) .fetch_all(conn) .await?; @@ -612,7 +612,7 @@ impl BackendConnection for SqlBackend async fn get_hyperlan_peers( &self, - implicated_cid: u64, + session_cid: u64, peers: &[u64], ) -> Result, AccountError> { if peers.is_empty() { @@ -625,7 +625,7 @@ impl BackendConnection for SqlBackend let insert = self.construct_arg_insert_any(peers); let query = self.format(format!("WITH input(peer_cid) AS (VALUES {insert}) SELECT peers.peer_cid, peers.username FROM input INNER JOIN peers ON input.peer_cid = peers.peer_cid WHERE peers.cid = ? LIMIT {limit}")); - let query: Vec = gen_query!(sqlx::query(&query), self, implicated_cid) + let query: Vec = gen_query!(sqlx::query(&query), self, session_cid) .fetch_all(conn) .await?; @@ -645,11 +645,11 @@ impl BackendConnection for SqlBackend async fn get_hyperlan_peer_list_as_server( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result>, AccountError> { let conn = &(self.get_conn().await?); let query = self.format("SELECT peers.peer_cid, peers.username FROM cnacs INNER JOIN peers ON cnacs.cid = peers.cid WHERE peers.cid = ?"); - let query: Vec = gen_query!(sqlx::query(&query), self, implicated_cid) + let query: Vec = gen_query!(sqlx::query(&query), self, session_cid) .fetch_all(conn) .await?; let mut ret = Vec::with_capacity(query.len()); @@ -683,16 +683,16 @@ impl BackendConnection for SqlBackend let conn = &(self.get_conn().await?); let mut tx = conn.begin().await?; - let implicated_cid = cnac.get_cid(); + let session_cid = cnac.get_cid(); let query = self.format("DELETE FROM peers WHERE cid = ?"); - let _query = gen_query!(sqlx::query(&query), self, implicated_cid) + let _query = gen_query!(sqlx::query(&query), self, session_cid) .execute(tx.deref_mut()) .await?; for MutualPeer { cid, username, .. } in peers { let query = self.format("INSERT INTO peers (peer_cid, cid, username) VALUES(?, ?, ?)"); - let _ = gen_query!(sqlx::query(&query), self, cid, implicated_cid) + let _ = gen_query!(sqlx::query(&query), self, cid, session_cid) .bind(username.unwrap_or_else(|| "NULL".into())) .execute(tx.deref_mut()) .await?; @@ -706,14 +706,14 @@ impl BackendConnection for SqlBackend async fn get_byte_map_value( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, sub_key: &str, ) -> Result>, AccountError> { let conn = &(self.get_conn().await?); let query = self.format("SELECT bin FROM bytemap WHERE cid = ? AND peer_cid = ? AND id = ? AND sub_id = ? LIMIT 1"); - let row: Option = gen_query!(sqlx::query(&query), self, implicated_cid, peer_cid) + let row: Option = gen_query!(sqlx::query(&query), self, session_cid, peer_cid) .bind(key) .bind(sub_key) .fetch_optional(conn) @@ -732,21 +732,21 @@ impl BackendConnection for SqlBackend async fn remove_byte_map_value( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, sub_key: &str, ) -> Result>, AccountError> { // TODO: Optimize this into a single step if let Some(value) = self - .get_byte_map_value(implicated_cid, peer_cid, key, sub_key) + .get_byte_map_value(session_cid, peer_cid, key, sub_key) .await? { let conn = &(self.get_conn().await?); let query = self.format( "DELETE FROM bytemap WHERE cid = ? AND peer_cid = ? AND id = ? AND sub_id = ?", ); - let _ = gen_query!(sqlx::query(&query), self, implicated_cid, peer_cid) + let _ = gen_query!(sqlx::query(&query), self, session_cid, peer_cid) .bind(key) .bind(sub_key) .execute(conn) @@ -760,7 +760,7 @@ impl BackendConnection for SqlBackend async fn store_byte_map_value( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, sub_key: &str, @@ -771,14 +771,13 @@ impl BackendConnection for SqlBackend let set_query = self .format("INSERT INTO bytemap (cid, peer_cid, id, sub_id, bin) VALUES (?, ?, ?, ?, ?)"); - let row: Option = - gen_query!(sqlx::query(&get_query), self, implicated_cid, peer_cid) - .bind(key) - .bind(sub_key) - .fetch_optional(&conn) - .await?; + let row: Option = gen_query!(sqlx::query(&get_query), self, session_cid, peer_cid) + .bind(key) + .bind(sub_key) + .fetch_optional(&conn) + .await?; - let _query = gen_query!(sqlx::query(&set_query), self, implicated_cid, peer_cid) + let _query = gen_query!(sqlx::query(&set_query), self, session_cid, peer_cid) .bind(key) .bind(sub_key) .bind(value) @@ -798,14 +797,14 @@ impl BackendConnection for SqlBackend async fn get_byte_map_values_by_key( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, ) -> Result>, AccountError> { let conn = &(self.get_conn().await?); let query = self .format("SELECT sub_id, bin FROM bytemap WHERE cid = ? AND peer_cid = ? AND id = ?"); - let rows: Vec = gen_query!(sqlx::query(&query), self, implicated_cid, peer_cid) + let rows: Vec = gen_query!(sqlx::query(&query), self, session_cid, peer_cid) .bind(key) .fetch_all(conn) .await?; @@ -823,17 +822,17 @@ impl BackendConnection for SqlBackend async fn remove_byte_map_values_by_key( &self, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, key: &str, ) -> Result>, AccountError> { let values = self - .get_byte_map_values_by_key(implicated_cid, peer_cid, key) + .get_byte_map_values_by_key(session_cid, peer_cid, key) .await?; let conn = &(self.get_conn().await?); let query = self.format("DELETE FROM bytemap WHERE cid = ? AND peer_cid = ? AND id = ?"); - let _ = gen_query!(sqlx::query(&query), self, implicated_cid, peer_cid) + let _ = gen_query!(sqlx::query(&query), self, session_cid, peer_cid) .bind(key) .execute(conn) .await?; diff --git a/citadel_user/src/client_account.rs b/citadel_user/src/client_account.rs index c766fc95e..5a7352ced 100644 --- a/citadel_user/src/client_account.rs +++ b/citadel_user/src/client_account.rs @@ -102,7 +102,7 @@ use std::fmt::Formatter; use crate::auth::proposed_credentials::ProposedCredentials; use crate::auth::DeclaredAuthenticationMode; use crate::serialization::SyncIO; -use citadel_crypt::fcm::fcm_ratchet::ThinRatchet; +use citadel_crypt::fcm::ratchet::ThinRatchet; use citadel_crypt::prelude::Toolset; use citadel_crypt::stacked_ratchet::StackedRatchet; use citadel_types::crypto::SecBuffer; @@ -144,7 +144,7 @@ pub struct ClientNetworkAccountInner, - /// Toolset which contains all the drills + /// Toolset which contains all the entropy_banks #[serde(bound = "")] pub crypt_container: PeerSessionCrypto, /// RTDB config for client-side communications @@ -183,12 +183,12 @@ impl ClientNetworkAccount { is_personal: bool, adjacent_nac: ConnectionInfo, auth_store: DeclaredAuthenticationMode, - base_hyper_ratchet: R, + base_stacked_ratchet: R, ) -> Result { log::trace!(target: "citadel", "Creating CNAC w/valid cid: {:?}", valid_cid); let creation_date = get_present_formatted_timestamp(); let crypt_container = PeerSessionCrypto::::new( - Toolset::::new(valid_cid, base_hyper_ratchet), + Toolset::::new(valid_cid, base_stacked_ratchet), is_personal, ); let mutuals = MultiMap::new(); @@ -213,7 +213,7 @@ impl ClientNetworkAccount { /// Resets the toolset, if necessary. If the CNAC was freshly serialized, the hyper ratchet /// is not updated. In either case, returns the static aux hyper ratchet #[allow(unused_results)] - pub fn refresh_static_hyper_ratchet(&self) -> R { + pub fn refresh_static_ratchet(&self) -> R { let mut write = self.write(); write.crypt_container.toolset.verify_init_state(); write.crypt_container.refresh_state(); @@ -238,13 +238,20 @@ impl ClientNetworkAccount { /// Towards the end of the registration phase, the [`ClientNetworkAccountInner`] gets transmitted to Alice. pub async fn new_from_network_personal( valid_cid: u64, - hyper_ratchet: R, + stacked_ratchet: R, auth_store: DeclaredAuthenticationMode, conn_info: ConnectionInfo, ) -> Result { const IS_PERSONAL: bool = true; // We supply none to the valid cid - Self::new(valid_cid, IS_PERSONAL, conn_info, auth_store, hyper_ratchet).await + Self::new( + valid_cid, + IS_PERSONAL, + conn_info, + auth_store, + stacked_ratchet, + ) + .await } /// Returns the username of this client @@ -307,7 +314,7 @@ impl ClientNetworkAccount { } /// This should ONLY be used for recovery mode - pub fn get_static_auxiliary_hyper_ratchet(&self) -> R { + pub fn get_static_auxiliary_stacked_ratchet(&self) -> R { let this = self.read(); this.crypt_container .toolset @@ -485,7 +492,7 @@ impl ClientNetworkAccount { /// Returns Some if success, None otherwise #[allow(unused_results)] pub(crate) fn remove_hyperlan_peer(&self, cid: u64) -> Option { - log::trace!(target: "citadel", "[remove peer] implicated_cid: {} | peer_cid: {}", self.get_cid(), cid); + log::trace!(target: "citadel", "[remove peer] session_cid: {} | peer_cid: {}", self.get_cid(), cid); let mut write = self.write(); if let Some(hyperlan_peers) = write.mutuals.get_vec_mut(&HYPERLAN_IDX) { if let Some(idx) = hyperlan_peers.iter().position(|peer| peer.cid == cid) { diff --git a/citadel_user/src/external_services/google_auth.rs b/citadel_user/src/external_services/google_auth.rs index 27a51d91b..6b1210b16 100644 --- a/citadel_user/src/external_services/google_auth.rs +++ b/citadel_user/src/external_services/google_auth.rs @@ -110,9 +110,9 @@ impl GoogleAuth { let iat = iat.to_string(); let exp = exp.to_string(); - let implicated_cid = uid.to_string(); + let session_cid = uid.to_string(); - //let final_claim = format!("array(\"cid\" => ${})", &implicated_cid); + //let final_claim = format!("array(\"cid\" => ${})", &session_cid); let mut claims = HashMap::new(); claims.insert("alg", "RS256"); @@ -121,7 +121,7 @@ impl GoogleAuth { claims.insert("aud", "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"); claims.insert("iat", iat.as_str()); claims.insert("exp", exp.as_str()); - claims.insert("uid", &implicated_cid); + claims.insert("uid", &session_cid); //claims.insert("claims", final_claim.as_str()); log::trace!(target: "citadel", "{:?}", &claims); diff --git a/citadel_user/src/external_services/mod.rs b/citadel_user/src/external_services/mod.rs index b712d6de7..f145f5374 100644 --- a/citadel_user/src/external_services/mod.rs +++ b/citadel_user/src/external_services/mod.rs @@ -118,12 +118,12 @@ pub mod service { /// This should be called after the server validates a login [marked async for now to allow room for future async processes) pub async fn on_post_login_serverside( &self, - implicated_cid: u64, + session_cid: u64, ) -> Result { let mut ret: ServicesObject = Default::default(); if let Some(auth) = self.google_auth.as_ref() { - ret.google_auth_jwt = Some(auth.sign_new_custom_jwt_auth(implicated_cid)?) + ret.google_auth_jwt = Some(auth.sign_new_custom_jwt_auth(session_cid)?) } ret.rtdb = self.rtdb_config.clone(); diff --git a/citadel_user/src/external_services/rtdb.rs b/citadel_user/src/external_services/rtdb.rs index 4b3c81ea5..4419c1705 100644 --- a/citadel_user/src/external_services/rtdb.rs +++ b/citadel_user/src/external_services/rtdb.rs @@ -142,7 +142,7 @@ impl ExternalServiceChannel for RtdbInstance { async fn send( &mut self, data: RawExternalPacket, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result<(), AccountError> { // implicated CID is sending to peer CID. Thus, access the peer CID's node, and push/post/append message under implicated CID's peer node @@ -153,7 +153,7 @@ impl ExternalServiceChannel for RtdbInstance { .child("users") .child(peer_cid.to_string()) .child("peers") - .child(implicated_cid.to_string()) + .child(session_cid.to_string()) .final_node("packets") .post(data) .await diff --git a/citadel_user/src/external_services/service_interface.rs b/citadel_user/src/external_services/service_interface.rs index 3cb40d606..6ccbc3693 100644 --- a/citadel_user/src/external_services/service_interface.rs +++ b/citadel_user/src/external_services/service_interface.rs @@ -24,7 +24,7 @@ //! async fn send( //! &mut self, //! data: RawExternalPacket, -//! implicated_cid: u64, +//! session_cid: u64, //! peer_cid: u64, //! ) -> Result<(), AccountError> { //! // Implement service-specific send logic @@ -56,11 +56,11 @@ pub type RawExternalPacket = Vec; #[async_trait] /// An interface for unifying interaction with underlying services pub trait ExternalServiceChannel { - /// Sends a payload from `implicated_cid` to `peer_cid` + /// Sends a payload from `session_cid` to `peer_cid` async fn send( &mut self, data: RawExternalPacket, - implicated_cid: u64, + session_cid: u64, peer_cid: u64, ) -> Result<(), AccountError>; } diff --git a/citadel_user/src/hypernode_account.rs b/citadel_user/src/hypernode_account.rs index 5012e8204..078b6ce79 100644 --- a/citadel_user/src/hypernode_account.rs +++ b/citadel_user/src/hypernode_account.rs @@ -76,6 +76,7 @@ use crate::account_manager::AccountManager; use crate::misc::AccountError; use crate::prelude::ClientNetworkAccount; use async_trait::async_trait; +use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::user::MutualPeer; use citadel_types::user::UserIdentifier; @@ -84,19 +85,17 @@ pub const CNAC_SERIALIZED_EXTENSION: &str = "hca"; #[async_trait] pub trait UserIdentifierExt { - type AccountManager; - type SearchOutput; // Usually the clientnetworkaccount type Error; - async fn search( + async fn search( &self, - account_manager: &Self::AccountManager, - ) -> Result, Self::Error>; + account_manager: &AccountManager, + ) -> Result>, Self::Error>; - /// Performs a search for the current peer given the `implicated_cid` - async fn search_peer( + /// Performs a search for the current peer given the `session_cid` + async fn search_peer( &self, - implicated_cid: u64, - account_manager: &Self::AccountManager, + session_cid: u64, + account_manager: &AccountManager, ) -> Result, Self::Error>; fn get_cid(&self) -> u64; @@ -104,38 +103,36 @@ pub trait UserIdentifierExt { #[async_trait] impl UserIdentifierExt for UserIdentifier { - type AccountManager = AccountManager; - type SearchOutput = ClientNetworkAccount; type Error = AccountError; /// Searches for the account - async fn search( + async fn search( &self, - account_manager: &AccountManager, - ) -> Result, AccountError> { + account_manager: &AccountManager, + ) -> Result>, AccountError> { match self { Self::ID(cid) => account_manager.get_client_by_cid(*cid).await, Self::Username(uname) => account_manager.get_client_by_username(uname).await, } } - /// Performs a search for the current peer given the `implicated_cid` - async fn search_peer( + /// Performs a search for the current peer given the `session_cid` + async fn search_peer( &self, - implicated_cid: u64, - account_manager: &AccountManager, + session_cid: u64, + account_manager: &AccountManager, ) -> Result, AccountError> { match self { UserIdentifier::ID(cid) => { account_manager .get_persistence_handler() - .get_hyperlan_peer_by_cid(implicated_cid, *cid) + .get_hyperlan_peer_by_cid(session_cid, *cid) .await } UserIdentifier::Username(name) => { account_manager .get_persistence_handler() - .get_hyperlan_peer_by_username(implicated_cid, name.as_str()) + .get_hyperlan_peer_by_username(session_cid, name.as_str()) .await } } diff --git a/citadel_user/tests/primary.rs b/citadel_user/tests/primary.rs index 125ec6ec5..f40211500 100644 --- a/citadel_user/tests/primary.rs +++ b/citadel_user/tests/primary.rs @@ -1,10 +1,7 @@ #[cfg(test)] mod tests { - use citadel_crypt::prelude::ConstructorOpts; - use citadel_crypt::stacked_ratchet::constructor::{ - BobToAliceTransferType, StackedRatchetConstructor, - }; + use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; use citadel_crypt::stacked_ratchet::StackedRatchet; use citadel_types::crypto::KemAlgorithm; use citadel_user::account_manager::AccountManager; @@ -860,29 +857,29 @@ mod tests { ); assert_eq!( - peer_pers - .hyperlan_peers_are_mutuals(peer_cnac.get_cid(), &[client.get_cid()]) + pers_cl + .hyperlan_peers_are_mutuals(client.get_cid(), &[peer_cnac.get_cid()]) .await .unwrap(), vec![true] ); assert_eq!( - pers_cl - .hyperlan_peers_are_mutuals(client.get_cid(), &[peer_cnac.get_cid()]) + pers_se + .hyperlan_peers_are_mutuals(peer_cnac.get_cid(), &[client.get_cid()]) .await .unwrap(), vec![true] ); assert_eq!( pers_se - .hyperlan_peers_are_mutuals(peer_cnac.get_cid(), &[client.get_cid()]) + .hyperlan_peers_are_mutuals(client.get_cid(), &[peer_cnac.get_cid()]) .await .unwrap(), vec![true] ); assert_eq!( pers_se - .hyperlan_peers_are_mutuals(client.get_cid(), &[peer_cnac.get_cid()]) + .hyperlan_peers_are_mutuals(peer_cnac.get_cid(), &[client.get_cid()]) .await .unwrap(), vec![true] @@ -1448,11 +1445,11 @@ mod tests { ) -> (StackedRatchet, StackedRatchet) { let opts = ConstructorOpts::new_vec_init( Some(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256), - 1, + Default::default(), ); let mut alice = - StackedRatchetConstructor::new_alice(opts.clone(), cid, version, None).unwrap(); - let bob = StackedRatchetConstructor::new_bob( + StackedRatchetConstructor::new_alice_constructor(opts.clone(), cid, version).unwrap(); + let mut bob = StackedRatchetConstructor::new_bob_constructor::>( cid, version, opts, @@ -1461,10 +1458,7 @@ mod tests { ) .unwrap(); alice - .stage1_alice( - BobToAliceTransferType::Default(bob.stage0_bob().unwrap()), - &[], - ) + .stage1_alice::>(bob.stage0_bob().unwrap(), &[]) .unwrap(); let bob = if let Some(cid) = endpoint_bob_cid { bob.finish_with_custom_cid(cid).unwrap() diff --git a/context.ai.json b/context.ai.json index aa99525c3..01c131909 100644 --- a/context.ai.json +++ b/context.ai.json @@ -1,2229 +1,2242 @@ [ - { - "file": "./citadel_sdk/src/responses.rs", - "context": [ - "Provides helper functions for protocol responses", - "Handles peer registration responses", - "Manages peer connection responses", - "Processes group invitation responses", - "Handles automatic ticket management", - "Manages connection type reversal", - "Performs username resolution and validation", - "Requires matching request tickets", - "Integrates with Remote interface", - "Supports PeerSignal and NodeResult handling" - ] - }, - { - "file": "./citadel_sdk/src/test_common.rs", - "context": [ - "Provides testing utilities for Citadel Protocol", - "Supports test server creation and configuration", - "Implements synchronization barriers for multi-peer tests", - "Includes UDP mode testing utilities", - "Provides P2P connection testing helpers", - "Manages local test peer coordination", - "Requires localhost-testing feature for most functionality", - "Integrates with NodeBuilder for server configuration", - "Supports EmptyKernel for basic testing", - "Includes connection validation utilities" - ] - }, - { - "file": "./citadel_sdk/src/remote_ext.rs", - "context": [ - "Extends NodeRemote with high-level protocol operations", - "Provides user registration and authentication", - "Manages connections and file transfers", - "Supports encrypted virtual filesystem", - "Handles peer discovery and group communication", - "Implements security settings configuration", - "Uses asynchronous operations throughout", - "Supports chunked file transfers for efficiency", - "Requires mutual registration for peer connections", - "Integrates with client-server and P2P modes" - ] - }, - { - "file": "./citadel_proto/src/inner_arg.rs", - "context": [ - "Provides type-safe parameter reference handling", - "Implements wrapper types for mutable and immutable references", - "Uses zero-cost abstractions for performance", - "Enforces proper dereferencing behavior", - "Preserves mutability constraints", - "Integrates with packet processing and validation", - "Supports cryptographic operation safety" - ] - }, - { - "file": "./citadel_proto/src/functional.rs", - "context": [ - "Provides functional programming utilities and extensions", - "Implements monadic-style operations and conditional chaining", - "Supports method chaining with Then trait", - "Provides conditional branching with IfEq and IfTrue", - "Implements tuple mapping with PairMap", - "Uses zero-cost abstractions and lazy evaluation", - "Enhances code readability throughout the codebase" - ] - }, - { - "file": "./citadel_proto/src/constants.rs", - "context": [ - "Defines core protocol constants and configuration", - "Manages protocol version using semantic versioning", - "Specifies network parameters and MTU sizes", - "Controls timing intervals and timeouts", - "Sets buffer sizes and group limitations", - "Configures security level update frequencies", - "Defines port ranges and networking defaults" - ] - }, - { - "file": "./citadel_proto/src/auth.rs", - "context": [ - "Defines authentication request types for Citadel Protocol", - "Supports both credential-based and passwordless authentication", - "Uses SecBuffer for secure credential handling", - "Manages user identification through CID and usernames", - "Handles server connection information for authentication", - "Implements transient device-based connections" - ] - }, - { - "file": "./citadel_proto/src/proto/validation.rs", - "context": [ - "Core packet validation module for the Citadel Protocol", - "Implements security-critical validation for all packet types", - "Handles connection, registration, group, and file transfer validation", - "Uses AEAD cryptography for packet integrity verification", - "Implements zero-copy validation where possible", - "Contains submodules for different validation contexts: do_connect, group, do_register, do_drill_update, pre_connect, file, and aead", - "Maintains protocol state consistency across all validation steps" - ] - }, - { - "file": "./citadel_proto/src/proto/session.rs", - "context": [ - "Core session management implementation for Citadel Protocol", - "Handles active connections between peers with state management", - "Implements secure file transfer with configurable security levels", - "Supports UDP connectivity for performance-critical operations", - "Provides clean shutdown and resource cleanup mechanisms", - "Handles connection interruptions and session resumption", - "Uses post-quantum cryptographic primitives for session security", - "Manages packet processing and stream handling", - "Implements session initialization and parameter configuration", - "Supports both TCP and UDP transport protocols", - "Handles authentication and credential management", - "Provides virtual connection management for different transfer types" - ] - }, - { - "file": "./citadel_proto/src/proto/session_queue_handler.rs", - "context": [ - "Implements queue-based task scheduling for protocol sessions", - "Uses DelayQueue for managing timed operations", - "Handles both reserved system tasks (indices 0-9) and ordinary tasks (10+)", - "Supports one-shot tasks for single-execution operations", - "Integrates with session state management", - "Implements Stream and Future traits for async operation", - "Provides thread-safe task scheduling through atomic operations", - "Manages session shutdown and cleanup" - ] - }, - { - "file": "./citadel_proto/src/proto/packet.rs", - "context": [ - "Implements the core Hypernode Data Protocol (HDP) packet structure", - "Defines packet headers, commands, and buffer handling", - "Provides zero-copy header parsing for efficiency", - "Manages packet composition and decomposition", - "Supports both BytesMut and Vec buffer types", - "Handles socket address tracking and packet routing", - "Implements hierarchical command organization" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/mod.rs", - "context": [ - "Core packet processing infrastructure for the Citadel Protocol", - "Key aspects:", - "- Manages multiple packet types (connection, auth, data)", - "- Implements processing pipeline with validation", - "- Handles packet security and integrity", - "- Provides backpressure and ordering guarantees", - "- Coordinates between different packet processors" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/connect_packet.rs", - "context": [ - "Handles connection establishment in Citadel Protocol", - "Key aspects:", - "- Implements secure multi-stage handshake", - "- Manages post-quantum key exchange", - "- Handles version and capability negotiation", - "- Provides connection state management", - "- Supports transport-agnostic connections" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/disconnect_packet.rs", - "context": [ - "Implements graceful disconnection protocol", - "Uses two-stage disconnect handshake", - "Manages secure packet validation", - "Handles session state transitions", - "Coordinates with kernel for disconnect signals", - "Implements packet delivery delay for reliability", - "Tracks disconnection tickets" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/file_packet.rs", - "context": [ - "Handles secure file transfer operations", - "Manages file transfer lifecycle", - "Supports chunked transfers", - "Integrates with virtual filesystem", - "Implements transfer state tracking", - "Handles both direct and proxied transfers" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/keep_alive_packet.rs", - "context": [ - "Implements connection maintenance through keep-alive packets", - "Key aspects:", - "- Manages periodic heartbeat packets", - "- Detects connection liveness", - "- Monitors connection quality", - "- Handles connection timeouts", - "- Triggers automatic reconnection" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/deregister_packet.rs", - "context": [ - "Handles client deregistration process", - "Manages secure account removal", - "Implements resource cleanup", - "Validates session state", - "Provides ticket-based tracking", - "Handles success/failure states", - "Manages client and server cleanup", - "Maintains security during removal", - "Integrates with account management", - "Reports deregistration results" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/raw_primary_packet.rs", - "context": [ - "Low-level packet processing for primary data packets", - "Key aspects:", - "- Handles raw packet operations", - "- Manages packet headers and payloads", - "- Implements zero-copy processing", - "- Ensures packet integrity", - "- Validates packet structure" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/hole_punch.rs", - "context": [ - "Implements NAT traversal through hole punching", - "Enables direct P2P connections behind NATs", - "Handles NAT traversal packet processing", - "Provides secure packet validation", - "Manages peer connection coordination", - "Implements connection pipe management", - "Supports proxied connections", - "Manages hole puncher pipes", - "Integrates with proxy system", - "Ensures authenticated packet handling" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/register_packet.rs", - "context": [ - "Handles client registration in Citadel Protocol", - "Implements secure multi-stage handshake", - "Provides passwordless registration support", - "Manages session state transitions", - "Implements cryptographic parameter negotiation", - "Handles registration failure cases", - "Supports post-quantum cryptography", - "Validates registration parameters", - "Integrates with account management", - "Manages initial cryptographic setup" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/rekey_packet.rs", - "context": [ - "Implements secure key rotation mechanism", - "Manages cryptographic key updates", - "Provides multi-stage key exchange", - "Implements post-quantum cryptography", - "Ensures perfect forward secrecy", - "Handles state synchronization", - "Supports proxy connections", - "Manages ratchet state updates", - "Handles key truncation operations", - "Integrates with security level system" - ] - }, - { - "file": "./citadel_proto/src/proto/session_manager.rs", - "context": [ - "Core session management system for Citadel Protocol", - "Handles stateful connections between peers", - "Manages both provisional and established connections", - "Implements secure peer-to-peer communication", - "Provides group broadcast functionality", - "Supports configurable security levels", - "Handles virtual connection management", - "Implements clean shutdown procedures", - "Manages connection upgrades and transitions", - "Integrates with HyperNodePeerLayer for P2P operations" - ] - }, - { - "file": "./citadel_user/src/lib.rs", - "context": [ - "Core user management system", - "Manages HyperVPN architecture", - "Provides hierarchical account structure", - "Handles authentication and security", - "Supports multiple backend storage", - "Integrates external services", - "Manages account lifecycle", - "Implements zero-trust architecture", - "Supports post-quantum cryptography", - "Provides feature-gated functionality" - ] - }, - { - "file": "./citadel_user/src/external_services/service_interface.rs", - "context": [ - "Defines external service interface", - "Provides unified service communication", - "Supports async data transmission", - "Handles raw packet data", - "Enables peer-to-peer communication", - "Standardizes error handling", - "Ensures thread safety", - "Manages service connections", - "Implements common service trait", - "Supports multiple service types" - ] - }, - { - "file": "./citadel_user/src/external_services/rtdb.rs", - "context": [ - "Provides Firebase RTDB integration", - "Manages real-time data synchronization", - "Handles authentication and tokens", - "Manages connection lifecycle", - "Implements data transfer interface", - "Supports connection refresh", - "Handles token expiration", - "Provides client configuration", - "Manages RTDB instances", - "Implements efficient data sync" - ] - }, - { - "file": "./citadel_user/src/external_services/mod.rs", - "context": [ - "Manages external service integration", - "Provides Google services support", - "Handles service configuration", - "Manages service authentication", - "Supports Firebase RTDB", - "Handles JWT management", - "Provides WASM compatibility", - "Manages service state", - "Handles post-login services", - "Supports feature flags" - ] - }, - { - "file": "./citadel_user/src/auth/proposed_credentials.rs", - "context": [ - "Manages credential proposals", - "Handles password hashing", - "Validates credentials", - "Supports passwordless auth", - "Implements username sanitization", - "Provides secure password storage", - "Manages authentication modes", - "Handles server validation", - "Implements credential comparison", - "Ensures memory safety" - ] - }, - { - "file": "./citadel_user/src/backend/sql_backend.rs", - "context": [ - "Provides SQL database storage", - "Supports multiple SQL variants", - "Implements connection pooling", - "Handles SQL syntax differences", - "Manages schema creation", - "Supports blob and text storage", - "Implements atomic operations", - "Manages peer relationships", - "Provides efficient querying", - "Enables ACID compliance" - ] - }, - { - "file": "./citadel_user/src/backend/redis_backend.rs", - "context": [ - "Provides distributed Redis storage", - "Implements connection pooling", - "Supports Redis clustering", - "Manages connection health checks", - "Handles connection timeouts", - "Provides automatic reconnection", - "Supports atomic operations", - "Manages peer relationships", - "Implements byte map storage", - "Enables high availability" - ] - }, - { - "file": "./citadel_user/src/backend/filesystem_backend.rs", - "context": [ - "Provides filesystem-based persistent storage", - "Implements hybrid storage with memory cache", - "Manages client network account persistence", - "Handles directory structure organization", - "Supports peer relationship storage", - "Implements virtual filesystem operations", - "Provides byte map storage functionality", - "Ensures atomic file operations", - "Supports personal and impersonal accounts", - "Integrates with memory backend for caching" - ] - }, - { - "file": "./citadel_user/src/backend/memory.rs", - "context": [ - "Provides in-memory backend storage", - "Implements thread-safe client storage", - "Manages peer relationships", - "Handles metadata operations", - "Supports byte map storage", - "Implements atomic operations", - "Manages resource cleanup", - "Supports WASM environments", - "Provides client registration", - "Maintains data consistency" - ] - }, - { - "file": "./citadel_user/src/backend/utils/mod.rs", - "context": [ - "Manages object transfer operations", - "Implements bidirectional transfers", - "Provides progress tracking", - "Handles transfer status updates", - "Implements stream-based transfers", - "Manages transfer control flow", - "Provides async transfer support", - "Handles resource cleanup", - "Implements transfer acceptance", - "Manages transfer orientation" - ] - }, - { - "file": "./citadel_user/src/backend/mod.rs", - "context": [ - "Defines core backend storage infrastructure", - "Manages multiple storage backend types", - "Implements unified backend interface", - "Provides async persistence operations", - "Handles database connections", - "Manages virtual filesystem operations", - "Implements thread-safe storage", - "Provides transaction management", - "Supports multiple database types", - "Handles automatic reconnection" - ] - }, - { - "file": "./citadel_user/src/auth/mod.rs", - "context": [ - "Manages authentication modes for CNACs", - "Implements Argon2id password hashing", - "Supports passwordless authentication", - "Handles username uniqueness", - "Manages full name storage", - "Provides authentication state checks", - "Implements serializable auth data", - "Handles credential validation", - "Manages authentication modes", - "Provides secure data storage" - ] - }, - { - "file": "./citadel_user/src/account_loader.rs", - "context": [ - "Manages loading of serialized client network accounts", - "Handles both personal and impersonal account types", - "Provides generic file loading capabilities", - "Implements efficient buffered I/O operations", - "Supports extensible deserialization", - "Manages filesystem-based account storage", - "Handles account loading errors gracefully", - "Integrates with directory management", - "Supports cryptographic account security", - "Provides account persistence functionality" - ] - }, - { - "file": "./citadel_user/src/server_misc_settings.rs", - "context": [ - "Manages server-side configuration settings", - "Controls passwordless authentication", - "Enforces credential requirements", - "Provides default security settings", - "Integrates with account management", - "Supports customizable authentication flows", - "Handles server-side security policies", - "Configures node authentication behavior", - "Manages credential validation rules", - "Controls server security features" - ] - }, - { - "file": "./citadel_user/src/serialization.rs", - "context": [ - "Provides binary serialization functionality", - "Implements efficient buffer operations", - "Supports in-place deserialization", - "Handles size estimation and pre-allocation", - "Uses bincode for binary encoding", - "Integrates with bytes buffer system", - "Provides trait-based serialization", - "Manages memory-efficient operations", - "Supports slice-based serialization", - "Implements error handling with AccountError" - ] - }, - { - "file": "./citadel_user/src/connection_metadata.rs", - "context": [ - "Manages connection metadata for client connections", - "Handles connection protocol specifications", - "Supports TCP, TLS, and QUIC protocols", - "Stores socket address information", - "Provides domain name handling", - "Implements serialization support", - "Manages connection state persistence", - "Supports connection info display", - "Handles protocol-specific settings", - "Maintains connection type information" - ] - }, - { - "file": "./citadel_user/src/directory_store.rs", - "context": [ - "Manages filesystem structure for Citadel Protocol", - "Handles directory creation and organization", - "Provides cross-platform path management", - "Organizes account storage directories", - "Manages configuration file locations", - "Supports virtual filesystem structure", - "Handles file transfer storage", - "Implements path formatting and generation", - "Maintains directory hierarchy", - "Ensures consistent file organization" - ] - }, - { - "file": "./citadel_user/src/credentials.rs", - "context": [ - "Manages credential validation and requirements", - "Defines username and password constraints", - "Enforces length and format restrictions", - "Provides configurable validation rules", - "Handles full name format validation", - "Implements default security policies", - "Supports custom requirement definitions", - "Ensures consistent credential formats", - "Validates credential formatting", - "Defines system-wide credential limits" - ] - }, - { - "file": "./citadel_user/src/hypernode_account.rs", - "context": [ - "Core HyperNode account functionality", - "Provides user identification extensions", - "Handles account search operations", - "Manages peer relationship lookups", - "Supports CID and username-based identification", - "Implements async account operations", - "Defines serialization file extension", - "Enables flexible account type extensions", - "Maintains bi-directional peer relationships", - "Integrates with persistence backends" - ] - }, - { - "file": "./citadel_user/src/account_manager.rs", - "context": [ - "Central user account management system", - "Handles account creation and registration", - "Supports multiple storage backends (Memory, File, SQL, Redis)", - "Manages HyperLAN peer relationships", - "Provides secure credential storage", - "Implements Argon2id password hashing", - "Supports personal and impersonal modes", - "Handles P2P connection registration", - "Provides thread-safe async operations", - "Manages external service integrations" - ] - }, - { - "file": "./citadel_user/src/client_account.rs", - "context": [ - "Manages individual client connections in Citadel Protocol", - "Handles both personal and impersonal connection modes", - "Implements secure credential management and validation", - "Provides ratchet-based cryptographic state management", - "Manages HyperLAN and HyperWAN peer relationships", - "Supports thread-safe operations through RwLock", - "Handles connection endpoint configuration", - "Implements peer list synchronization", - "Provides P2P connection support", - "Maintains immutable critical security fields" - ] - }, - { - "file": "./citadel_crypt/src/argon/autotuner.rs", - "context": [ - "Automatic parameter tuning for Argon2 password hashing", - "Implements ORY's guidelines for parameter selection", - "Uses memory-first tuning strategy for optimal security", - "Dynamically adjusts based on system capabilities", - "Supports multi-threading with CPU core detection", - "Provides configurable minimum execution time", - "Includes safeguards for memory usage", - "Implements iterative parameter optimization", - "Supports custom hash lengths and secret keys", - "Designed for release-mode performance tuning" - ] - }, - { - "file": "./citadel_crypt/src/lib.rs", - "context": [ - "Core cryptographic framework for the Citadel Protocol", - "Implements post-quantum cryptography and perfect forward secrecy", - "Provides secure memory management with zero-copy operations", - "Features entropy banking system for key derivation", - "Includes packet vectorization and port scrambling", - "Implements FCM (Forward Chain Messaging) primitives", - "Integrates Argon2 with auto-tuning capabilities", - "All operations are thread-safe and memory-efficient", - "Uses defense-in-depth with multiple security layers", - "Automatic memory zeroing for sensitive data" - ] - }, - { - "file": "./citadel_wire/src/lib.rs", - "context": [ - "Main entry point for Citadel Wire crate", - "Provides secure peer-to-peer connection capabilities", - "Implements NAT traversal and UDP hole punching", - "Enforces zero unsafe code policy", - "Uses async-first design with security focus" - ] - }, - { - "file": "./async_ip/src/lib.rs", - "context": [ - "Utility crate for asynchronous IP address resolution", - "Provides both IPv4 and IPv6 resolution with fallback mechanisms", - "Supports internal and external IP detection", - "Uses multiple services concurrently for reliability", - "Has special handling for WebAssembly environments", - "Key component for NAT traversal and network identification", - "Uses HTTP-based IP resolution services with configurable endpoints", - "Implements custom error handling for network and parsing failures" - ] - }, - { - "file": "./firebase-rtdb/src/lib.rs", - "context": [ - "Lightweight async Rust client for Firebase Realtime Database", - "Provides JWT-based authentication with automatic token renewal", - "Implements all CRUD operations with JSON serialization", - "Uses hierarchical node-based database access", - "Handles connection timeouts and TLS security", - "Provides error handling for network and database operations", - "Supports connection pooling and TCP nodelay for performance", - "Implements Firebase Security Rules compatibility", - "Uses reqwest for HTTP communication with async support", - "Manages authentication state and token expiration" - ] - }, - { - "file": "./firebase-rtdb/Cargo.toml", - "context": [ - "Package configuration for Firebase RTDB client", - "Uses workspace-level dependency management", - "Configures reqwest with rustls-tls for secure communication", - "Includes serde for JSON serialization support", - "Defines crate metadata and documentation links", - "Specifies test dependencies for integration testing" - ] - }, - { - "file": "./firebase-rtdb/tests/primary.rs", - "context": [ - "Integration test file for Firebase RTDB client", - "Currently empty, prepared for future test implementations" - ] - }, - { - "file": "./netbeam/src/lib.rs", - "context": [ - "Core library for high-performance networking with multiplexing support", - "Provides reliable ordered message delivery guarantees", - "Implements network-aware synchronization primitives", - "Uses zero unsafe code and leverages Rust's type system", - "Requires Tokio runtime for async operations", - "Supports both client and server modes with bi-directional communication", - "Includes time tracking utilities for network operations", - "Features proper error handling and type-safe APIs" - ] - }, - { - "file": "./netbeam/src/reliable_conn.rs", - "context": [ - "Core traits for reliable network connections", - "Implements ordered message delivery guarantees", - "Supports both direct and NAT-traversed connections", - "Provides connection addressing abstraction", - "Includes serialization support for messages", - "Implements network simulation for testing" - ] - }, - { - "file": "./netbeam/src/multiplex.rs", - "context": [ - "Network stream multiplexing implementation", - "Enables multiple logical connections over single physical connection", - "Provides bi-directional communication channels", - "Implements automatic stream ID generation and management", - "Ensures thread-safe subscription handling", - "Supports custom connection key types", - "Maintains message ordering within streams", - "Handles graceful stream initialization and cleanup", - "Uses pre-action and post-action hooks for lifecycle management", - "Provides both borrowed and owned subscription types" - ] - }, - { - "file": "./netbeam/src/time_tracker.rs", - "context": [ - "Provides precise timing utilities for network operations", - "Uses monotonic system time for consistency", - "Implements nanosecond precision timing", - "Handles time overflow protection", - "Used for latency measurements and timing-sensitive operations" - ] - }, - { - "file": "./citadel_types/src/lib.rs", - "context": [ - "Core type definitions for the Citadel Protocol", - "Provides fundamental data structures and utilities", - "Includes cryptographic types and parameters", - "Defines protocol-specific message types", - "Contains user-related data structures", - "Implements error handling and validation", - "Exports commonly used types through prelude" - ] - }, - { - "file": "./citadel_types/src/crypto/mod.rs", - "context": [ - "Defines cryptographic types and parameters", - "Implements secure memory management with SecBuffer", - "Provides algorithm selection and configuration", - "Supports post-quantum cryptography algorithms", - "Includes security level specifications", - "Implements secure memory locking and zeroing", - "Provides serialization for cryptographic types", - "Supports various secrecy modes for different use cases" - ] - }, - { - "file": "./citadel_logging/src/lib.rs", - "context": [ - "Structured logging facade for the Citadel Protocol", - "Built on top of the tracing ecosystem", - "Provides consistent logging setup across components", - "Supports file and line number information", - "Implements environment-based log level filtering", - "Includes panic handling with logging", - "Supports async-aware instrumentation", - "Uses span-based structured logging", - "Provides multiple log levels with target-based filtering" - ] - }, - { - "file": "./citadel_io/src/lib.rs", - "context": [ - "Cross-platform I/O utility crate for native and WebAssembly targets", - "Provides consistent interfaces for synchronization primitives", - "Implements platform-specific random number generation", - "Abstracts async runtime differences between native and WASM", - "Re-exports Tokio ecosystem with platform-specific implementations", - "Supports deadlock detection on native platforms" - ] - }, - { - "file": "./citadel_io/src/standard/locks.rs", - "context": [ - "Native platform synchronization primitives using parking_lot", - "High-performance mutex and read-write lock implementations", - "RAII-style lock guards for automatic resource management", - "More efficient than standard library synchronization types" - ] - }, - { - "file": "./citadel_io/src/wasm/locks.rs", - "context": [ - "WebAssembly-compatible synchronization primitives", - "Wraps standard library locks for WASM compatibility", - "Maintains API compatibility with native code", - "Single-threaded implementation for current WASM limitations" - ] - }, - { - "file": "./citadel_io/src/wasm/rng.rs", - "context": [ - "WebAssembly-compatible random number generation", - "Uses Web Crypto API through getrandom crate", - "Provides cryptographically secure random numbers", - "Implements RngCore and CryptoRng traits", - "Supports both fixed-size and dynamic buffer generation" - ] - }, - { - "file": "./citadel_pqcrypto/src/lib.rs", - "context": [ - "# Documentation Progress", - "## citadel_pqcrypto Crate", - "### Completed Documentation", - "- `lib.rs`: Crate-level documentation with overview, features, and examples", - "- `constructor_opts.rs`: Documentation for `ConstructorOpts` struct", - "- Enhanced `RecursiveChain` documentation with examples and security considerations", - "- `encryption.rs`: Module-level documentation", - "- `AeadModule` trait documentation", - "- `AesModule` implementation docs", - "- `ChaChaModule` implementation docs", - "- `AsconModule` implementation docs", - "- `KyberModule` implementation docs with quantum security considerations", - "- `export.rs`: Enhanced module-level documentation", - "- Key store serialization documentation", - "- Security considerations for key material", - "- Examples for serialization/deserialization", - "- `bytes_in_place.rs`: Comprehensive module documentation", - "- `InPlaceBuffer` and `EzBuffer` documentation", - "- Examples for buffer operations", - "- `wire.rs`: Module-level documentation", - "- Parameter transfer documentation", - "- Scrambling dictionary documentation", - "- `replay_attack_container.rs`: Module-level documentation", - "- Anti-replay attack mechanism documentation", - "- Examples and security considerations", - "### Security Considerations Documented", - "- Post-quantum cryptography principles", - "- Proper nonce handling", - "- Zeroization of sensitive data", - "- Constant-time operations", - "- Forward secrecy", - "- Local-user encryption for endpoint privacy", - "- Anti-replay attack protections", - "- Key material serialization safety", - "- Buffer operation safety", - "- Parameter transfer security", - "### Next Steps", - "1. Review remaining files in citadel_pqcrypto for any documentation gaps", - "2. Cross-reference documentation between related components", - "3. Ensure all security considerations are thoroughly documented", - "4. Add more real-world usage examples", - "### Code Style and Standards", - "- All documentation follows Rust documentation best practices", - "- Examples are provided for key functionality", - "- Security considerations are clearly outlined", - "- Cross-references between related components are maintained" - ] - }, - { - "file": "./citadel_pqcrypto/src/constructor_opts.rs", - "context": [ - "Provides configuration options for post-quantum cryptography (PQC) instances", - "Includes ConstructorOpts for PQC initialization and RecursiveChain for key derivation", - "Focuses on secure parameter handling and memory safety", - "Supports both initial and chained cryptographic operations" - ] - }, - { - "file": "./citadel_pqcrypto/src/bytes_in_place.rs", - "context": [ - "Implements memory-efficient and secure in-place buffer operations", - "Features window-based buffer access control for safe data manipulation", - "Supports both Vec and BytesMut buffer types", - "Emphasizes zero-copy operations and memory safety" - ] - }, - { - "file": "./citadel_pqcrypto/src/wire.rs", - "context": [ - "Implements secure wire protocol for PQC parameter transfer", - "Provides parameter transfer structures for Alice-Bob key exchange", - "Features data scrambling for additional security", - "Supports both symmetric and asymmetric encryption modes", - "Ensures memory safety and automatic parameter cleanup" - ] - }, - { - "file": "./citadel_pqcrypto/src/replay_attack_container.rs", - "context": [ - "Implements protection against replay attacks in communications", - "Uses circular buffer for efficient PID history tracking", - "Supports out-of-order packet delivery within configurable window", - "Provides thread-safe PID generation and validation", - "Features automatic state reset on re-keying" - ] - }, - { - "file": "./citadel_wire/src/hypernode_type.rs", - "context": [ - "This module defines network node types and their behaviors in the Citadel Protocol. It handles configuration for server nodes with static IPs and peer nodes in residential NAT environments, including automatic UPnP handling and NAT traversal fallback mechanisms" - ] - }, - { - "file": "./citadel_wire/src/error.rs", - "context": [ - "This module implements error types specific to network traversal and firewall operations", - "It provides custom error types for UPnP and hole punching operations, with conversion traits to standard IO errors", - "The error types help categorize and handle various network-related failure scenarios, including port mapping failures and exhausted hole punching attempts" - ] - }, - { - "file": "./citadel_wire/src/standard/nat_identification.rs", - "context": [ - "Provides NAT type identification and analysis", - "Uses STUN servers for NAT behavior detection", - "Analyzes port and IP translation patterns", - "Supports IPv4 and IPv6 compatibility checks", - "Determines optimal NAT traversal strategies" - ] - }, - { - "file": "./citadel_wire/src/standard/socket_helpers.rs", - "context": [ - "Provides socket creation and configuration utilities", - "Handles TCP and UDP socket setup", - "Implements platform-specific socket options", - "Supports IPv4 and IPv6 with automatic mapping", - "Manages socket reuse for NAT traversal" - ] - }, - { - "file": "./citadel_wire/src/standard/upnp_handler.rs", - "context": [ - "Provides UPnP port mapping and gateway management", - "Handles automatic gateway discovery and configuration", - "Manages port forwarding for TCP and UDP protocols", - "Supports configurable lease durations and targeted forwarding", - "Implements external IP and local IP detection" - ] - }, - { - "file": "./citadel_wire/src/standard/quic.rs", - "context": [ - "Implements QUIC protocol for secure connections", - "Supports client and server endpoints", - "Handles self-signed and PKCS#12 certificates", - "Provides NAT traversal-friendly transport", - "Uses Tokio for async/await support" - ] - }, - { - "file": "./citadel_wire/src/standard/tls.rs", - "context": [ - "Provides TLS configuration and certificate management", - "Supports both TLS and QUIC protocols", - "Handles self-signed and PKCS#12 certificates", - "Implements native system certificate loading", - "Uses Rustls for secure TLS implementation" - ] - }, - { - "file": "./citadel_wire/src/standard/misc.rs", - "context": [ - "Provides certificate format conversion utilities", - "Handles PKCS#12 to QUIC format conversion", - "Manages certificate chain extraction", - "Implements private key conversion", - "Ensures memory-safe certificate handling" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/hole_punch_config.rs", - "context": [ - "Provides configuration for UDP hole punching NAT traversal", - "Handles address prediction and socket preparation", - "Configures port ranges based on NAT behavior", - "Supports both IPv4 and IPv6 traversal", - "Implements iterator-based address generation" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/udp_hole_puncher.rs", - "context": [ - "Implements core UDP hole punching algorithm for NAT traversal", - "Handles asynchronous connection establishment between peers", - "Supports dual-stack IPv4/IPv6 with automatic retry", - "Manages encrypted configuration exchange", - "Provides NAT-aware socket binding optimization" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs", - "context": [ - "Manages NAT-aware UDP socket addressing", - "Handles separate send/receive addresses for NAT traversal", - "Provides packet validation and connection state tracking", - "Implements socket cleanup after hole punching", - "Supports UPnP address translation detection" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/mod.rs", - "context": [ - "Core UDP NAT traversal framework module", - "Coordinates multiple traversal methods and strategies", - "Manages UPnP and hole punching integration", - "Provides unique connection identification", - "Implements method prioritization and fallback" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs", - "context": [ - "Manages secure packet encryption for hole punching configuration", - "Handles custom STUN server configurations", - "Provides zero-copy packet handling optimizations", - "Supports localhost testing mode with disabled encryption", - "Implements thread-safe encryption function containers" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/linear/method3.rs", - "context": [ - "Implements advanced UDP hole punching with variable TTL values", - "Handles asymmetric NAT traversal failures with recovery mode", - "Provides encrypted configuration exchange between peers", - "Supports dual-stack IPv4/IPv6 operation", - "Implements thread-safe UDP socket operations" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/linear/mod.rs", - "context": [ - "Implements linear UDP hole punching for client-server scenarios", - "Coordinates multiple traversal methods with fallback strategy", - "Manages synchronized connection establishment timing", - "Provides recovery mode for asymmetric failures", - "Integrates UPnP support with automatic fallback" - ] - }, - { - "file": "./citadel_wire/src/udp_traversal/multi/mod.rs", - "context": [ - "Implements concurrent dual-stack UDP hole punching", - "Manages multiple traversal attempts across ports", - "Coordinates winner selection between attempts", - "Handles IPv4/IPv6 protocol selection", - "Provides multiplexed connection management" - ] - }, - { - "file": "./citadel_wire/src/standard/mod.rs", - "context": [ - "Provides core networking components for Citadel Protocol", - "Implements QUIC, TLS, and NAT traversal", - "Handles UPnP and socket configuration", - "Supports peer-to-peer networking", - "Uses async-first design philosophy" - ] - }, - { - "file": "./citadel_crypt/src/entropy_bank.rs", - "context": [ - "Implements dynamic entropy management", - "Provides secure nonce generation", - "Handles packet encryption/decryption", - "Prevents replay attacks", - "Manages transient counters", - "Integrates post-quantum operations" - ] - }, - { - "file": "./citadel_crypt/src/endpoint_crypto_container.rs", - "context": [ - "Manages peer session cryptographic state", - "Implements thread-safe ratchet updates", - "Handles version control and conflicts", - "Manages session key rotation", - "Supports atomic state transitions", - "Integrates with post-quantum cryptography" - ] - }, - { - "file": "./citadel_crypt/src/misc.rs", - "context": [ - "Provides cryptographic utility functions", - "Implements flexible error handling", - "Generates random port mappings", - "Supports security level validation", - "Implements error type conversions", - "Provides debug and display utilities" - ] - }, - { - "file": "./citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs", - "context": [ - "Implements secure partitioned buffer management", - "Provides fixed-size memory partitioning", - "Enforces strict partition boundaries", - "Supports zero-copy partition access", - "Implements automatic buffer zeroing", - "Provides thread-safe partition operations" - ] - }, - { - "file": "./citadel_crypt/src/packet_vector.rs", - "context": [ - "Implements secure packet sequencing", - "Provides wave-based packet organization", - "Manages scrambled port assignments", - "Implements sequence hiding", - "Supports automatic memory zeroing", - "Ensures ordered packet delivery" - ] - }, - { - "file": "./citadel_crypt/src/sync_toggle.rs", - "context": [ - "Provides thread-safe toggle state management", - "Implements atomic state transitions", - "Supports one-way toggle operations", - "Enables state change detection", - "Uses sequential consistency ordering" - ] - }, - { - "file": "./citadel_crypt/src/stacked_ratchet.rs", - "context": [ - "Implements perfect forward secrecy", - "Manages independent key evolution", - "Provides post-quantum support", - "Handles message protection", - "Implements anti-replay protection", - "Ensures ordered packet delivery" - ] - }, - { - "file": "./citadel_crypt/src/scramble/crypt_splitter.rs", - "context": [ - "Implements secure packet splitting and scrambling", - "Provides wave-based packet transmission", - "Supports dynamic packet sizing based on security level", - "Implements packet reconstruction with timeout handling", - "Manages encrypted and unencrypted packet transmission", - "Integrates with post-quantum cryptography" - ] - }, - { - "file": "./citadel_crypt/src/streaming_crypt_scrambler.rs", - "context": [ - "Implements asynchronous streaming encryption for large data sources", - "Supports both file-based and in-memory data sources", - "Provides backpressure support through async/await", - "Manages efficient group-based encryption and transmission", - "Supports custom header inscription for packets", - "Implements progress tracking and cancellation" - ] - }, - { - "file": "./citadel_crypt/src/toolset.rs", - "context": [ - "Manages cryptographic ratchet versioning and synchronization", - "Implements memory-bounded storage for active ratchets", - "Provides static auxiliary ratchet for persistent encryption", - "Handles secure ratchet updates and deregistration", - "Ensures thread-safe access to cryptographic primitives", - "Manages rolling window of active encryption keys" - ] - }, - { - "file": "./citadel_crypt/src/fcm/keys.rs", - "context": [ - "Manages Firebase Cloud Messaging (FCM) credentials", - "Provides thread-safe access to API keys and client IDs", - "Implements efficient memory management through Arc", - "Supports serialization for credential persistence", - "Ensures secure handling of sensitive credential data", - "Enables type-safe FCM key construction and access" - ] - }, - { - "file": "./citadel_crypt/src/fcm/fcm_ratchet.rs", - "context": [ - "Implements size-optimized cryptographic ratchet for FCM", - "Provides post-quantum secure messaging within 4KB limit", - "Manages secure key evolution and perfect forward secrecy", - "Supports both synchronous and asynchronous operations", - "Implements message protection and validation", - "Integrates with Firebase Cloud Messaging service" - ] - }, - { - "file": "./citadel_crypt/src/argon/argon_container.rs", - "context": [ - "Implements asynchronous Argon2 password hashing", - "Provides client and server-side password handling", - "Supports configurable memory and time cost parameters", - "Implements secure memory management with SecBuffer", - "Supports associated data and secret keys", - "Uses Argon2id variant for optimal security" - ] - }, - { - "file": "./citadel_crypt/src/secure_buffer/sec_packet.rs", - "context": [ - "Implements secure packet buffer handling", - "Provides three-part packet structure (header, payload, extension)", - "Enforces ordered write operations for packet construction", - "Implements zero-copy packet operations", - "Manages automatic memory cleanup", - "Supports fixed-size header optimization" - ] - }, - { - "file": "./citadel_crypt/src/secure_buffer/mod.rs", - "context": [ - "Provides secure buffer management module", - "Implements memory-safe buffer operations", - "Supports zero-copy data handling", - "Manages automatic memory zeroing", - "Includes packet writing utilities", - "Integrates with streaming operations" - ] - }, - { - "file": "./citadel_user/src/misc.rs", - "context": [ - "Provides core error handling for account operations", - "Manages CNAC metadata structures", - "Implements cross-platform path validation", - "Handles timestamp formatting", - "Provides virtual path management", - "Implements error type conversion", - "Manages account identification", - "Handles platform-specific path formatting", - "Provides directory validation", - "Implements metadata comparison" - ] - }, - { - "file": "./citadel_proto/src/proto/remote.rs", - "context": [ - "Implements remote communication functionality", - "Provides high-level interface for node communication", - "Supports asynchronous request/response patterns", - "Manages ticket-based request tracking", - "Integrates with account management system", - "Implements callback-based event handling", - "Ensures secure communication channels", - "Supports post-quantum cryptography", - "Handles network errors comprehensively", - "Provides bounded message sending" - ] - }, - { - "file": "./citadel_proto/src/proto/endpoint_crypto_accessor.rs", - "context": [ - "Manages cryptographic state access for P2P and C2S channels", - "Provides safe borrowing mechanisms for crypto operations", - "Implements version-aware state management", - "Ensures thread-safe container access", - "Integrates with post-quantum cryptography", - "Handles missing or invalid crypto states", - "Supports both peer-to-peer and client-server modes", - "Uses StackedRatchet for core crypto operations" - ] - }, - { - "file": "./citadel_proto/src/proto/session.rs", - "context": [ - "Core session management for Citadel Protocol", - "Handles active connections between peers", - "Manages authentication and key exchange", - "Implements secure file transfer functionality", - "Supports UDP connectivity for performance", - "Provides clean shutdown and resource cleanup", - "Uses post-quantum cryptography for security", - "Maintains perfect forward secrecy", - "Implements automatic key rotation", - "Handles session state transitions", - "Manages packet processing and connection state" - ] - }, - { - "file": "./citadel_proto/src/proto/node_request.rs", - "context": [ - "Defines node communication request types", - "Manages node registration and connections", - "Handles secure file transfer operations", - "Implements peer-to-peer command handling", - "Supports group broadcast functionality", - "Provides session security configuration", - "Implements key rotation operations", - "Uses ticket tracking for async responses", - "Supports SHA-256 password hashing", - "Configurable security levels for operations" - ] - }, - { - "file": "./citadel_proto/src/proto/state_container.rs", - "context": [ - "Core state management system for Citadel Protocol", - "Manages connection states and transitions", - "Handles virtual connections (P2P and C2S)", - "Implements secure group messaging", - "Manages file transfers with progress tracking", - "Supports UDP connectivity for performance", - "Implements cryptographic state verification", - "Protects against replay attacks", - "Provides secure group key management", - "Implements end-to-end file encryption", - "Handles connection timeouts and cleanup" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/mod.rs", - "context": [ - "Core packet processing infrastructure for the Citadel Protocol", - "Key aspects:", - "- Manages multiple packet types (connection, auth, data)", - "- Implements processing pipeline with validation", - "- Handles packet security and integrity", - "- Provides backpressure and ordering guarantees", - "- Coordinates between different packet processors" - ] - }, - { - "file": "./citadel_proto/src/proto/node.rs", - "context": [ - "Core networking implementation for Citadel Protocol", - "Supports multiple transport protocols (TCP, TLS, QUIC)", - "Implements NAT traversal for P2P connections", - "Manages concurrent network sessions", - "Uses post-quantum cryptography", - "Supports pre-shared key authentication", - "Handles both client-server and P2P modes", - "Provides protocol negotiation", - "Manages network socket listeners", - "Implements secure session establishment", - "Handles kernel communication", - "Supports TLS certificate management" - ] - }, - { - "file": "./citadel_proto/src/proto/node_result.rs", - "context": [ - "Defines result types for node operations", - "Handles registration, connection, and event results", - "Manages transfer and group operation outcomes", - "Provides comprehensive error reporting", - "Implements ticket tracking for async operations", - "Integrates with session and channel management" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/preconnect_packet.rs", - "context": [ - "Handles initial connection establishment", - "Implements NAT traversal and hole punching", - "Manages protocol version compatibility", - "Handles session state validation", - "Implements security level negotiation", - "Supports UDP and QUIC transports", - "Initializes cryptographic ratchets", - "Manages preconnect handshake flow", - "Validates session states", - "Processes connection requests", - "Handles NAT traversal configuration", - "Manages protocol version synchronization" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/udp_packet.rs", - "context": [ - "Handles UDP packet processing", - "Manages unordered channels", - "Validates packet security", - "Monitors channel state", - "Handles cleanup tasks", - "Processes secure payloads", - "Manages data transmission", - "Validates authentication", - "Handles session state", - "Manages buffer security", - "Processes channel drops", - "Handles error states" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/peer/mod.rs", - "context": [ - "Manages peer communications", - "Handles group broadcasts", - "Processes peer commands", - "Manages server interactions", - "Handles signal processing", - "Manages disconnect signals", - "Tracks peer sessions", - "Handles operation results", - "Manages error states", - "Supports ticket operations", - "Processes peer state", - "Coordinates peer modules" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/peer/signal_handler_interface.rs", - "context": [ - "Defines signal handling interface", - "Manages async signal operations", - "Handles outbound signals", - "Processes server signals", - "Manages target reception", - "Implements error handling", - "Uses async traits", - "Supports signal types", - "Manages signal flow", - "Requires type implementation", - "Handles network errors", - "Pending PeerSignal structification" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/peer/server/mod.rs", - "context": [ - "Manages server-side peer operations", - "Handles post-connection states", - "Processes post-registration", - "Validates peer sessions", - "Tracks session states", - "Manages server operations", - "Handles connection states", - "Processes registration completion", - "Coordinates peer modules", - "Manages state tracking", - "Handles peer validation", - "Manages session completion" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs", - "context": [ - "Handles post-connection phase", - "Establishes virtual connections", - "Configures security settings", - "Manages UDP channels", - "Routes peer signals", - "Synchronizes session states", - "Manages connection tables", - "Handles TCP channels", - "Processes peer responses", - "Manages security levels", - "Tracks connection states", - "Needs disconnect cleanup" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/peer/server/post_register.rs", - "context": [ - "Handles post-registration phase", - "Processes registration responses", - "Sets up HyperLAN P2P", - "Validates usernames", - "Routes signals", - "Manages tickets", - "Processes responses", - "Handles registration declines", - "Manages async registration", - "Needs error routing", - "Manages security levels", - "Tracks registration state" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs", - "context": [ - "Handles group messaging", - "Manages group membership", - "Processes group broadcasts", - "Controls group permissions", - "Tracks group state", - "Handles invitations", - "Manages group listing", - "Processes member states", - "Handles group creation", - "Manages group termination", - "Encrypts messages", - "Handles disconnections" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs", - "context": [ - "Processes peer commands", - "Routes peer signals", - "Handles key exchange", - "Manages group broadcasts", - "Tracks session states", - "Manages tickets", - "Handles error states", - "Mediates server operations", - "Validates authentication", - "Supports client/server roles", - "Manages peer sessions", - "Implements group operations" - ] - }, - { - "file": "./citadel_proto/src/proto/transfer_stats.rs", - "context": [ - "Handles network transfer statistics tracking with nanosecond precision", - "Core component for monitoring protocol performance metrics", - "Implements AddAssign for aggregating statistics over time", - "Tracks transfer rates, jitter, and total bytes transferred", - "Uses wrapping arithmetic to handle potential numeric overflows", - "Thread-safe design for concurrent statistics updates" - ] - }, - { - "file": "./citadel_proto/src/proto/packet_processor/channel.rs", - "context": [ - "Implements peer-to-peer communication channels", - "Provides both TCP and UDP channel implementations", - "Supports WebRTC compatibility through feature flag", - "Uses split architecture for async send/receive operations", - "Implements automatic resource cleanup on channel drop", - "Manages security levels for message encryption", - "Handles virtual connections between peers", - "Supports ordered and reliable message delivery", - "Integrates with the peer layer for connection management", - "Provides Stream trait implementation for async operations" - ] - }, - { - "file": "./citadel_proto/src/proto/peer_layer.rs", - "context": [ - "The peer layer module serves as the core peer-to-peer networking infrastructure in the Citadel Protocol. It manages peer connections, message groups, and signal routing between nodes. Key features include:", - "Peer signal management and routing between nodes", - "Message group functionality with concurrent/pending peer support", - "Ticket-based connection tracking and management", - "Timeout handling with callback support", - "HyperLAN client communication integration" - ] - }, - { - "file": "./citadel_proto/src/proto/p2p_conn_handler.rs", - "context": [ - "The P2P connection handler module implements direct peer-to-peer connections and NAT traversal in the Citadel Protocol. Notable features include:", - "Direct P2P connection management (TCP/UDP)", - "NAT traversal via UDP hole punching", - "Connection lifecycle management", - "WebRTC compatibility", - "Integration with Citadel's security infrastructure" - ] - }, - { - "file": "./citadel_proto/src/proto/group_channel.rs", - "context": [ - "The group channel module provides secure group communication channels in the Citadel Protocol. Key aspects include:", - "Split channel architecture (separate send/receive)", - "Group broadcast capabilities", - "Permission-based member management", - "Secure message encryption", - "Asynchronous message streaming support" - ] - }, - { - "file": "./citadel_proto/src/proto/message_group.rs", - "context": [ - "The message group module implements a consent-based group messaging framework in the Citadel Protocol. Key features include:", - "Axis of consent model centered around group initiator", - "Management of concurrent and pending peer states", - "Group lifecycle and permission management", - "Short-lived messaging frames for temporary communications", - "Local message history persistence" - ] - }, - { - "file": "./citadel_proto/src/proto/peer_crypt.rs", - "context": [ - "The peer cryptography module handles secure key exchange and NAT traversal for P2P connections. Notable aspects include:", - "Multi-stage key exchange protocol", - "NAT type detection and compatibility checking", - "TLS integration for secure connections", - "Configurable security levels and UDP modes", - "STUN/TURN server fallback support" - ] - }, - { - "file": "./citadel_proto/src/proto/hole_punch_compat_sink_stream.rs", - "context": [ - "The hole punch compatibility stream module provides NAT traversal functionality. Key features include:", - "Reliable ordered stream interface for hole punching", - "Protocol compatibility layer", - "Stacked ratchet encryption integration", - "Support for both C2S and P2P routing", - "Connection state and packet ordering management" - ] - }, - { - "file": "./citadel_proto/src/proto/mod.rs", - "context": [ - "Core implementation of the Citadel Protocol", - "Manages sessions, packet processing, and security", - "Implements peer-to-peer and group communication", - "Handles connection state management", - "Provides efficient packet validation and routing", - "Integrates with cryptographic and networking layers" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/mod.rs", - "context": [ - "The state subcontainers module provides specialized state management for different aspects of the Citadel Protocol's connection lifecycle. Key features include:", - "Connection state management and transitions", - "Key exchange and cryptographic state handling", - "Registration and authentication state tracking", - "State persistence and timeout management", - "Modular state container architecture" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", - "context": [ - "Core connection state management module for Citadel Protocol", - "Implements stage-based connection tracking and transitions", - "Handles credential management during connection process", - "Provides timing control and failure monitoring", - "Supports different connection modes and state recovery", - "Integrates with packet processing and session management", - "Uses atomic operations for thread-safe state transitions" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs", - "context": [ - "The peer key exchange management container handles P2P cryptographic states. Key features include:", - "Key exchange state management", - "Session security configuration", - "UDP channel control", - "Role-based key exchange", - "Session password management" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs", - "context": [ - "The pre-connection state container manages initial connection setup. Important aspects include:", - "Pre-connection stage tracking", - "Node type management", - "Cryptographic initialization", - "UDP channel setup", - "Connection ticket handling" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/register_state_container.rs", - "context": [ - "The registration state container handles user registration processes. Key features include:", - "Registration stage management", - "Cryptographic setup handling", - "Registration timing control", - "Failure state management", - "Passwordless registration support" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs", - "context": [ - "Manages account deregistration state in Citadel Protocol", - "Tracks deregistration process progress and timing", - "Handles deregistration tickets for process identification", - "Provides atomic state transitions for thread safety", - "Integrates with session and remote operations", - "Ensures proper validation of deregistration requests" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs", - "context": [ - "Manages expiration state for high-traffic packet processing", - "Prevents false expiration of active groups under high load", - "Supports both inbound and outbound traffic monitoring", - "Handles file transfer expiry tracking", - "Provides adaptive expiry timing based on workload", - "Integrates with packet processing and group management", - "Uses constant GROUP_EXPIRE_TIME_MS for timing control" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/rekey_container.rs", - "context": [ - "Manages cryptographic key rotation and ratchet updates", - "Implements security level-based key update scheduling", - "Supports peer-to-peer key updates and notifications", - "Provides configurable security levels (0-4)", - "Implements adaptive update frequency based on security needs", - "Handles local rekey requests and kernel notifications", - "Uses stacked ratchet construction for key rotation" - ] - }, - { - "file": "./citadel_proto/src/proto/codec.rs", - "context": [ - "Basic bytes codec implementation for raw data transmission", - "Handles efficient encoding/decoding with configurable buffer capacity", - "Implements zero-copy operations for performance", - "Uses tokio_util's Encoder and Decoder traits", - "Maintains minimum buffer size with auto-resizing" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/rekey_container.rs", - "context": [ - "Manages cryptographic key rotation and ratchet updates", - "Implements security level-based key update scheduling", - "Supports peer-to-peer key updates with state tracking", - "Provides configurable security levels (0-4)", - "Handles local rekey requests and notifications", - "Implements adaptive update frequency based on security needs" - ] - }, - { - "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", - "context": [ - "Manages active connection states with stage-based transitions", - "Handles connection credentials and authentication", - "Tracks connection timing and failures", - "Supports different connection modes", - "Provides state recovery mechanisms", - "Integrates with session and packet processing systems" - ] - }, - { - "file": "./citadel_proto/src/proto/peer/group_channel.rs", - "context": [ - "Implements secure group communication channels", - "Uses split channel architecture for send/receive operations", - "Supports group broadcasts and member management", - "Implements permission-based group operations", - "Provides Stream trait implementation for async messaging", - "Uses SecBuffer for encrypted message payloads", - "Handles proper cleanup through Drop implementations" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/clean_shutdown.rs", - "context": [ - "The clean shutdown module provides utilities for gracefully terminating components in the Citadel Protocol. It ensures proper resource cleanup and component notification during shutdown operations.", - "Key aspects:", - "- Implements asynchronous shutdown coordination", - "- Manages resource cleanup during shutdown", - "- Provides timeout-based forced shutdown", - "- Ensures thread-safe shutdown operations", - "- Broadcasts shutdown notifications to components" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/dual_cell.rs", - "context": [ - "The dual cell module implements a flexible cell type that adapts to both single-threaded and multi-threaded contexts through compile-time feature selection.", - "Key aspects:", - "- Uses Cell in single-threaded mode", - "- Uses atomics in multi-threaded mode", - "- Provides zero-cost thread safety abstractions", - "- Ensures proper Send + Sync implementations", - "- Maintains consistent API across modes" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/dual_late_init.rs", - "context": [ - "The dual late init module provides a container for safely initializing values after construction. It ensures proper initialization semantics across different threading contexts.", - "Key aspects:", - "- Ensures safe late initialization", - "- Tracks initialization state", - "- Prevents double initialization", - "- Provides thread-safe access", - "- Zero overhead in single-threaded mode" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/dual_rwlock.rs", - "context": [ - "The dual rwlock module implements a flexible read-write lock that adapts to both single-threaded and multi-threaded contexts through compile-time features.", - "Key aspects:", - "- Uses RefCell in single-threaded mode", - "- Uses RwLock in multi-threaded mode", - "- Supports multiple readers", - "- Provides exclusive writer access", - "- Prevents deadlocks", - "- Zero-cost thread safety abstractions" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/lock_holder.rs", - "context": [ - "The lock holder module provides safe lock management across asynchronous boundaries in the Citadel Protocol. It ensures proper lock handling and cleanup.", - "Key aspects:", - "- Manages locks in async contexts", - "- Prevents deadlocks", - "- Implements RAII-style cleanup", - "- Tracks lock states", - "- Ensures thread safety", - "- Prevents resource leaks" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/mod.rs", - "context": [ - "The misc module provides utility types and functions used throughout the Citadel Protocol implementation.", - "Key aspects:", - "- Provides thread-safe data structures", - "- Implements network utilities", - "- Manages protocol resources", - "- Supports async operations", - "- Defines protocol types", - "- Ensures zero-cost abstractions" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/net.rs", - "context": [ - "The network utilities module provides core networking functionality for the Citadel Protocol, handling connections and protocol operations.", - "Key aspects:", - "- Manages network sockets and connections", - "- Handles address resolution", - "- Implements protocol negotiation", - "- Supports multiple transport protocols", - "- Provides error handling and recovery", - "- Handles both IPv4 and IPv6" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/ordered_channel.rs", - "context": [ - "The ordered channel module implements a messaging channel that guarantees message ordering for protocol operations.", - "Key aspects:", - "- Ensures strict message ordering", - "- Implements asynchronous operations", - "- Provides backpressure mechanisms", - "- Supports multiple producers", - "- Maintains thread safety", - "- Handles channel state and cleanup" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/panic_future.rs", - "context": [ - "The panic future module provides a wrapper for safely handling panics in asynchronous code, preventing process crashes.", - "Key aspects:", - "- Catches and handles panics", - "- Converts panics to errors", - "- Wraps futures safely", - "- Ensures proper unwinding", - "- Maintains thread safety", - "- Preserves error types" - ] - }, - { - "file": "./citadel_proto/src/proto/misc/session_security_settings.rs", - "context": [ - "The session security settings module defines configuration options for securing protocol sessions.", - "Key aspects:", - "- Configures encryption parameters", - "- Manages authentication settings", - "- Controls key management", - "- Sets security levels", - "- Handles protocol versions", - "- Provides secure defaults" - ] - }, - { - "file": "./netbeam/src/sync/subscription.rs", - "context": [ - "Provides bidirectional subscription-based streaming", - "Key aspects:", - "- Implements reliable ordered messaging", - "- Manages stream subscriptions", - "- Supports connection multiplexing", - "- Handles connection lifecycle", - "- Ensures thread-safety and ordering" - ] - }, - { - "file": "./netbeam/src/sync/sync_start.rs", - "context": [ - "Implements network operation synchronization primitives", - "Key aspects:", - "- Coordinates operation start between nodes", - "- Supports type-safe payload exchange", - "- Handles network latency compensation", - "- Integrates with async/await", - "- Provides timing coordination" - ] - }, - { - "file": "./netbeam/src/sync/network_endpoint.rs", - "context": [ - "Provides network endpoint abstraction with address management", - "Key aspects:", - "- Manages socket addresses", - "- Handles connection registration", - "- Tracks connection roles", - "- Integrates with network applications", - "- Provides address resolution" - ] - }, - { - "file": "./netbeam/src/sync/network_application.rs", - "context": [ - "Core network application implementation with synchronization primitives", - "Key aspects:", - "- Provides network synchronization primitives", - "- Implements network operations (select, join)", - "- Manages communication channels", - "- Handles connection multiplexing", - "- Ensures operation coordination" - ] - }, - { - "file": "./netbeam/src/sync/callback_channel.rs", - "context": [ - "Specialized channel implementation for async request-response patterns", - "Key aspects:", - "- Asynchronous message passing with optional callbacks", - "- Built on Tokio MPSC channels", - "- Supports fire-and-forget operations", - "- Thread-safe and cloneable", - "- Implements Stream trait for receiver" - ] - }, - { - "file": "./netbeam/src/sync/tracked_callback_channel.rs", - "context": [ - "Enhanced callback channel with request-response tracking", - "Key aspects:", - "- Request tracking with unique IDs", - "- Response correlation with requests", - "- Thread-safe tracking with atomics", - "- Memory-efficient response tracking", - "- Support for response timeouts" - ] - }, - { - "file": "./netbeam/src/sync/mod.rs", - "context": [ - "Core synchronization module for Netbeam framework", - "Key aspects:", - "- Network endpoint and application management", - "- Bidirectional channels with callbacks", - "- Request-response tracking", - "- Symmetric conversation tracking", - "- Reliable ordered streaming", - "- Thread-safe operations" - ] - }, - { - "file": "./netbeam/src/sync/operations/net_join.rs", - "context": [ - "Network-aware join operation for future synchronization", - "Key aspects:", - "- Synchronizes futures across network endpoints", - "- Returns when both endpoints complete", - "- Early error termination", - "- Type-safe generic values", - "- Network-aware node types" - ] - }, - { - "file": "./netbeam/src/sync/operations/net_try_join.rs", - "context": [ - "Network-aware try-join operation for fallible future synchronization", - "Key aspects:", - "- Synchronizes fallible futures across endpoints", - "- Returns when both endpoints complete", - "- Early error termination", - "- Type-safe generic value and error types", - "- State synchronization between nodes", - "- Network-aware node types" - ] - }, - { - "file": "./netbeam/src/sync/operations/net_select.rs", - "context": [ - "Network-aware select operation for racing futures", - "Key aspects:", - "- Races futures between network endpoints", - "- First endpoint to complete wins", - "- Built-in conflict resolution", - "- Type-safe generic result type", - "- Network-aware node types" - ] - }, - { - "file": "./netbeam/src/sync/operations/net_select_ok.rs", - "context": [ - "Network-aware select operation for racing fallible futures", - "Key aspects:", - "- Races fallible futures between endpoints", - "- First successful endpoint wins", - "- Built-in conflict resolution", - "- Error handling with Result type", - "- State synchronization between nodes", - "- Network-aware node types" - ] - }, - { - "file": "./netbeam/src/sync/primitives/net_mutex.rs", - "context": [ - "Distributed mutex implementation for network synchronization", - "Key aspects:", - "- Distributed mutual exclusion", - "- Network-aware locking", - "- Automatic lock release", - "- State synchronization", - "- Deadlock prevention", - "- Background state management" - ] - }, - { - "file": "./netbeam/src/sync/primitives/mod.rs", - "context": [ - "Provides network-aware synchronization primitives", - "Defines NetObject trait for network-compatible types", - "Houses distributed mutex and rwlock implementations", - "Ensures thread-safety and serializability across network boundaries", - "Integrates with Serde for object serialization" - ] - }, - { - "file": "./citadel_sdk/src/builder/mod.rs", - "context": [ - "Provides builder patterns for configuring Citadel Protocol components", - "Ensures type-safe configuration building", - "Validates component configurations", - "Supports flexible node setup for different network roles", - "Integrates with core networking kernel", - "Manages protocol implementation details" - ] - }, - { - "file": "./citadel_sdk/src/builder/node_builder.rs", - "context": [ - "Implements builder pattern for Citadel network nodes", - "Supports both peer and server node configuration", - "Provides multiple backend storage options", - "Configures security settings (TLS, certificates)", - "Integrates with Google services", - "Supports STUN server configuration for NAT traversal", - "Manages server authentication via pre-shared keys", - "Handles kernel executor settings", - "Configures password hashing settings", - "Supports database configuration for enterprise features" - ] - }, - { - "file": "./citadel_sdk/src/backend_kv_store.rs", - "context": [ - "Implements connection-scoped persistent key-value storage", - "Provides async operations for storing and retrieving arbitrary data", - "Uses session and peer IDs for storage isolation", - "Integrates with the persistence handler backend", - "Supports bulk operations for managing multiple key-value pairs", - "Implements automatic error handling and conversion", - "Designed for application-level data persistence" - ] - }, - { - "file": "./citadel_sdk/src/macros.rs", - "context": [ - "Provides procedural macros for SDK trait implementations", - "Reduces boilerplate in network communication code", - "Supports async/await patterns in trait implementations", - "Ensures consistent Remote trait implementations", - "Integrates with account management and request handling", - "Used internally by the SDK for code generation", - "Requires async-trait feature for async implementations" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/client/mod.rs", - "context": [ - "Root module for client-side networking components", - "Provides connection builders and configuration", - "Supports multiple authentication methods", - "Manages UDP and NAT traversal settings", - "Implements session security and PSK handling", - "Organizes broadcast, peer, and server connections", - "Defines base traits for prefab implementations" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/client/single_connection.rs", - "context": [ - "Implements single client-to-server connection management", - "Supports multiple authentication modes", - "Provides NAT traversal with UDP support", - "Handles secure session management", - "Supports object transfer handling", - "Manages connection lifecycle" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/client/peer_connection.rs", - "context": [ - "Manages peer-to-peer connections", - "Supports multiple simultaneous peers", - "Implements file transfer capabilities", - "Handles NAT traversal settings", - "Provides session security", - "Manages peer identification" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/client/broadcast.rs", - "context": [ - "Implements group-based communication", - "Uses owner-based trust model", - "Supports public and private groups", - "Handles member registration", - "Manages group invitations", - "Enables concurrent participation" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/mod.rs", - "context": [ - "Root module for pre-built network components", - "Organizes client and server implementations", - "Provides remote connection management", - "Handles file transfer functionality", - "Manages signal and event processing", - "Implements connection security", - "Supports peer discovery and listing" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs", - "context": [ - "Provides automatic handling of file transfers on the server side", - "Implements automatic acceptance and silent processing", - "Uses minimal resources with no configuration needed", - "Integrates with NetKernel for event handling", - "Handles error conditions gracefully", - "Supports both basic and RE-VFS transfers" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/server/client_connect_listener.rs", - "context": [ - "Executes custom logic on client connection events", - "Implements async event processing and security management", - "Supports both TCP and UDP channels", - "Handles session security settings", - "Manages connection success events", - "Provides type-safe callback execution" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/server/empty.rs", - "context": [ - "Provides minimal no-op network kernel implementation", - "Implements zero overhead event acceptance", - "Suitable for basic connection acceptance", - "Uses minimal system resources", - "Provides clean shutdown handling", - "Not designed for interactive servers" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/server/internal_service.rs", - "context": [ - "Enables integration of internal services like HTTP servers", - "Supports HTTP/1.1 and HTTP/2 protocols", - "Implements bidirectional communication", - "Provides service lifecycle management", - "Handles automatic resource cleanup", - "Supports custom service handlers" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/server/mod.rs", - "context": [ - "Organizes server-side network components", - "Provides pre-built server implementations", - "Supports file transfer and client connections", - "Enables internal service integration", - "Implements event-driven architecture", - "Manages component lifecycle" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/shared/internal_service.rs", - "context": [ - "Provides core functionality for internal service integration", - "Implements bidirectional communication channels", - "Uses async I/O for efficient communication", - "Handles protocol conversion automatically", - "Manages resource cleanup and shutdown", - "Supports thread-safe message passing" - ] - }, - { - "file": "./citadel_sdk/src/prefabs/shared/mod.rs", - "context": [ - "Contains shared network components for client and server", - "Provides role-agnostic functionality", - "Implements thread-safe shared utilities", - "Uses async-first design patterns", - "Manages cross-role functionality", - "Organizes internal service integration" - ] - } + { + "file": "./citadel_sdk/src/responses.rs", + "context": [ + "Provides helper functions for protocol responses", + "Handles peer registration responses", + "Manages peer connection responses", + "Processes group invitation responses", + "Handles automatic ticket management", + "Manages connection type reversal", + "Performs username resolution and validation", + "Requires matching request tickets", + "Integrates with Remote interface", + "Supports PeerSignal and NodeResult handling" + ] + }, + { + "file": "./citadel_sdk/src/test_common.rs", + "context": [ + "Provides testing utilities for Citadel Protocol", + "Supports test server creation and configuration", + "Implements synchronization barriers for multi-peer tests", + "Includes UDP mode testing utilities", + "Provides P2P connection testing helpers", + "Manages local test peer coordination", + "Requires localhost-testing feature for most functionality", + "Integrates with NodeBuilder for server configuration", + "Supports EmptyKernel for basic testing", + "Includes connection validation utilities" + ] + }, + { + "file": "./citadel_sdk/src/remote_ext.rs", + "context": [ + "Extends NodeRemote with high-level protocol operations", + "Provides user registration and authentication", + "Manages connections and file transfers", + "Supports encrypted virtual filesystem", + "Handles peer discovery and group communication", + "Implements security settings configuration", + "Uses asynchronous operations throughout", + "Supports chunked file transfers for efficiency", + "Requires mutual registration for peer connections", + "Integrates with client-server and P2P modes" + ] + }, + { + "file": "./citadel_proto/src/inner_arg.rs", + "context": [ + "Provides type-safe parameter reference handling", + "Implements wrapper types for mutable and immutable references", + "Uses zero-cost abstractions for performance", + "Enforces proper dereferencing behavior", + "Preserves mutability constraints", + "Integrates with packet processing and validation", + "Supports cryptographic operation safety" + ] + }, + { + "file": "./citadel_proto/src/functional.rs", + "context": [ + "Provides functional programming utilities and extensions", + "Implements monadic-style operations and conditional chaining", + "Supports method chaining with Then trait", + "Provides conditional branching with IfEq and IfTrue", + "Implements tuple mapping with PairMap", + "Uses zero-cost abstractions and lazy evaluation", + "Enhances code readability throughout the codebase" + ] + }, + { + "file": "./citadel_proto/src/constants.rs", + "context": [ + "Defines core protocol constants and configuration", + "Manages protocol version using semantic versioning", + "Specifies network parameters and MTU sizes", + "Controls timing intervals and timeouts", + "Sets buffer sizes and group limitations", + "Configures security level update frequencies", + "Defines port ranges and networking defaults" + ] + }, + { + "file": "./citadel_proto/src/auth.rs", + "context": [ + "Defines authentication request types for Citadel Protocol", + "Supports both credential-based and passwordless authentication", + "Uses SecBuffer for secure credential handling", + "Manages user identification through CID and usernames", + "Handles server connection information for authentication", + "Implements transient device-based connections" + ] + }, + { + "file": "./citadel_proto/src/proto/validation.rs", + "context": [ + "Core packet validation module for the Citadel Protocol", + "Implements security-critical validation for all packet types", + "Handles connection, registration, group, and file transfer validation", + "Uses AEAD cryptography for packet integrity verification", + "Implements zero-copy validation where possible", + "Contains submodules for different validation contexts: do_connect, group, do_register, do_entropy_bank_update, pre_connect, file, and aead", + "Maintains protocol state consistency across all validation steps" + ] + }, + { + "file": "./citadel_proto/src/proto/session.rs", + "context": [ + "Core session management implementation for Citadel Protocol", + "Handles active connections between peers with state management", + "Implements secure file transfer with configurable security levels", + "Supports UDP connectivity for performance-critical operations", + "Provides clean shutdown and resource cleanup mechanisms", + "Handles connection interruptions and session resumption", + "Uses post-quantum cryptographic primitives for session security", + "Manages packet processing and stream handling", + "Implements session initialization and parameter configuration", + "Supports both TCP and UDP transport protocols", + "Handles authentication and credential management", + "Provides virtual connection management for different transfer types" + ] + }, + { + "file": "./citadel_proto/src/proto/session_queue_handler.rs", + "context": [ + "Implements queue-based task scheduling for protocol sessions", + "Uses DelayQueue for managing timed operations", + "Handles both reserved system tasks (indices 0-9) and ordinary tasks (10+)", + "Supports one-shot tasks for single-execution operations", + "Integrates with session state management", + "Implements Stream and Future traits for async operation", + "Provides thread-safe task scheduling through atomic operations", + "Manages session shutdown and cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet.rs", + "context": [ + "Implements the core Hypernode Data Protocol (HDP) packet structure", + "Defines packet headers, commands, and buffer handling", + "Provides zero-copy header parsing for efficiency", + "Manages packet composition and decomposition", + "Supports both BytesMut and Vec buffer types", + "Handles socket address tracking and packet routing", + "Implements hierarchical command organization" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/mod.rs", + "context": [ + "Core packet processing infrastructure for the Citadel Protocol", + "Key aspects:", + "- Manages multiple packet types (connection, auth, data)", + "- Implements processing pipeline with validation", + "- Handles packet security and integrity", + "- Provides backpressure and ordering guarantees", + "- Coordinates between different packet processors" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/connect_packet.rs", + "context": [ + "Handles connection establishment in Citadel Protocol", + "Key aspects:", + "- Implements secure multi-stage handshake", + "- Manages post-quantum key exchange", + "- Handles version and capability negotiation", + "- Provides connection state management", + "- Supports transport-agnostic connections" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/disconnect_packet.rs", + "context": [ + "Implements graceful disconnection protocol", + "Uses two-stage disconnect handshake", + "Manages secure packet validation", + "Handles session state transitions", + "Coordinates with kernel for disconnect signals", + "Implements packet delivery delay for reliability", + "Tracks disconnection tickets" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/file_packet.rs", + "context": [ + "Handles secure file transfer operations", + "Manages file transfer lifecycle", + "Supports chunked transfers", + "Integrates with virtual filesystem", + "Implements transfer state tracking", + "Handles both direct and proxied transfers" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/keep_alive_packet.rs", + "context": [ + "Implements connection maintenance through keep-alive packets", + "Key aspects:", + "- Manages periodic heartbeat packets", + "- Detects connection liveness", + "- Monitors connection quality", + "- Handles connection timeouts", + "- Triggers automatic reconnection" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/deregister_packet.rs", + "context": [ + "Handles client deregistration process", + "Manages secure account removal", + "Implements resource cleanup", + "Validates session state", + "Provides ticket-based tracking", + "Handles success/failure states", + "Manages client and server cleanup", + "Maintains security during removal", + "Integrates with account management", + "Reports deregistration results" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/raw_primary_packet.rs", + "context": [ + "Low-level packet processing for primary data packets", + "Key aspects:", + "- Handles raw packet operations", + "- Manages packet headers and payloads", + "- Implements zero-copy processing", + "- Ensures packet integrity", + "- Validates packet structure" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/hole_punch.rs", + "context": [ + "Implements NAT traversal through hole punching", + "Enables direct P2P connections behind NATs", + "Handles NAT traversal packet processing", + "Provides secure packet validation", + "Manages peer connection coordination", + "Implements connection pipe management", + "Supports proxied connections", + "Manages hole puncher pipes", + "Integrates with proxy system", + "Ensures authenticated packet handling" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/register_packet.rs", + "context": [ + "Handles client registration in Citadel Protocol", + "Implements secure multi-stage handshake", + "Provides passwordless registration support", + "Manages session state transitions", + "Implements cryptographic parameter negotiation", + "Handles registration failure cases", + "Supports post-quantum cryptography", + "Validates registration parameters", + "Integrates with account management", + "Manages initial cryptographic setup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/rekey_packet.rs", + "context": [ + "Implements secure key rotation mechanism", + "Manages cryptographic key updates", + "Provides multi-stage key exchange", + "Implements post-quantum cryptography", + "Ensures perfect forward secrecy", + "Handles state synchronization", + "Supports proxy connections", + "Manages ratchet state updates", + "Handles key truncation operations", + "Integrates with security level system" + ] + }, + { + "file": "./citadel_proto/src/proto/session_manager.rs", + "context": [ + "Core session management system for Citadel Protocol", + "Handles stateful connections between peers", + "Manages both provisional and established connections", + "Implements secure peer-to-peer communication", + "Provides group broadcast functionality", + "Supports configurable security levels", + "Handles virtual connection management", + "Implements clean shutdown procedures", + "Manages connection upgrades and transitions", + "Integrates with HyperNodePeerLayer for P2P operations" + ] + }, + { + "file": "./citadel_user/src/lib.rs", + "context": [ + "Core user management system", + "Manages HyperVPN architecture", + "Provides hierarchical account structure", + "Handles authentication and security", + "Supports multiple backend storage", + "Integrates external services", + "Manages account lifecycle", + "Implements zero-trust architecture", + "Supports post-quantum cryptography", + "Provides feature-gated functionality" + ] + }, + { + "file": "./citadel_user/src/external_services/service_interface.rs", + "context": [ + "Defines external service interface", + "Provides unified service communication", + "Supports async data transmission", + "Handles raw packet data", + "Enables peer-to-peer communication", + "Standardizes error handling", + "Ensures thread safety", + "Manages service connections", + "Implements common service trait", + "Supports multiple service types" + ] + }, + { + "file": "./citadel_user/src/external_services/rtdb.rs", + "context": [ + "Provides Firebase RTDB integration", + "Manages real-time data synchronization", + "Handles authentication and tokens", + "Manages connection lifecycle", + "Implements data transfer interface", + "Supports connection refresh", + "Handles token expiration", + "Provides client configuration", + "Manages RTDB instances", + "Implements efficient data sync" + ] + }, + { + "file": "./citadel_user/src/external_services/mod.rs", + "context": [ + "Manages external service integration", + "Provides Google services support", + "Handles service configuration", + "Manages service authentication", + "Supports Firebase RTDB", + "Handles JWT management", + "Provides WASM compatibility", + "Manages service state", + "Handles post-login services", + "Supports feature flags" + ] + }, + { + "file": "./citadel_user/src/auth/proposed_credentials.rs", + "context": [ + "Manages credential proposals", + "Handles password hashing", + "Validates credentials", + "Supports passwordless auth", + "Implements username sanitization", + "Provides secure password storage", + "Manages authentication modes", + "Handles server validation", + "Implements credential comparison", + "Ensures memory safety" + ] + }, + { + "file": "./citadel_user/src/backend/sql_backend.rs", + "context": [ + "Provides SQL database storage", + "Supports multiple SQL variants", + "Implements connection pooling", + "Handles SQL syntax differences", + "Manages schema creation", + "Supports blob and text storage", + "Implements atomic operations", + "Manages peer relationships", + "Provides efficient querying", + "Enables ACID compliance" + ] + }, + { + "file": "./citadel_user/src/backend/redis_backend.rs", + "context": [ + "Provides distributed Redis storage", + "Implements connection pooling", + "Supports Redis clustering", + "Manages connection health checks", + "Handles connection timeouts", + "Provides automatic reconnection", + "Supports atomic operations", + "Manages peer relationships", + "Implements byte map storage", + "Enables high availability" + ] + }, + { + "file": "./citadel_user/src/backend/filesystem_backend.rs", + "context": [ + "Provides filesystem-based persistent storage", + "Implements hybrid storage with memory cache", + "Manages client network account persistence", + "Handles directory structure organization", + "Supports peer relationship storage", + "Implements virtual filesystem operations", + "Provides byte map storage functionality", + "Ensures atomic file operations", + "Supports personal and impersonal accounts", + "Integrates with memory backend for caching" + ] + }, + { + "file": "./citadel_user/src/backend/memory.rs", + "context": [ + "Provides in-memory backend storage", + "Implements thread-safe client storage", + "Manages peer relationships", + "Handles metadata operations", + "Supports byte map storage", + "Implements atomic operations", + "Manages resource cleanup", + "Supports WASM environments", + "Provides client registration", + "Maintains data consistency" + ] + }, + { + "file": "./citadel_user/src/backend/utils/mod.rs", + "context": [ + "Manages object transfer operations", + "Implements bidirectional transfers", + "Provides progress tracking", + "Handles transfer status updates", + "Implements stream-based transfers", + "Manages transfer control flow", + "Provides async transfer support", + "Handles resource cleanup", + "Implements transfer acceptance", + "Manages transfer orientation" + ] + }, + { + "file": "./citadel_user/src/backend/mod.rs", + "context": [ + "Defines core backend storage infrastructure", + "Manages multiple storage backend types", + "Implements unified backend interface", + "Provides async persistence operations", + "Handles database connections", + "Manages virtual filesystem operations", + "Implements thread-safe storage", + "Provides transaction management", + "Supports multiple database types", + "Handles automatic reconnection" + ] + }, + { + "file": "./citadel_user/src/auth/mod.rs", + "context": [ + "Manages authentication modes for CNACs", + "Implements Argon2id password hashing", + "Supports passwordless authentication", + "Handles username uniqueness", + "Manages full name storage", + "Provides authentication state checks", + "Implements serializable auth data", + "Handles credential validation", + "Manages authentication modes", + "Provides secure data storage" + ] + }, + { + "file": "./citadel_user/src/account_loader.rs", + "context": [ + "Manages loading of serialized client network accounts", + "Handles both personal and impersonal account types", + "Provides generic file loading capabilities", + "Implements efficient buffered I/O operations", + "Supports extensible deserialization", + "Manages filesystem-based account storage", + "Handles account loading errors gracefully", + "Integrates with directory management", + "Supports cryptographic account security", + "Provides account persistence functionality" + ] + }, + { + "file": "./citadel_user/src/server_misc_settings.rs", + "context": [ + "Manages server-side configuration settings", + "Controls passwordless authentication", + "Enforces credential requirements", + "Provides default security settings", + "Integrates with account management", + "Supports customizable authentication flows", + "Handles server-side security policies", + "Configures node authentication behavior", + "Manages credential validation rules", + "Controls server security features" + ] + }, + { + "file": "./citadel_user/src/serialization.rs", + "context": [ + "Provides binary serialization functionality", + "Implements efficient buffer operations", + "Supports in-place deserialization", + "Handles size estimation and pre-allocation", + "Uses bincode for binary encoding", + "Integrates with bytes buffer system", + "Provides trait-based serialization", + "Manages memory-efficient operations", + "Supports slice-based serialization", + "Implements error handling with AccountError" + ] + }, + { + "file": "./citadel_user/src/connection_metadata.rs", + "context": [ + "Manages connection metadata for client connections", + "Handles connection protocol specifications", + "Supports TCP, TLS, and QUIC protocols", + "Stores socket address information", + "Provides domain name handling", + "Implements serialization support", + "Manages connection state persistence", + "Supports connection info display", + "Handles protocol-specific settings", + "Maintains connection type information" + ] + }, + { + "file": "./citadel_user/src/directory_store.rs", + "context": [ + "Manages filesystem structure for Citadel Protocol", + "Handles directory creation and organization", + "Provides cross-platform path management", + "Organizes account storage directories", + "Manages configuration file locations", + "Supports virtual filesystem structure", + "Handles file transfer storage", + "Implements path formatting and generation", + "Maintains directory hierarchy", + "Ensures consistent file organization" + ] + }, + { + "file": "./citadel_user/src/credentials.rs", + "context": [ + "Manages credential validation and requirements", + "Defines username and password constraints", + "Enforces length and format restrictions", + "Provides configurable validation rules", + "Handles full name format validation", + "Implements default security policies", + "Supports custom requirement definitions", + "Ensures consistent credential formats", + "Validates credential formatting", + "Defines system-wide credential limits" + ] + }, + { + "file": "./citadel_user/src/hypernode_account.rs", + "context": [ + "Core HyperNode account functionality", + "Provides user identification extensions", + "Handles account search operations", + "Manages peer relationship lookups", + "Supports CID and username-based identification", + "Implements async account operations", + "Defines serialization file extension", + "Enables flexible account type extensions", + "Maintains bi-directional peer relationships", + "Integrates with persistence backends" + ] + }, + { + "file": "./citadel_user/src/account_manager.rs", + "context": [ + "Central user account management system", + "Handles account creation and registration", + "Supports multiple storage backends (Memory, File, SQL, Redis)", + "Manages HyperLAN peer relationships", + "Provides secure credential storage", + "Implements Argon2id password hashing", + "Supports personal and impersonal modes", + "Handles P2P connection registration", + "Provides thread-safe async operations", + "Manages external service integrations" + ] + }, + { + "file": "./citadel_user/src/client_account.rs", + "context": [ + "Manages individual client connections in Citadel Protocol", + "Handles both personal and impersonal connection modes", + "Implements secure credential management and validation", + "Provides ratchet-based cryptographic state management", + "Manages HyperLAN and HyperWAN peer relationships", + "Supports thread-safe operations through RwLock", + "Handles connection endpoint configuration", + "Implements peer list synchronization", + "Provides P2P connection support", + "Maintains immutable critical security fields" + ] + }, + { + "file": "./citadel_crypt/src/argon/autotuner.rs", + "context": [ + "Automatic parameter tuning for Argon2 password hashing", + "Implements ORY's guidelines for parameter selection", + "Uses memory-first tuning strategy for optimal security", + "Dynamically adjusts based on system capabilities", + "Supports multi-threading with CPU core detection", + "Provides configurable minimum execution time", + "Includes safeguards for memory usage", + "Implements iterative parameter optimization", + "Supports custom hash lengths and secret keys", + "Designed for release-mode performance tuning" + ] + }, + { + "file": "./citadel_crypt/src/lib.rs", + "context": [ + "Core cryptographic framework for the Citadel Protocol", + "Implements post-quantum cryptography and perfect forward secrecy", + "Provides secure memory management with zero-copy operations", + "Features entropy banking system for key derivation", + "Includes packet vectorization and port scrambling", + "Implements FCM (Forward Chain Messaging) primitives", + "Integrates Argon2 with auto-tuning capabilities", + "All operations are thread-safe and memory-efficient", + "Uses defense-in-depth with multiple security layers", + "Automatic memory zeroing for sensitive data" + ] + }, + { + "file": "./citadel_wire/src/lib.rs", + "context": [ + "Main entry point for Citadel Wire crate", + "Provides secure peer-to-peer connection capabilities", + "Implements NAT traversal and UDP hole punching", + "Enforces zero unsafe code policy", + "Uses async-first design with security focus" + ] + }, + { + "file": "./async_ip/src/lib.rs", + "context": [ + "Utility crate for asynchronous IP address resolution", + "Provides both IPv4 and IPv6 resolution with fallback mechanisms", + "Supports internal and external IP detection", + "Uses multiple services concurrently for reliability", + "Has special handling for WebAssembly environments", + "Key component for NAT traversal and network identification", + "Uses HTTP-based IP resolution services with configurable endpoints", + "Implements custom error handling for network and parsing failures" + ] + }, + { + "file": "./firebase-rtdb/src/lib.rs", + "context": [ + "Lightweight async Rust client for Firebase Realtime Database", + "Provides JWT-based authentication with automatic token renewal", + "Implements all CRUD operations with JSON serialization", + "Uses hierarchical node-based database access", + "Handles connection timeouts and TLS security", + "Provides error handling for network and database operations", + "Supports connection pooling and TCP nodelay for performance", + "Implements Firebase Security Rules compatibility", + "Uses reqwest for HTTP communication with async support", + "Manages authentication state and token expiration" + ] + }, + { + "file": "./firebase-rtdb/Cargo.toml", + "context": [ + "Package configuration for Firebase RTDB client", + "Uses workspace-level dependency management", + "Configures reqwest with rustls-tls for secure communication", + "Includes serde for JSON serialization support", + "Defines crate metadata and documentation links", + "Specifies test dependencies for integration testing" + ] + }, + { + "file": "./firebase-rtdb/tests/primary.rs", + "context": [ + "Integration test file for Firebase RTDB client", + "Currently empty, prepared for future test implementations" + ] + }, + { + "file": "./netbeam/src/lib.rs", + "context": [ + "Core library for high-performance networking with multiplexing support", + "Provides reliable ordered message delivery guarantees", + "Implements network-aware synchronization primitives", + "Uses zero unsafe code and leverages Rust's type system", + "Requires Tokio runtime for async operations", + "Supports both client and server modes with bi-directional communication", + "Includes time tracking utilities for network operations", + "Features proper error handling and type-safe APIs" + ] + }, + { + "file": "./netbeam/src/reliable_conn.rs", + "context": [ + "Core traits for reliable network connections", + "Implements ordered message delivery guarantees", + "Supports both direct and NAT-traversed connections", + "Provides connection addressing abstraction", + "Includes serialization support for messages", + "Implements network simulation for testing" + ] + }, + { + "file": "./netbeam/src/multiplex.rs", + "context": [ + "Network stream multiplexing implementation", + "Enables multiple logical connections over single physical connection", + "Provides bi-directional communication channels", + "Implements automatic stream ID generation and management", + "Ensures thread-safe subscription handling", + "Supports custom connection key types", + "Maintains message ordering within streams", + "Handles graceful stream initialization and cleanup", + "Uses pre-action and post-action hooks for lifecycle management", + "Provides both borrowed and owned subscription types" + ] + }, + { + "file": "./netbeam/src/time_tracker.rs", + "context": [ + "Provides precise timing utilities for network operations", + "Uses monotonic system time for consistency", + "Implements nanosecond precision timing", + "Handles time overflow protection", + "Used for latency measurements and timing-sensitive operations" + ] + }, + { + "file": "./citadel_types/src/lib.rs", + "context": [ + "Core type definitions for the Citadel Protocol", + "Provides fundamental data structures and utilities", + "Includes cryptographic types and parameters", + "Defines protocol-specific message types", + "Contains user-related data structures", + "Implements error handling and validation", + "Exports commonly used types through prelude" + ] + }, + { + "file": "./citadel_types/src/crypto/mod.rs", + "context": [ + "Defines cryptographic types and parameters", + "Implements secure memory management with SecBuffer", + "Provides algorithm selection and configuration", + "Supports post-quantum cryptography algorithms", + "Includes security level specifications", + "Implements secure memory locking and zeroing", + "Provides serialization for cryptographic types", + "Supports various secrecy modes for different use cases" + ] + }, + { + "file": "./citadel_logging/src/lib.rs", + "context": [ + "Structured logging facade for the Citadel Protocol", + "Built on top of the tracing ecosystem", + "Provides consistent logging setup across components", + "Supports file and line number information", + "Implements environment-based log level filtering", + "Includes panic handling with logging", + "Supports async-aware instrumentation", + "Uses span-based structured logging", + "Provides multiple log levels with target-based filtering" + ] + }, + { + "file": "./citadel_io/src/lib.rs", + "context": [ + "Cross-platform I/O utility crate for native and WebAssembly targets", + "Provides consistent interfaces for synchronization primitives", + "Implements platform-specific random number generation", + "Abstracts async runtime differences between native and WASM", + "Re-exports Tokio ecosystem with platform-specific implementations", + "Supports deadlock detection on native platforms" + ] + }, + { + "file": "./citadel_io/src/standard/locks.rs", + "context": [ + "Native platform synchronization primitives using parking_lot", + "High-performance mutex and read-write lock implementations", + "RAII-style lock guards for automatic resource management", + "More efficient than standard library synchronization types" + ] + }, + { + "file": "./citadel_io/src/wasm/locks.rs", + "context": [ + "WebAssembly-compatible synchronization primitives", + "Wraps standard library locks for WASM compatibility", + "Maintains API compatibility with native code", + "Single-threaded implementation for current WASM limitations" + ] + }, + { + "file": "./citadel_io/src/wasm/rng.rs", + "context": [ + "WebAssembly-compatible random number generation", + "Uses Web Crypto API through getrandom crate", + "Provides cryptographically secure random numbers", + "Implements RngCore and CryptoRng traits", + "Supports both fixed-size and dynamic buffer generation" + ] + }, + { + "file": "./citadel_pqcrypto/src/lib.rs", + "context": [ + "# Documentation Progress", + "## citadel_pqcrypto Crate", + "### Completed Documentation", + "- `lib.rs`: Crate-level documentation with overview, features, and examples", + "- `constructor_opts.rs`: Documentation for `ConstructorOpts` struct", + "- Enhanced `RecursiveChain` documentation with examples and security considerations", + "- `encryption.rs`: Module-level documentation", + "- `AeadModule` trait documentation", + "- `AesModule` implementation docs", + "- `ChaChaModule` implementation docs", + "- `AsconModule` implementation docs", + "- `KyberModule` implementation docs with quantum security considerations", + "- `export.rs`: Enhanced module-level documentation", + "- Key store serialization documentation", + "- Security considerations for key material", + "- Examples for serialization/deserialization", + "- `bytes_in_place.rs`: Comprehensive module documentation", + "- `InPlaceBuffer` and `EzBuffer` documentation", + "- Examples for buffer operations", + "- `wire.rs`: Module-level documentation", + "- Parameter transfer documentation", + "- Scrambling dictionary documentation", + "- `replay_attack_container.rs`: Module-level documentation", + "- Anti-replay attack mechanism documentation", + "- Examples and security considerations", + "### Security Considerations Documented", + "- Post-quantum cryptography principles", + "- Proper nonce handling", + "- Zeroization of sensitive data", + "- Constant-time operations", + "- Forward secrecy", + "- Local-user encryption for endpoint privacy", + "- Anti-replay attack protections", + "- Key material serialization safety", + "- Buffer operation safety", + "- Parameter transfer security", + "### Next Steps", + "1. Review remaining files in citadel_pqcrypto for any documentation gaps", + "2. Cross-reference documentation between related components", + "3. Ensure all security considerations are thoroughly documented", + "4. Add more real-world usage examples", + "### Code Style and Standards", + "- All documentation follows Rust documentation best practices", + "- Examples are provided for key functionality", + "- Security considerations are clearly outlined", + "- Cross-references between related components are maintained" + ] + }, + { + "file": "./citadel_pqcrypto/src/constructor_opts.rs", + "context": [ + "Provides configuration options for post-quantum cryptography (PQC) instances", + "Includes ConstructorOpts for PQC initialization and RecursiveChain for key derivation", + "Focuses on secure parameter handling and memory safety", + "Supports both initial and chained cryptographic operations" + ] + }, + { + "file": "./citadel_pqcrypto/src/bytes_in_place.rs", + "context": [ + "Implements memory-efficient and secure in-place buffer operations", + "Features window-based buffer access control for safe data manipulation", + "Supports both Vec and BytesMut buffer types", + "Emphasizes zero-copy operations and memory safety" + ] + }, + { + "file": "./citadel_pqcrypto/src/wire.rs", + "context": [ + "Implements secure wire protocol for PQC parameter transfer", + "Provides parameter transfer structures for Alice-Bob key exchange", + "Features data scrambling for additional security", + "Supports both symmetric and asymmetric encryption modes", + "Ensures memory safety and automatic parameter cleanup" + ] + }, + { + "file": "./citadel_pqcrypto/src/replay_attack_container.rs", + "context": [ + "Implements protection against replay attacks in communications", + "Uses circular buffer for efficient PID history tracking", + "Supports out-of-order packet delivery within configurable window", + "Provides thread-safe PID generation and validation", + "Features automatic state reset on re-keying" + ] + }, + { + "file": "./citadel_wire/src/hypernode_type.rs", + "context": [ + "This module defines network node types and their behaviors in the Citadel Protocol. It handles configuration for server nodes with static IPs and peer nodes in residential NAT environments, including automatic UPnP handling and NAT traversal fallback mechanisms" + ] + }, + { + "file": "./citadel_wire/src/error.rs", + "context": [ + "This module implements error types specific to network traversal and firewall operations", + "It provides custom error types for UPnP and hole punching operations, with conversion traits to standard IO errors", + "The error types help categorize and handle various network-related failure scenarios, including port mapping failures and exhausted hole punching attempts" + ] + }, + { + "file": "./citadel_wire/src/standard/nat_identification.rs", + "context": [ + "Provides NAT type identification and analysis", + "Uses STUN servers for NAT behavior detection", + "Analyzes port and IP translation patterns", + "Supports IPv4 and IPv6 compatibility checks", + "Determines optimal NAT traversal strategies" + ] + }, + { + "file": "./citadel_wire/src/standard/socket_helpers.rs", + "context": [ + "Provides socket creation and configuration utilities", + "Handles TCP and UDP socket setup", + "Implements platform-specific socket options", + "Supports IPv4 and IPv6 with automatic mapping", + "Manages socket reuse for NAT traversal" + ] + }, + { + "file": "./citadel_wire/src/standard/upnp_handler.rs", + "context": [ + "Provides UPnP port mapping and gateway management", + "Handles automatic gateway discovery and configuration", + "Manages port forwarding for TCP and UDP protocols", + "Supports configurable lease durations and targeted forwarding", + "Implements external IP and local IP detection" + ] + }, + { + "file": "./citadel_wire/src/standard/quic.rs", + "context": [ + "Implements QUIC protocol for secure connections", + "Supports client and server endpoints", + "Handles self-signed and PKCS#12 certificates", + "Provides NAT traversal-friendly transport", + "Uses Tokio for async/await support" + ] + }, + { + "file": "./citadel_wire/src/standard/tls.rs", + "context": [ + "Provides TLS configuration and certificate management", + "Supports both TLS and QUIC protocols", + "Handles self-signed and PKCS#12 certificates", + "Implements native system certificate loading", + "Uses Rustls for secure TLS implementation" + ] + }, + { + "file": "./citadel_wire/src/standard/misc.rs", + "context": [ + "Provides certificate format conversion utilities", + "Handles PKCS#12 to QUIC format conversion", + "Manages certificate chain extraction", + "Implements private key conversion", + "Ensures memory-safe certificate handling" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/hole_punch_config.rs", + "context": [ + "Provides configuration for UDP hole punching NAT traversal", + "Handles address prediction and socket preparation", + "Configures port ranges based on NAT behavior", + "Supports both IPv4 and IPv6 traversal", + "Implements iterator-based address generation" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/udp_hole_puncher.rs", + "context": [ + "Implements core UDP hole punching algorithm for NAT traversal", + "Handles asynchronous connection establishment between peers", + "Supports dual-stack IPv4/IPv6 with automatic retry", + "Manages encrypted configuration exchange", + "Provides NAT-aware socket binding optimization" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs", + "context": [ + "Manages NAT-aware UDP socket addressing", + "Handles separate send/receive addresses for NAT traversal", + "Provides packet validation and connection state tracking", + "Implements socket cleanup after hole punching", + "Supports UPnP address translation detection" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/mod.rs", + "context": [ + "Core UDP NAT traversal framework module", + "Coordinates multiple traversal methods and strategies", + "Manages UPnP and hole punching integration", + "Provides unique connection identification", + "Implements method prioritization and fallback" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs", + "context": [ + "Manages secure packet encryption for hole punching configuration", + "Handles custom STUN server configurations", + "Provides zero-copy packet handling optimizations", + "Supports localhost testing mode with disabled encryption", + "Implements thread-safe encryption function containers" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/method3.rs", + "context": [ + "Implements advanced UDP hole punching with variable TTL values", + "Handles asymmetric NAT traversal failures with recovery mode", + "Provides encrypted configuration exchange between peers", + "Supports dual-stack IPv4/IPv6 operation", + "Implements thread-safe UDP socket operations" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/linear/mod.rs", + "context": [ + "Implements linear UDP hole punching for client-server scenarios", + "Coordinates multiple traversal methods with fallback strategy", + "Manages synchronized connection establishment timing", + "Provides recovery mode for asymmetric failures", + "Integrates UPnP support with automatic fallback" + ] + }, + { + "file": "./citadel_wire/src/udp_traversal/multi/mod.rs", + "context": [ + "Implements concurrent dual-stack UDP hole punching", + "Manages multiple traversal attempts across ports", + "Coordinates winner selection between attempts", + "Handles IPv4/IPv6 protocol selection", + "Provides multiplexed connection management" + ] + }, + { + "file": "./citadel_wire/src/standard/mod.rs", + "context": [ + "Provides core networking components for Citadel Protocol", + "Implements QUIC, TLS, and NAT traversal", + "Handles UPnP and socket configuration", + "Supports peer-to-peer networking", + "Uses async-first design philosophy" + ] + }, + { + "file": "./citadel_crypt/src/entropy_bank.rs", + "context": [ + "Implements dynamic entropy management", + "Provides secure nonce generation", + "Handles packet encryption/decryption", + "Prevents replay attacks", + "Manages transient counters", + "Integrates post-quantum operations" + ] + }, + { + "file": "./citadel_crypt/src/endpoint_crypto_container.rs", + "context": [ + "Manages peer session cryptographic state", + "Implements thread-safe ratchet updates", + "Handles version control and conflicts", + "Manages session key rotation", + "Supports atomic state transitions", + "Integrates with post-quantum cryptography" + ] + }, + { + "file": "./citadel_crypt/src/misc.rs", + "context": [ + "Provides cryptographic utility functions", + "Implements flexible error handling", + "Generates random port mappings", + "Supports security level validation", + "Implements error type conversions", + "Provides debug and display utilities" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs", + "context": [ + "Implements secure partitioned buffer management", + "Provides fixed-size memory partitioning", + "Enforces strict partition boundaries", + "Supports zero-copy partition access", + "Implements automatic buffer zeroing", + "Provides thread-safe partition operations" + ] + }, + { + "file": "./citadel_crypt/src/packet_vector.rs", + "context": [ + "Implements secure packet sequencing", + "Provides wave-based packet organization", + "Manages scrambled port assignments", + "Implements sequence hiding", + "Supports automatic memory zeroing", + "Ensures ordered packet delivery" + ] + }, + { + "file": "./citadel_crypt/src/sync_toggle.rs", + "context": [ + "Provides thread-safe toggle state management", + "Implements atomic state transitions", + "Supports one-way toggle operations", + "Enables state change detection", + "Uses sequential consistency ordering" + ] + }, + { + "file": "./citadel_crypt/src/stacked_ratchet.rs", + "context": [ + "Implements perfect forward secrecy", + "Manages independent key evolution", + "Provides post-quantum support", + "Handles message protection", + "Implements anti-replay protection", + "Ensures ordered packet delivery" + ] + }, + { + "file": "./citadel_crypt/src/scramble/crypt_splitter.rs", + "context": [ + "Implements secure packet splitting and scrambling", + "Provides wave-based packet transmission", + "Supports dynamic packet sizing based on security level", + "Implements packet reconstruction with timeout handling", + "Manages encrypted and unencrypted packet transmission", + "Integrates with post-quantum cryptography" + ] + }, + { + "file": "./citadel_crypt/src/streaming_crypt_scrambler.rs", + "context": [ + "Implements asynchronous streaming encryption for large data sources", + "Supports both file-based and in-memory data sources", + "Provides backpressure support through async/await", + "Manages efficient group-based encryption and transmission", + "Supports custom header inscription for packets", + "Implements progress tracking and cancellation" + ] + }, + { + "file": "./citadel_crypt/src/toolset.rs", + "context": [ + "Manages cryptographic ratchet versioning and synchronization", + "Implements memory-bounded storage for active ratchets", + "Provides static auxiliary ratchet for persistent encryption", + "Handles secure ratchet updates and deregistration", + "Ensures thread-safe access to cryptographic primitives", + "Manages rolling window of active encryption keys" + ] + }, + { + "file": "./citadel_crypt/src/fcm/keys.rs", + "context": [ + "Manages Firebase Cloud Messaging (FCM) credentials", + "Provides thread-safe access to API keys and client IDs", + "Implements efficient memory management through Arc", + "Supports serialization for credential persistence", + "Ensures secure handling of sensitive credential data", + "Enables type-safe FCM key construction and access" + ] + }, + { + "file": "./citadel_crypt/src/fcm/fcm_ratchet.rs", + "context": [ + "Implements size-optimized cryptographic ratchet for FCM", + "Provides post-quantum secure messaging within 4KB limit", + "Manages secure key evolution and perfect forward secrecy", + "Supports both synchronous and asynchronous operations", + "Implements message protection and validation", + "Integrates with Firebase Cloud Messaging service" + ] + }, + { + "file": "./citadel_crypt/src/argon/argon_container.rs", + "context": [ + "Implements asynchronous Argon2 password hashing", + "Provides client and server-side password handling", + "Supports configurable memory and time cost parameters", + "Implements secure memory management with SecBuffer", + "Supports associated data and secret keys", + "Uses Argon2id variant for optimal security" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/sec_packet.rs", + "context": [ + "Implements secure packet buffer handling", + "Provides three-part packet structure (header, payload, extension)", + "Enforces ordered write operations for packet construction", + "Implements zero-copy packet operations", + "Manages automatic memory cleanup", + "Supports fixed-size header optimization" + ] + }, + { + "file": "./citadel_crypt/src/secure_buffer/mod.rs", + "context": [ + "Provides secure buffer management module", + "Implements memory-safe buffer operations", + "Supports zero-copy data handling", + "Manages automatic memory zeroing", + "Includes packet writing utilities", + "Integrates with streaming operations" + ] + }, + { + "file": "./citadel_user/src/misc.rs", + "context": [ + "Provides core error handling for account operations", + "Manages CNAC metadata structures", + "Implements cross-platform path validation", + "Handles timestamp formatting", + "Provides virtual path management", + "Implements error type conversion", + "Manages account identification", + "Handles platform-specific path formatting", + "Provides directory validation", + "Implements metadata comparison" + ] + }, + { + "file": "./citadel_proto/src/proto/remote.rs", + "context": [ + "Implements remote communication functionality", + "Provides high-level interface for node communication", + "Supports asynchronous request/response patterns", + "Manages ticket-based request tracking", + "Integrates with account management system", + "Implements callback-based event handling", + "Ensures secure communication channels", + "Supports post-quantum cryptography", + "Handles network errors comprehensively", + "Provides bounded message sending" + ] + }, + { + "file": "./citadel_proto/src/proto/endpoint_crypto_accessor.rs", + "context": [ + "Manages cryptographic state access for P2P and C2S channels", + "Provides safe borrowing mechanisms for crypto operations", + "Implements version-aware state management", + "Ensures thread-safe container access", + "Integrates with post-quantum cryptography", + "Handles missing or invalid crypto states", + "Supports both peer-to-peer and client-server modes", + "Uses StackedRatchet for core crypto operations" + ] + }, + { + "file": "./citadel_proto/src/proto/session.rs", + "context": [ + "Core session management for Citadel Protocol", + "Handles active connections between peers", + "Manages authentication and key exchange", + "Implements secure file transfer functionality", + "Supports UDP connectivity for performance", + "Provides clean shutdown and resource cleanup", + "Uses post-quantum cryptography for security", + "Maintains perfect forward secrecy", + "Implements automatic key rotation", + "Handles session state transitions", + "Manages packet processing and connection state" + ] + }, + { + "file": "./citadel_proto/src/proto/node_request.rs", + "context": [ + "Defines node communication request types", + "Manages node registration and connections", + "Handles secure file transfer operations", + "Implements peer-to-peer command handling", + "Supports group broadcast functionality", + "Provides session security configuration", + "Implements key rotation operations", + "Uses ticket tracking for async responses", + "Supports SHA-256 password hashing", + "Configurable security levels for operations" + ] + }, + { + "file": "./citadel_proto/src/proto/state_container.rs", + "context": [ + "Core state management system for Citadel Protocol", + "Manages connection states and transitions", + "Handles virtual connections (P2P and C2S)", + "Implements secure group messaging", + "Manages file transfers with progress tracking", + "Supports UDP connectivity for performance", + "Implements cryptographic state verification", + "Protects against replay attacks", + "Provides secure group key management", + "Implements end-to-end file encryption", + "Handles connection timeouts and cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/mod.rs", + "context": [ + "Core packet processing infrastructure for the Citadel Protocol", + "Key aspects:", + "- Manages multiple packet types (connection, auth, data)", + "- Implements processing pipeline with validation", + "- Handles packet security and integrity", + "- Provides backpressure and ordering guarantees", + "- Coordinates between different packet processors" + ] + }, + { + "file": "./citadel_proto/src/proto/node.rs", + "context": [ + "Core networking implementation for Citadel Protocol", + "Supports multiple transport protocols (TCP, TLS, QUIC)", + "Implements NAT traversal for P2P connections", + "Manages concurrent network sessions", + "Uses post-quantum cryptography", + "Supports pre-shared key authentication", + "Handles both client-server and P2P modes", + "Provides protocol negotiation", + "Manages network socket listeners", + "Implements secure session establishment", + "Handles kernel communication", + "Supports TLS certificate management" + ] + }, + { + "file": "./citadel_proto/src/proto/node_result.rs", + "context": [ + "Defines result types for node operations", + "Handles registration, connection, and event results", + "Manages transfer and group operation outcomes", + "Provides comprehensive error reporting", + "Implements ticket tracking for async operations", + "Integrates with session and channel management" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/preconnect_packet.rs", + "context": [ + "Handles initial connection establishment", + "Implements NAT traversal and hole punching", + "Manages protocol version compatibility", + "Handles session state validation", + "Implements security level negotiation", + "Supports UDP and QUIC transports", + "Initializes cryptographic ratchets", + "Manages preconnect handshake flow", + "Validates session states", + "Processes connection requests", + "Handles NAT traversal configuration", + "Manages protocol version synchronization" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/udp_packet.rs", + "context": [ + "Handles UDP packet processing", + "Manages unordered channels", + "Validates packet security", + "Monitors channel state", + "Handles cleanup tasks", + "Processes secure payloads", + "Manages data transmission", + "Validates authentication", + "Handles session state", + "Manages buffer security", + "Processes channel drops", + "Handles error states" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/mod.rs", + "context": [ + "Manages peer communications", + "Handles group broadcasts", + "Processes peer commands", + "Manages server interactions", + "Handles signal processing", + "Manages disconnect signals", + "Tracks peer sessions", + "Handles operation results", + "Manages error states", + "Supports ticket operations", + "Processes peer state", + "Coordinates peer modules" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/signal_handler_interface.rs", + "context": [ + "Defines signal handling interface", + "Manages async signal operations", + "Handles outbound signals", + "Processes server signals", + "Manages target reception", + "Implements error handling", + "Uses async traits", + "Supports signal types", + "Manages signal flow", + "Requires type implementation", + "Handles network errors", + "Pending PeerSignal structification" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/server/mod.rs", + "context": [ + "Manages server-side peer operations", + "Handles post-connection states", + "Processes post-registration", + "Validates peer sessions", + "Tracks session states", + "Manages server operations", + "Handles connection states", + "Processes registration completion", + "Coordinates peer modules", + "Manages state tracking", + "Handles peer validation", + "Manages session completion" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs", + "context": [ + "Handles post-connection phase", + "Establishes virtual connections", + "Configures security settings", + "Manages UDP channels", + "Routes peer signals", + "Synchronizes session states", + "Manages connection tables", + "Handles TCP channels", + "Processes peer responses", + "Manages security levels", + "Tracks connection states", + "Needs disconnect cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/server/post_register.rs", + "context": [ + "Handles post-registration phase", + "Processes registration responses", + "Sets up HyperLAN P2P", + "Validates usernames", + "Routes signals", + "Manages tickets", + "Processes responses", + "Handles registration declines", + "Manages async registration", + "Needs error routing", + "Manages security levels", + "Tracks registration state" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs", + "context": [ + "Handles group messaging", + "Manages group membership", + "Processes group broadcasts", + "Controls group permissions", + "Tracks group state", + "Handles invitations", + "Manages group listing", + "Processes member states", + "Handles group creation", + "Manages group termination", + "Encrypts messages", + "Handles disconnections" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs", + "context": [ + "Processes peer commands", + "Routes peer signals", + "Handles key exchange", + "Manages group broadcasts", + "Tracks session states", + "Manages tickets", + "Handles error states", + "Mediates server operations", + "Validates authentication", + "Supports client/server roles", + "Manages peer sessions", + "Implements group operations" + ] + }, + { + "file": "./citadel_proto/src/proto/transfer_stats.rs", + "context": [ + "Handles network transfer statistics tracking with nanosecond precision", + "Core component for monitoring protocol performance metrics", + "Implements AddAssign for aggregating statistics over time", + "Tracks transfer rates, jitter, and total bytes transferred", + "Uses wrapping arithmetic to handle potential numeric overflows", + "Thread-safe design for concurrent statistics updates" + ] + }, + { + "file": "./citadel_proto/src/proto/packet_processor/channel.rs", + "context": [ + "Implements peer-to-peer communication channels", + "Provides both TCP and UDP channel implementations", + "Supports WebRTC compatibility through feature flag", + "Uses split architecture for async send/receive operations", + "Implements automatic resource cleanup on channel drop", + "Manages security levels for message encryption", + "Handles virtual connections between peers", + "Supports ordered and reliable message delivery", + "Integrates with the peer layer for connection management", + "Provides Stream trait implementation for async operations" + ] + }, + { + "file": "./citadel_proto/src/proto/peer_layer.rs", + "context": [ + "The peer layer module serves as the core peer-to-peer networking infrastructure in the Citadel Protocol. It manages peer connections, message groups, and signal routing between nodes. Key features include:", + "Peer signal management and routing between nodes", + "Message group functionality with concurrent/pending peer support", + "Ticket-based connection tracking and management", + "Timeout handling with callback support", + "HyperLAN client communication integration" + ] + }, + { + "file": "./citadel_proto/src/proto/p2p_conn_handler.rs", + "context": [ + "The P2P connection handler module implements direct peer-to-peer connections and NAT traversal in the Citadel Protocol. Notable features include:", + "Direct P2P connection management (TCP/UDP)", + "NAT traversal via UDP hole punching", + "Connection lifecycle management", + "WebRTC compatibility", + "Integration with Citadel's security infrastructure" + ] + }, + { + "file": "./citadel_proto/src/proto/group_channel.rs", + "context": [ + "The group channel module provides secure group communication channels in the Citadel Protocol. Key aspects include:", + "Split channel architecture (separate send/receive)", + "Group broadcast capabilities", + "Permission-based member management", + "Secure message encryption", + "Asynchronous message streaming support" + ] + }, + { + "file": "./citadel_proto/src/proto/message_group.rs", + "context": [ + "The message group module implements a consent-based group messaging framework in the Citadel Protocol. Key features include:", + "Axis of consent model centered around group initiator", + "Management of concurrent and pending peer states", + "Group lifecycle and permission management", + "Short-lived messaging frames for temporary communications", + "Local message history persistence" + ] + }, + { + "file": "./citadel_proto/src/proto/peer_crypt.rs", + "context": [ + "The peer cryptography module handles secure key exchange and NAT traversal for P2P connections. Notable aspects include:", + "Multi-stage key exchange protocol", + "NAT type detection and compatibility checking", + "TLS integration for secure connections", + "Configurable security levels and UDP modes", + "STUN/TURN server fallback support" + ] + }, + { + "file": "./citadel_proto/src/proto/hole_punch_compat_sink_stream.rs", + "context": [ + "The hole punch compatibility stream module provides NAT traversal functionality. Key features include:", + "Reliable ordered stream interface for hole punching", + "Protocol compatibility layer", + "Stacked ratchet encryption integration", + "Support for both C2S and P2P routing", + "Connection state and packet ordering management" + ] + }, + { + "file": "./citadel_proto/src/proto/mod.rs", + "context": [ + "Core implementation of the Citadel Protocol", + "Manages sessions, packet processing, and security", + "Implements peer-to-peer and group communication", + "Handles connection state management", + "Provides efficient packet validation and routing", + "Integrates with cryptographic and networking layers" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/mod.rs", + "context": [ + "The state subcontainers module provides specialized state management for different aspects of the Citadel Protocol's connection lifecycle. Key features include:", + "Connection state management and transitions", + "Key exchange and cryptographic state handling", + "Registration and authentication state tracking", + "State persistence and timeout management", + "Modular state container architecture" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", + "context": [ + "Core connection state management module for Citadel Protocol", + "Implements stage-based connection tracking and transitions", + "Handles credential management during connection process", + "Provides timing control and failure monitoring", + "Supports different connection modes and state recovery", + "Integrates with packet processing and session management", + "Uses atomic operations for thread-safe state transitions" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs", + "context": [ + "The peer key exchange management container handles P2P cryptographic states. Key features include:", + "Key exchange state management", + "Session security configuration", + "UDP channel control", + "Role-based key exchange", + "Session password management" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs", + "context": [ + "The pre-connection state container manages initial connection setup. Important aspects include:", + "Pre-connection stage tracking", + "Node type management", + "Cryptographic initialization", + "UDP channel setup", + "Connection ticket handling" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/register_state_container.rs", + "context": [ + "The registration state container handles user registration processes. Key features include:", + "Registration stage management", + "Cryptographic setup handling", + "Registration timing control", + "Failure state management", + "Passwordless registration support" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs", + "context": [ + "Manages account deregistration state in Citadel Protocol", + "Tracks deregistration process progress and timing", + "Handles deregistration tickets for process identification", + "Provides atomic state transitions for thread safety", + "Integrates with session and remote operations", + "Ensures proper validation of deregistration requests" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs", + "context": [ + "Manages expiration state for high-traffic packet processing", + "Prevents false expiration of active groups under high load", + "Supports both inbound and outbound traffic monitoring", + "Handles file transfer expiry tracking", + "Provides adaptive expiry timing based on workload", + "Integrates with packet processing and group management", + "Uses constant GROUP_EXPIRE_TIME_MS for timing control" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/rekey_container.rs", + "context": [ + "Manages cryptographic key rotation and ratchet updates", + "Implements security level-based key update scheduling", + "Supports peer-to-peer key updates and notifications", + "Provides configurable security levels (0-4)", + "Implements adaptive update frequency based on security needs", + "Handles local rekey requests and kernel notifications", + "Uses stacked ratchet construction for key rotation" + ] + }, + { + "file": "./citadel_proto/src/proto/codec.rs", + "context": [ + "Basic bytes codec implementation for raw data transmission", + "Handles efficient encoding/decoding with configurable buffer capacity", + "Implements zero-copy operations for performance", + "Uses tokio_util's Encoder and Decoder traits", + "Maintains minimum buffer size with auto-resizing" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/rekey_container.rs", + "context": [ + "Manages cryptographic key rotation and ratchet updates", + "Implements security level-based key update scheduling", + "Supports peer-to-peer key updates with state tracking", + "Provides configurable security levels (0-4)", + "Handles local rekey requests and notifications", + "Implements adaptive update frequency based on security needs" + ] + }, + { + "file": "./citadel_proto/src/proto/state_subcontainers/connect_state_container.rs", + "context": [ + "Manages active connection states with stage-based transitions", + "Handles connection credentials and authentication", + "Tracks connection timing and failures", + "Supports different connection modes", + "Provides state recovery mechanisms", + "Integrates with session and packet processing systems" + ] + }, + { + "file": "./citadel_proto/src/proto/peer/group_channel.rs", + "context": [ + "Implements secure group communication channels", + "Uses split channel architecture for send/receive operations", + "Supports group broadcasts and member management", + "Implements permission-based group operations", + "Provides Stream trait implementation for async messaging", + "Uses SecBuffer for encrypted message payloads", + "Handles proper cleanup through Drop implementations" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/clean_shutdown.rs", + "context": [ + "The clean shutdown module provides utilities for gracefully terminating components in the Citadel Protocol. It ensures proper resource cleanup and component notification during shutdown operations.", + "Key aspects:", + "- Implements asynchronous shutdown coordination", + "- Manages resource cleanup during shutdown", + "- Provides timeout-based forced shutdown", + "- Ensures thread-safe shutdown operations", + "- Broadcasts shutdown notifications to components" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/dual_cell.rs", + "context": [ + "The dual cell module implements a flexible cell type that adapts to both single-threaded and multi-threaded contexts through compile-time feature selection.", + "Key aspects:", + "- Uses Cell in single-threaded mode", + "- Uses atomics in multi-threaded mode", + "- Provides zero-cost thread safety abstractions", + "- Ensures proper Send + Sync implementations", + "- Maintains consistent API across modes" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/dual_late_init.rs", + "context": [ + "The dual late init module provides a container for safely initializing values after construction. It ensures proper initialization semantics across different threading contexts.", + "Key aspects:", + "- Ensures safe late initialization", + "- Tracks initialization state", + "- Prevents double initialization", + "- Provides thread-safe access", + "- Zero overhead in single-threaded mode" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/dual_rwlock.rs", + "context": [ + "The dual rwlock module implements a flexible read-write lock that adapts to both single-threaded and multi-threaded contexts through compile-time features.", + "Key aspects:", + "- Uses RefCell in single-threaded mode", + "- Uses RwLock in multi-threaded mode", + "- Supports multiple readers", + "- Provides exclusive writer access", + "- Prevents deadlocks", + "- Zero-cost thread safety abstractions" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/lock_holder.rs", + "context": [ + "The lock holder module provides safe lock management across asynchronous boundaries in the Citadel Protocol. It ensures proper lock handling and cleanup.", + "Key aspects:", + "- Manages locks in async contexts", + "- Prevents deadlocks", + "- Implements RAII-style cleanup", + "- Tracks lock states", + "- Ensures thread safety", + "- Prevents resource leaks" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/mod.rs", + "context": [ + "The misc module provides utility types and functions used throughout the Citadel Protocol implementation.", + "Key aspects:", + "- Provides thread-safe data structures", + "- Implements network utilities", + "- Manages protocol resources", + "- Supports async operations", + "- Defines protocol types", + "- Ensures zero-cost abstractions" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/net.rs", + "context": [ + "The network utilities module provides core networking functionality for the Citadel Protocol, handling connections and protocol operations.", + "Key aspects:", + "- Manages network sockets and connections", + "- Handles address resolution", + "- Implements protocol negotiation", + "- Supports multiple transport protocols", + "- Provides error handling and recovery", + "- Handles both IPv4 and IPv6" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/ordered_channel.rs", + "context": [ + "The ordered channel module implements a messaging channel that guarantees message ordering for protocol operations.", + "Key aspects:", + "- Ensures strict message ordering", + "- Implements asynchronous operations", + "- Provides backpressure mechanisms", + "- Supports multiple producers", + "- Maintains thread safety", + "- Handles channel state and cleanup" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/panic_future.rs", + "context": [ + "The panic future module provides a wrapper for safely handling panics in asynchronous code, preventing process crashes.", + "Key aspects:", + "- Catches and handles panics", + "- Converts panics to errors", + "- Wraps futures safely", + "- Ensures proper unwinding", + "- Maintains thread safety", + "- Preserves error types" + ] + }, + { + "file": "./citadel_proto/src/proto/misc/session_security_settings.rs", + "context": [ + "The session security settings module defines configuration options for securing protocol sessions.", + "Key aspects:", + "- Configures encryption parameters", + "- Manages authentication settings", + "- Controls key management", + "- Sets security levels", + "- Handles protocol versions", + "- Provides secure defaults" + ] + }, + { + "file": "./netbeam/src/sync/subscription.rs", + "context": [ + "Provides bidirectional subscription-based streaming", + "Key aspects:", + "- Implements reliable ordered messaging", + "- Manages stream subscriptions", + "- Supports connection multiplexing", + "- Handles connection lifecycle", + "- Ensures thread-safety and ordering" + ] + }, + { + "file": "./netbeam/src/sync/sync_start.rs", + "context": [ + "Implements network operation synchronization primitives", + "Key aspects:", + "- Coordinates operation start between nodes", + "- Supports type-safe payload exchange", + "- Handles network latency compensation", + "- Integrates with async/await", + "- Provides timing coordination" + ] + }, + { + "file": "./netbeam/src/sync/network_endpoint.rs", + "context": [ + "Provides network endpoint abstraction with address management", + "Key aspects:", + "- Manages socket addresses", + "- Handles connection registration", + "- Tracks connection roles", + "- Integrates with network applications", + "- Provides address resolution" + ] + }, + { + "file": "./netbeam/src/sync/network_application.rs", + "context": [ + "Core network application implementation with synchronization primitives", + "Key aspects:", + "- Provides network synchronization primitives", + "- Implements network operations (select, join)", + "- Manages communication channels", + "- Handles connection multiplexing", + "- Ensures operation coordination" + ] + }, + { + "file": "./netbeam/src/sync/callback_channel.rs", + "context": [ + "Specialized channel implementation for async request-response patterns", + "Key aspects:", + "- Asynchronous message passing with optional callbacks", + "- Built on Tokio MPSC channels", + "- Supports fire-and-forget operations", + "- Thread-safe and cloneable", + "- Implements Stream trait for receiver" + ] + }, + { + "file": "./netbeam/src/sync/tracked_callback_channel.rs", + "context": [ + "Enhanced callback channel with request-response tracking", + "Key aspects:", + "- Request tracking with unique IDs", + "- Response correlation with requests", + "- Thread-safe tracking with atomics", + "- Memory-efficient response tracking", + "- Support for response timeouts" + ] + }, + { + "file": "./netbeam/src/sync/mod.rs", + "context": [ + "Core synchronization module for Netbeam framework", + "Key aspects:", + "- Network endpoint and application management", + "- Bidirectional channels with callbacks", + "- Request-response tracking", + "- Symmetric conversation tracking", + "- Reliable ordered streaming", + "- Thread-safe operations" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_join.rs", + "context": [ + "Network-aware join operation for future synchronization", + "Key aspects:", + "- Synchronizes futures across network endpoints", + "- Returns when both endpoints complete", + "- Early error termination", + "- Type-safe generic values", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_try_join.rs", + "context": [ + "Network-aware try-join operation for fallible future synchronization", + "Key aspects:", + "- Synchronizes fallible futures across endpoints", + "- Returns when both endpoints complete", + "- Early error termination", + "- Type-safe generic value and error types", + "- State synchronization between nodes", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_select.rs", + "context": [ + "Network-aware select operation for racing futures", + "Key aspects:", + "- Races futures between network endpoints", + "- First endpoint to complete wins", + "- Built-in conflict resolution", + "- Type-safe generic result type", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/operations/net_select_ok.rs", + "context": [ + "Network-aware select operation for racing fallible futures", + "Key aspects:", + "- Races fallible futures between endpoints", + "- First successful endpoint wins", + "- Built-in conflict resolution", + "- Error handling with Result type", + "- State synchronization between nodes", + "- Network-aware node types" + ] + }, + { + "file": "./netbeam/src/sync/primitives/net_mutex.rs", + "context": [ + "Distributed mutex implementation for network synchronization", + "Key aspects:", + "- Distributed mutual exclusion", + "- Network-aware locking", + "- Automatic lock release", + "- State synchronization", + "- Deadlock prevention", + "- Background state management" + ] + }, + { + "file": "./netbeam/src/sync/primitives/mod.rs", + "context": [ + "Provides network-aware synchronization primitives", + "Defines NetObject trait for network-compatible types", + "Houses distributed mutex and rwlock implementations", + "Ensures thread-safety and serializability across network boundaries", + "Integrates with Serde for object serialization" + ] + }, + { + "file": "./citadel_sdk/src/builder/mod.rs", + "context": [ + "Provides builder patterns for configuring Citadel Protocol components", + "Ensures type-safe configuration building", + "Validates component configurations", + "Supports flexible node setup for different network roles", + "Integrates with core networking kernel", + "Manages protocol implementation details" + ] + }, + { + "file": "./citadel_sdk/src/builder/node_builder.rs", + "context": [ + "Implements builder pattern for Citadel network nodes", + "Supports both peer and server node configuration", + "Provides multiple backend storage options", + "Configures security settings (TLS, certificates)", + "Integrates with Google services", + "Supports STUN server configuration for NAT traversal", + "Manages server authentication via pre-shared keys", + "Handles kernel executor settings", + "Configures password hashing settings", + "Supports database configuration for enterprise features" + ] + }, + { + "file": "./citadel_sdk/src/backend_kv_store.rs", + "context": [ + "Implements connection-scoped persistent key-value storage", + "Provides async operations for storing and retrieving arbitrary data", + "Uses session and peer IDs for storage isolation", + "Integrates with the persistence handler backend", + "Supports bulk operations for managing multiple key-value pairs", + "Implements automatic error handling and conversion", + "Designed for application-level data persistence" + ] + }, + { + "file": "./citadel_sdk/src/macros.rs", + "context": [ + "Provides procedural macros for SDK trait implementations", + "Reduces boilerplate in network communication code", + "Supports async/await patterns in trait implementations", + "Ensures consistent Remote trait implementations", + "Integrates with account management and request handling", + "Used internally by the SDK for code generation", + "Requires async-trait feature for async implementations" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/mod.rs", + "context": [ + "Root module for client-side networking components", + "Provides connection builders and configuration", + "Supports multiple authentication methods", + "Manages UDP and NAT traversal settings", + "Implements session security and PSK handling", + "Organizes broadcast, peer, and server connections", + "Defines base traits for prefab implementations" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/single_connection.rs", + "context": [ + "Implements single client-to-server connection management", + "Supports multiple authentication modes", + "Provides NAT traversal with UDP support", + "Handles secure session management", + "Supports object transfer handling", + "Manages connection lifecycle" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/peer_connection.rs", + "context": [ + "Manages peer-to-peer connections", + "Supports multiple simultaneous peers", + "Implements file transfer capabilities", + "Handles NAT traversal settings", + "Provides session security", + "Manages peer identification" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/client/broadcast.rs", + "context": [ + "Implements group-based communication", + "Uses owner-based trust model", + "Supports public and private groups", + "Handles member registration", + "Manages group invitations", + "Enables concurrent participation" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/mod.rs", + "context": [ + "Root module for pre-built network components", + "Organizes client and server implementations", + "Provides remote connection management", + "Handles file transfer functionality", + "Manages signal and event processing", + "Implements connection security", + "Supports peer discovery and listing" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs", + "context": [ + "Provides automatic handling of file transfers on the server side", + "Implements automatic acceptance and silent processing", + "Uses minimal resources with no configuration needed", + "Integrates with NetKernel for event handling", + "Handles error conditions gracefully", + "Supports both basic and RE-VFS transfers" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/client_connect_listener.rs", + "context": [ + "Executes custom logic on client connection events", + "Implements async event processing and security management", + "Supports both TCP and UDP channels", + "Handles session security settings", + "Manages connection success events", + "Provides type-safe callback execution" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/empty.rs", + "context": [ + "Provides minimal no-op network kernel implementation", + "Implements zero overhead event acceptance", + "Suitable for basic connection acceptance", + "Uses minimal system resources", + "Provides clean shutdown handling", + "Not designed for interactive servers" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/internal_service.rs", + "context": [ + "Enables integration of internal services like HTTP servers", + "Supports HTTP/1.1 and HTTP/2 protocols", + "Implements bidirectional communication", + "Provides service lifecycle management", + "Handles automatic resource cleanup", + "Supports custom service handlers" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/server/mod.rs", + "context": [ + "Organizes server-side network components", + "Provides pre-built server implementations", + "Supports file transfer and client connections", + "Enables internal service integration", + "Implements event-driven architecture", + "Manages component lifecycle" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/shared/internal_service.rs", + "context": [ + "Provides core functionality for internal service integration", + "Implements bidirectional communication channels", + "Uses async I/O for efficient communication", + "Handles protocol conversion automatically", + "Manages resource cleanup and shutdown", + "Supports thread-safe message passing" + ] + }, + { + "file": "./citadel_sdk/src/prefabs/shared/mod.rs", + "context": [ + "Contains shared network components for client and server", + "Provides role-agnostic functionality", + "Implements thread-safe shared utilities", + "Uses async-first design patterns", + "Manages cross-role functionality", + "Organizes internal service integration" + ] + }, + { + "file": "./citadel_crypt/src/ratchet_manager.rs", + "context": [ + "Implements a secure key ratcheting protocol manager for peer-to-peer communication", + "Manages bidirectional communication between Alice and Bob roles during key updates", + "Handles asynchronous state synchronization with support for Pre-Shared Keys (PSKs)", + "Implements state truncation to manage memory while maintaining security", + "Core component for maintaining forward secrecy in the cryptographic protocol", + "Uses Sink/Stream pattern for message passing between peers", + "Integrates with PeerSessionCrypto for underlying cryptographic operations", + "Supports versioned ratchet states with safe deregistration of old versions" + ] + } ] diff --git a/example-library/examples/c2s/client_basic_transient_connection.rs b/example-library/examples/c2s/client_basic_transient_connection.rs index 89fa36b7e..8a2bfb8c7 100644 --- a/example-library/examples/c2s/client_basic_transient_connection.rs +++ b/example-library/examples/c2s/client_basic_transient_connection.rs @@ -51,10 +51,11 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings. If a custom transient ID is required, use `transient_with_id` over `transient`. - let server_connection_settings = ServerConnectionSettingsBuilder::transient(server_addr) - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::transient(server_addr)? + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create client kernel let kernel = SingleClientServerConnectionKernel::new( @@ -66,7 +67,7 @@ async fn main() -> Result<(), Box> { ); // Build the node - let client = NodeBuilder::default().build(kernel)?; + let client = DefaultNodeBuilder::default().build(kernel)?; // Run the node client.await?; diff --git a/example-library/examples/c2s/client_basic_with_server_password.rs b/example-library/examples/c2s/client_basic_with_server_password.rs index ab7154fcf..5d796aeaa 100644 --- a/example-library/examples/c2s/client_basic_with_server_password.rs +++ b/example-library/examples/c2s/client_basic_with_server_password.rs @@ -55,16 +55,17 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - server_addr, - "my_username", - "My Name", - "notsecurepassword", - ) - .with_session_security_settings(session_security) - .with_session_password(connect_password) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + "my_username", + "My Name", + "notsecurepassword", + )? + .with_session_security_settings(session_security) + .with_session_password(connect_password) + .disable_udp() + .build()?; // Create client kernel let kernel = SingleClientServerConnectionKernel::new( @@ -76,7 +77,7 @@ async fn main() -> Result<(), Box> { ); // Build the node - let client = NodeBuilder::default().build(kernel)?; + let client = DefaultNodeBuilder::default().build(kernel)?; // Run the node client.await?; diff --git a/example-library/examples/c2s/client_echo.rs b/example-library/examples/c2s/client_echo.rs index d4ca2d5e0..496c784bf 100644 --- a/example-library/examples/c2s/client_echo.rs +++ b/example-library/examples/c2s/client_echo.rs @@ -46,15 +46,16 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - server_addr, - "my_username", - "My Name", - "notsecurepassword", - ) - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + "my_username", + "My Name", + "notsecurepassword", + )? + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create client kernel let kernel = SingleClientServerConnectionKernel::new( @@ -87,7 +88,7 @@ async fn main() -> Result<(), Box> { ); // Build the node - let client = NodeBuilder::default().build(kernel)?; + let client = DefaultNodeBuilder::default().build(kernel)?; // Run the node client.await?; diff --git a/example-library/examples/c2s/server_basic.rs b/example-library/examples/c2s/server_basic.rs index 14b751dd4..734500aa7 100644 --- a/example-library/examples/c2s/server_basic.rs +++ b/example-library/examples/c2s/server_basic.rs @@ -38,7 +38,7 @@ async fn main() -> Result<(), Box> { let kernel = server::empty::EmptyKernel; // Build the server - let node = NodeBuilder::default() + let node = DefaultNodeBuilder::default() .with_node_type(NodeType::server(server_addr)?) .build(kernel)?; diff --git a/example-library/examples/c2s/server_basic_with_password.rs b/example-library/examples/c2s/server_basic_with_password.rs index 42cca905c..0b028a8d2 100644 --- a/example-library/examples/c2s/server_basic_with_password.rs +++ b/example-library/examples/c2s/server_basic_with_password.rs @@ -58,7 +58,7 @@ async fn main() -> Result<(), Box> { // Build the server. It is password-protected, meaning that each time // a client attempts to register or connect, they must provide the password. // This "password" is effectively a pre-shared key (PSK) - let node = NodeBuilder::default() + let node = DefaultNodeBuilder::default() .with_node_type(NodeType::server(server_addr)?) .with_server_password(connect_password) .build(kernel)?; diff --git a/example-library/examples/c2s/server_echo.rs b/example-library/examples/c2s/server_echo.rs index ab3f069f8..4c2543f9d 100644 --- a/example-library/examples/c2s/server_echo.rs +++ b/example-library/examples/c2s/server_echo.rs @@ -70,7 +70,7 @@ async fn main() -> Result<(), Box> { }); // Build the server - let node = NodeBuilder::default() + let node = DefaultNodeBuilder::default() .with_node_type(NodeType::server(server_addr)?) .build(kernel)?; diff --git a/example-library/examples/p2p/chat.rs b/example-library/examples/p2p/chat.rs index dd2b119aa..8dd5a5955 100644 --- a/example-library/examples/p2p/chat.rs +++ b/example-library/examples/p2p/chat.rs @@ -53,15 +53,16 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - server_addr, - my_user, - "Name", - "notsecurepassword", - ) - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + )? + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create peer connection setup let peer_connection = PeerConnectionSetupAggregator::default() @@ -118,7 +119,7 @@ async fn main() -> Result<(), Box> { ); // Build the peer - let node = NodeBuilder::default().build(kernel)?; + let node = DefaultNodeBuilder::default().build(kernel)?; // Run the peer node.await?; diff --git a/example-library/examples/p2p/file_transfer.rs b/example-library/examples/p2p/file_transfer.rs index 70c82cf85..1fbf36846 100644 --- a/example-library/examples/p2p/file_transfer.rs +++ b/example-library/examples/p2p/file_transfer.rs @@ -53,15 +53,16 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - server_addr, - my_user, - "Name", - "notsecurepassword", - ) - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + )? + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create peer connection setup let peer_connection = PeerConnectionSetupAggregator::default() @@ -104,7 +105,7 @@ async fn main() -> Result<(), Box> { ); // Build the peer - let node = NodeBuilder::default().build(kernel)?; + let node = DefaultNodeBuilder::default().build(kernel)?; // Run the peer node.await?; diff --git a/example-library/examples/p2p/revfs_delete.rs b/example-library/examples/p2p/revfs_delete.rs index 925a5c3d0..6784944ee 100644 --- a/example-library/examples/p2p/revfs_delete.rs +++ b/example-library/examples/p2p/revfs_delete.rs @@ -63,15 +63,16 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - server_addr, - my_user, - "Name", - "notsecurepassword", - ) - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + )? + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create peer connection setup let peer_connection = PeerConnectionSetupAggregator::default() @@ -122,7 +123,7 @@ async fn main() -> Result<(), Box> { ); // Build the peer - let node = NodeBuilder::default().build(kernel)?; + let node = DefaultNodeBuilder::default().build(kernel)?; // Run the peer node.await?; diff --git a/example-library/examples/p2p/revfs_read_write.rs b/example-library/examples/p2p/revfs_read_write.rs index 67d6380a5..51ff19f36 100644 --- a/example-library/examples/p2p/revfs_read_write.rs +++ b/example-library/examples/p2p/revfs_read_write.rs @@ -61,15 +61,16 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - server_addr, - my_user, - "Name", - "notsecurepassword", - ) - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + )? + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create peer connection setup let peer_connection = PeerConnectionSetupAggregator::default() @@ -127,7 +128,7 @@ async fn main() -> Result<(), Box> { ); // Build the peer - let node = NodeBuilder::default().build(kernel)?; + let node = DefaultNodeBuilder::default().build(kernel)?; // Run the peer node.await?; diff --git a/example-library/examples/p2p/revfs_take.rs b/example-library/examples/p2p/revfs_take.rs index cfa94a931..6c85f9caa 100644 --- a/example-library/examples/p2p/revfs_take.rs +++ b/example-library/examples/p2p/revfs_take.rs @@ -64,15 +64,16 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings - let server_connection_settings = ServerConnectionSettingsBuilder::credentialed_registration( - server_addr, - my_user, - "Name", - "notsecurepassword", - ) - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = + DefaultServerConnectionSettingsBuilder::credentialed_registration( + server_addr, + my_user, + "Name", + "notsecurepassword", + ) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create peer connection setup let peer_connection = PeerConnectionSetupAggregator::default() @@ -131,7 +132,7 @@ async fn main() -> Result<(), Box> { ); // Build the peer - let node = NodeBuilder::default().build(kernel)?; + let node = DefaultNodeBuilder::default().build(kernel)?; // Run the peer node.await?; diff --git a/suggestions.ai.json b/suggestions.ai.json index ef423337c..e51b1482a 100644 --- a/suggestions.ai.json +++ b/suggestions.ai.json @@ -2047,5 +2047,17 @@ "Add test network simulation", "Consider adding test monitoring tools" ] + }, + { + "file": "./citadel_crypt/src/ratchet_manager.rs", + "suggestions": [ + "Consider adding error recovery mechanisms for interrupted rekeying operations", + "Add metrics/telemetry for monitoring rekeying performance and success rates", + "Consider implementing ratchet state backup/recovery mechanisms", + "Add more detailed logging for debugging complex rekeying scenarios", + "Consider adding configuration options for ratchet truncation policies", + "Add comprehensive unit tests for edge cases in the rekeying protocol", + "Consider implementing rate limiting for rekey operations to prevent DoS" + ] } ] From c64b46865107999643c74552d8bad71e7092f737 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Thu, 5 Dec 2024 20:46:58 -0500 Subject: [PATCH 06/12] wip: use loop notation in RatchetManager instead of complex logic --- citadel_crypt/src/ratchet_manager.rs | 47 ++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/citadel_crypt/src/ratchet_manager.rs b/citadel_crypt/src/ratchet_manager.rs index f14a6d03c..1fa6aab68 100644 --- a/citadel_crypt/src/ratchet_manager.rs +++ b/citadel_crypt/src/ratchet_manager.rs @@ -106,7 +106,7 @@ pub enum RekeyState { Idle, } -#[derive(Debug, Copy, Clone, Eq, PartialEq, NoUninit)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, NoUninit, Serialize, Deserialize)] #[repr(u8)] pub enum RekeyRole { Idle, @@ -116,9 +116,9 @@ pub enum RekeyRole { #[derive(Serialize, Deserialize)] pub enum RatchetMessage { - AliceToBob(Vec), // Serialized transfer - BobToAlice(Vec), // Serialized transfer - Truncate(u32), // Version to truncate + AliceToBob(Vec), // Serialized transfer + BobToAlice(Vec, RekeyRole), // Serialized transfer + sender's role + Truncate(u32), // Version to truncate LeaderCanFinish, LoserCanFinish, } @@ -127,7 +127,7 @@ impl Debug for RatchetMessage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { RatchetMessage::AliceToBob(_) => write!(f, "AliceToBob"), - RatchetMessage::BobToAlice(_) => write!(f, "BobToAlice"), + RatchetMessage::BobToAlice(_, role) => write!(f, "BobToAlice(sender_role: {:?})", role), RatchetMessage::Truncate(_) => write!(f, "Truncate"), RatchetMessage::LeaderCanFinish => write!(f, "LeaderCanFinish"), RatchetMessage::LoserCanFinish => write!(f, "LoserCanFinish"), @@ -257,7 +257,7 @@ where let mut completed_as_leader = false; let mut completed_as_loser = false; - while !((!is_initiator && completed_as_leader) || (is_initiator && completed_as_loser)) { + loop { let msg = receiver.next().await; log::debug!(target: "citadel", "Client {} received message {msg:?}", self.cid); match msg { @@ -303,13 +303,20 @@ where KemTransferStatus::Some(transfer, _) => { let serialized = bincode::serialize(&transfer) .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + + { + let container = self.container.write(); + let _ = container.update_in_progress.toggle_on_if_untoggled(); + drop(container); + } + + self.set_role(RekeyRole::Loser); self.sender .lock() .await - .send(RatchetMessage::BobToAlice(serialized)) + .send(RatchetMessage::BobToAlice(serialized, RekeyRole::Loser)) .await .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; - self.set_role(RekeyRole::Loser); log::debug!( target: "citadel", "Client {} is {:?}. Sent BobToAlice", @@ -331,8 +338,18 @@ where } } } - Some(RatchetMessage::BobToAlice(transfer_data)) => { - // Allow Leader if contention, or Idle if no contention + Some(RatchetMessage::BobToAlice(transfer_data, sender_role)) => { + // If the sender became a Loser, they expect us to be Leader + if sender_role == RekeyRole::Loser && self.role() != RekeyRole::Leader { + log::debug!( + target: "citadel", + "Client {} transitioning to Leader as peer became Loser before we were able to transition", + self.cid + ); + self.set_role(RekeyRole::Leader); + } + + // Now verify we're in a valid state to process the message if self.role() == RekeyRole::Loser { return Err(CryptError::RekeyUpdateError(format!( "Unexpected BobToAlice message since our role is not Leader, but {:?}", @@ -433,6 +450,7 @@ where .send(RatchetMessage::LeaderCanFinish) .await .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + break; } Some(RatchetMessage::LeaderCanFinish) => { // Allow Leader if contention, or Idle if no contention @@ -442,7 +460,7 @@ where self.role() ))); } - log::debug!(target: "citadel", "Client {} received LeaderCanFinish 00", self.cid); + log::debug!(target: "citadel", "Client {} received LeaderCanFinish", self.cid); { let mut container = self.container.write(); @@ -450,8 +468,8 @@ where let _ = container.maybe_unlock(false); } - log::debug!("cid {} received LeaderCanFinish", self.cid); completed_as_leader = true; + break; } Some(RatchetMessage::LoserCanFinish) => { @@ -478,6 +496,7 @@ where .send(RatchetMessage::LeaderCanFinish) .await .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + break; } None => { @@ -567,7 +586,9 @@ mod racy { if let Some(delay) = delay { tokio::time::sleep(delay).await; } - container.trigger_rekey().await + let res = container.trigger_rekey().await; + log::debug!(target: "citadel", "*** [FINISHED] Client {} rekey result: {res:?}", container.cid); + res }; // Randomly assign a delay to Alice or Bob, if applicable From e33797d27d2973585f9605552657e457f48c4b4b Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Wed, 18 Dec 2024 21:57:15 -0500 Subject: [PATCH 07/12] feat: RatchetManager: all stress tests passing --- .../src/endpoint_crypto_container.rs | 519 +--------------- citadel_crypt/src/lib.rs | 6 +- citadel_crypt/src/ratchet_manager.rs | 540 ++++++++++++----- citadel_crypt/src/ratchets/mod.rs | 204 +++++++ .../src/{fcm => ratchets/mono}/keys.rs | 2 +- .../src/{fcm => ratchets/mono}/mod.rs | 1 - .../src/{fcm => ratchets/mono}/ratchet.rs | 88 ++- citadel_crypt/src/ratchets/stacked/mod.rs | 2 + .../{ => ratchets/stacked}/stacked_ratchet.rs | 562 +++--------------- citadel_crypt/src/scramble/crypt_splitter.rs | 4 +- .../src/streaming_crypt_scrambler.rs | 4 +- citadel_crypt/src/sync_toggle.rs | 2 +- citadel_crypt/src/toolset.rs | 9 +- citadel_crypt/tests/primary.rs | 60 +- citadel_proto/src/kernel/kernel_executor.rs | 2 +- citadel_proto/src/kernel/kernel_trait.rs | 2 +- citadel_proto/src/kernel/mod.rs | 2 +- citadel_proto/src/lib.rs | 7 +- .../src/proto/endpoint_crypto_accessor.rs | 2 +- citadel_proto/src/proto/mod.rs | 2 +- citadel_proto/src/proto/node.rs | 2 +- citadel_proto/src/proto/node_request.rs | 2 +- citadel_proto/src/proto/packet_crafter.rs | 30 +- .../proto/packet_processor/connect_packet.rs | 4 +- .../packet_processor/deregister_packet.rs | 2 +- .../packet_processor/disconnect_packet.rs | 2 +- .../src/proto/packet_processor/file_packet.rs | 2 +- .../src/proto/packet_processor/hole_punch.rs | 2 +- .../packet_processor/keep_alive_packet.rs | 2 +- .../packet_processor/peer/group_broadcast.rs | 2 +- .../src/proto/packet_processor/peer/mod.rs | 2 +- .../packet_processor/peer/peer_cmd_packet.rs | 2 +- .../peer/server/post_connect.rs | 2 +- .../peer/server/post_register.rs | 2 +- .../packet_processor/preconnect_packet.rs | 2 +- .../packet_processor/primary_group_packet.rs | 2 +- .../packet_processor/raw_primary_packet.rs | 2 +- .../proto/packet_processor/register_packet.rs | 2 +- .../proto/packet_processor/rekey_packet.rs | 2 +- .../src/proto/packet_processor/udp_packet.rs | 2 +- citadel_proto/src/proto/peer/channel.rs | 2 +- .../peer/hole_punch_compat_sink_stream.rs | 2 +- .../src/proto/peer/p2p_conn_handler.rs | 2 +- citadel_proto/src/proto/peer/peer_layer.rs | 2 +- citadel_proto/src/proto/remote.rs | 2 +- citadel_proto/src/proto/session.rs | 2 +- citadel_proto/src/proto/session_manager.rs | 2 +- .../src/proto/session_queue_handler.rs | 2 +- citadel_proto/src/proto/state_container.rs | 2 +- .../peer_kem_state_container.rs | 2 +- .../preconnect_state_container.rs | 2 +- .../register_state_container.rs | 2 +- .../state_subcontainers/rekey_container.rs | 2 +- citadel_proto/src/proto/validation.rs | 12 +- citadel_sdk/src/builder/node_builder.rs | 2 +- citadel_sdk/src/fs.rs | 3 +- .../src/prefabs/shared/internal_service.rs | 3 +- citadel_sdk/src/remote_ext.rs | 3 +- citadel_sdk/tests/stress_tests.rs | 2 +- citadel_user/src/account_loader.rs | 2 +- citadel_user/src/account_manager.rs | 8 +- .../src/backend/filesystem_backend.rs | 2 +- citadel_user/src/backend/memory.rs | 2 +- citadel_user/src/backend/mod.rs | 7 +- citadel_user/src/backend/redis_backend.rs | 2 +- citadel_user/src/backend/sql_backend.rs | 7 +- citadel_user/src/client_account.rs | 12 +- citadel_user/src/hypernode_account.rs | 2 +- citadel_user/tests/crypto.rs | 4 +- citadel_user/tests/primary.rs | 11 +- 70 files changed, 880 insertions(+), 1320 deletions(-) create mode 100644 citadel_crypt/src/ratchets/mod.rs rename citadel_crypt/src/{fcm => ratchets/mono}/keys.rs (98%) rename citadel_crypt/src/{fcm => ratchets/mono}/mod.rs (77%) rename citadel_crypt/src/{fcm => ratchets/mono}/ratchet.rs (78%) create mode 100644 citadel_crypt/src/ratchets/stacked/mod.rs rename citadel_crypt/src/{ => ratchets/stacked}/stacked_ratchet.rs (57%) diff --git a/citadel_crypt/src/endpoint_crypto_container.rs b/citadel_crypt/src/endpoint_crypto_container.rs index d7a364d62..b925577af 100644 --- a/citadel_crypt/src/endpoint_crypto_container.rs +++ b/citadel_crypt/src/endpoint_crypto_container.rs @@ -58,14 +58,14 @@ //! # Related Components //! //! - [`crate::toolset::Toolset`] - Cryptographic toolset -//! - [`crate::stacked_ratchet::StackedRatchet`] - Ratchet implementation +//! - [`crate::ratchets::stacked::stacked_ratchet::StackedRatchet`] - Ratchet implementation //! - [`crate::misc::CryptError`] - Error handling //! #![allow(missing_docs)] use crate::misc::CryptError; -use crate::stacked_ratchet::Ratchet; +use crate::ratchets::Ratchet; use crate::sync_toggle::{CurrentToggleState, SyncToggle}; use crate::toolset::{Toolset, ToolsetUpdateStatus}; use citadel_pqcrypto::constructor_opts::ConstructorOpts; @@ -86,9 +86,7 @@ pub struct PeerSessionCrypto { pub update_in_progress: SyncToggle, // if local is initiator, then in the case both nodes send a FastMessage at the same time (causing an update to the keys), the initiator takes preference, and the non-initiator's upgrade attempt gets dropped (if update_in_progress) pub local_is_initiator: bool, - pub rolling_object_id: ObjectId, pub rolling_group_id: u64, - pub lock_set_by_alice: Option, /// Alice sends to Bob, then bob updates internally the toolset. However. Bob can't send packets to Alice quite yet using that newest version. He must first wait from Alice to commit on her end and wait for an ACK. /// If alice sends a packet using the latest version, that's okay since we already have that entropy_bank version on Bob's side; it's just that Bob can't send packets using the latest version until AFTER receiving the ACK pub latest_usable_version: u32, @@ -104,9 +102,7 @@ impl PeerSessionCrypto { toolset, update_in_progress: SyncToggle::new(), local_is_initiator, - rolling_object_id: ObjectId::random(), rolling_group_id: 0, - lock_set_by_alice: None, latest_usable_version: 0, } } @@ -118,9 +114,7 @@ impl PeerSessionCrypto { toolset: self.toolset.clone(), update_in_progress: self.update_in_progress.clone(), local_is_initiator: self.local_is_initiator, - rolling_object_id: self.rolling_object_id, rolling_group_id: self.rolling_group_id, - lock_set_by_alice: self.lock_set_by_alice, latest_usable_version: self.latest_usable_version, } } @@ -172,17 +166,6 @@ impl PeerSessionCrypto { return Ok((None, status)); } - /* - let transfer = if local_is_alice { - None - } else { - // Generate transfer after version update - let transfer = newest_version.stage0_bob().ok_or_else(|| { - CryptError::RekeyUpdateError("Unable to progress past stage0_bob".to_string()) - })?; - Some(transfer) - };*/ - // Generate transfer after version update let transfer = newest_version.stage0_bob().ok_or_else(|| { CryptError::RekeyUpdateError("Unable to progress past stage0_bob".to_string()) @@ -201,7 +184,7 @@ impl PeerSessionCrypto { .ok_or_else(|| { CryptError::RekeyUpdateError("Unable to progress past update_from".to_string()) })?; - log::trace!(target: "citadel", "[E2E] Successfully updated StackedRatchet from v{cur_vers} to v{next_vers} for {local_cid}"); + log::trace!(target: "citadel", "[E2E] Client {local_cid} successfully updated Ratchet from v{cur_vers} to v{next_vers}"); Ok((Some(transfer), status)) } @@ -218,17 +201,15 @@ impl PeerSessionCrypto { pub fn update_sync_safe( &mut self, constructor: R::Constructor, - local_is_alice: bool, - local_cid: u64, triggered_by_bob_to_alice_transfer: bool, ) -> Result, CryptError> { + let local_cid = self.toolset.cid; let update_in_progress = - self.update_in_progress.state() == CurrentToggleState::AlreadyToggled; - log::trace!(target: "citadel", "[E2E] Calling UPDATE (local_is_alice: {}. Update in progress: {})", local_is_alice, update_in_progress); + self.update_in_progress.toggle_on_if_untoggled() == CurrentToggleState::AlreadyToggled; - // if local is alice (relative), then update_in_progress will be expected to be true. As such, we don't want an update to occur - // if local_is_alice is true, and we are updating, that means we are in the middle of a protocol resolution, and we don't want to update - if update_in_progress && local_is_alice { + log::trace!(target: "citadel", "[E2E] Calling UPDATE (triggered by bob_to_alice tx: {triggered_by_bob_to_alice_transfer}. Update in progress: {update_in_progress})"); + + if update_in_progress && !triggered_by_bob_to_alice_transfer { // update is in progress. We only update if local is NOT the initiator (this implies the packet triggering this was sent by the initiator, which takes the preference as desired) // if local is initiator, then the packet was sent by the non-initiator, and as such, we don't update on local if !self.local_is_initiator { @@ -243,6 +224,7 @@ impl PeerSessionCrypto { local_cid, !triggered_by_bob_to_alice_transfer, ); + if let Err(err) = &result { log::error!(target: "citadel", "[E2E] Error during update: {:?}", err); self.update_in_progress.toggle_off(); @@ -250,60 +232,36 @@ impl PeerSessionCrypto { let (transfer, status) = result?; - let ret = if let Some(transfer) = transfer { - KemTransferStatus::Some(transfer, status) + if let Some(transfer) = transfer { + Ok(KemTransferStatus::Some(transfer, status)) } else { - // if it returns with None, and local isn't alice, return an error since we expected Some + // if it returns with None, and this wasn't triggered by a bob to alice tx return an error since we expected Some if !triggered_by_bob_to_alice_transfer { return Err(CryptError::RekeyUpdateError( "This should only be reached if triggered by a bob-to-alice transfer event, yet, conflicting program state".to_string(), )); } - KemTransferStatus::StatusNoTransfer(status) - }; - - // if ret has some, we need one more thing. If we are upgrading the ratchet here on bob's end, we need to place a lock to ensure to updates come from this end until after a TRUNCATE packet comes - // if this is alice's end, we don't unlock quite yet - if !local_is_alice && ret.has_some() { - let _ = self.update_in_progress.toggle_on_if_untoggled(); - self.lock_set_by_alice = Some(false); + Ok(KemTransferStatus::StatusNoTransfer(status)) } - - Ok(ret) } /// Unlocks the hold on future updates, then returns the latest stacked_ratchet - /// Providing "false" will unconditionally unlock the ratchet - pub fn maybe_unlock(&mut self, requires_locked_by_alice: bool) -> Option<&R> { - if requires_locked_by_alice { - if self.lock_set_by_alice.unwrap_or(false) { - if self.update_in_progress.reset_and_get_previous() - != CurrentToggleState::AlreadyToggled - { - log::error!(target: "citadel", "Expected update_in_progress to be true"); - } - - self.lock_set_by_alice = None; - log::trace!(target: "citadel", "Unlocking for {}", self.toolset.cid); - } - } else { - if self.update_in_progress.reset_and_get_previous() - != CurrentToggleState::AlreadyToggled - { - log::error!(target: "citadel", "Expected update_in_progress to be true. LSBA: {:?} | Cid: {}", self.lock_set_by_alice, self.toolset.cid); - } - - self.lock_set_by_alice = None; - log::trace!(target: "citadel", "Unlocking for {}", self.toolset.cid); + pub fn maybe_unlock(&mut self) -> Option<&R> { + if self.update_in_progress.reset_and_get_previous() != CurrentToggleState::AlreadyToggled { + log::error!(target: "citadel", "Client {} expected update_in_progress to be true", self.toolset.cid); + return None; } + log::trace!(target: "citadel", "Unlocking for {}", self.toolset.cid); + self.get_ratchet(None) } /// For alice: this should be called ONLY if the update occurred locally. This updates the latest usable version at the endpoint /// For bob: this should be called AFTER receiving the TRUNCATE_STATUS/ACK packet pub fn post_alice_stage1_or_post_stage1_bob(&mut self) { + log::trace!(target: "citadel", "post_alice_stage1_or_post_stage1_bob for {}: Upgrading from {} to {}", self.toolset.cid, self.latest_usable_version, self.latest_usable_version.wrapping_add(1)); self.latest_usable_version = self.latest_usable_version.wrapping_add(1); } @@ -316,36 +274,18 @@ impl PeerSessionCrypto { Uuid::new_v4().as_u128().into() } - /// Returns a new constructor only if a concurrent update isn't occurring - /// `force`: If the internal boolean was locked prior to calling this in anticipation, force should be true - pub fn get_next_constructor(&mut self, force: bool) -> Option { - let next_constructor = move |this: &mut Self| { - // Only set lock_set_by_alice if we successfully get a constructor - if let Some(constructor) = this.get_ratchet(None)?.next_alice_constructor() { - this.lock_set_by_alice = Some(true); - Some(constructor) - } else { - None - } - }; - - if force { - return next_constructor(self); - } - - if self.update_in_progress.toggle_on_if_untoggled() == CurrentToggleState::AlreadyToggled { - None + pub fn get_next_constructor(&mut self) -> Option { + if self.update_in_progress.toggle_on_if_untoggled() == CurrentToggleState::JustToggled { + self.get_ratchet(None)?.next_alice_constructor() } else { - next_constructor(self) + None } } /// Refreshed the internal state to init state pub fn refresh_state(&mut self) { self.update_in_progress.toggle_off(); - self.lock_set_by_alice = None; self.rolling_group_id = 0; - self.rolling_object_id = ObjectId::random(); } /// Gets the parameters used at registrations @@ -367,7 +307,6 @@ pub trait AssociatedCryptoParams { fn crypto_params(&self) -> CryptoParameters; } -// TODO: Use GAT's to have a type AliceToBobConstructor<'a>. Get rid of these enums pub trait EndpointRatchetConstructor: Debug + Send + Sync + 'static { type AliceToBobWireTransfer: Send + Sync @@ -461,413 +400,3 @@ impl KemTransferStatus { matches!(self, KemTransferStatus::Some(..)) } } - -#[cfg(test)] -pub(crate) mod no_race { - use crate::endpoint_crypto_container::{KemTransferStatus, PeerSessionCrypto}; - use crate::prelude::{ConstructorOpts, Toolset}; - use crate::stacked_ratchet::constructor::StackedRatchetConstructor; - use crate::stacked_ratchet::{Ratchet, StackedRatchet}; - use crate::toolset::{ToolsetUpdateStatus, MAX_RATCHETS_IN_MEMORY}; - use citadel_types::prelude::{EncryptionAlgorithm, KemAlgorithm, SecurityLevel}; - - const ALICE_CID: u64 = 10; - const BOB_CID: u64 = 20; - pub const TEST_PSKS: &[&[u8]] = &[b"test_psk_1", b"test_psk_2"]; - - fn gen>( - version: u32, - opts: Vec, - psks: &[T], - ) -> (StackedRatchet, StackedRatchet) { - let mut cx_alice = - StackedRatchetConstructor::new_alice_constructor(opts.clone(), ALICE_CID, version) - .unwrap(); - let mut cx_bob = StackedRatchetConstructor::new_bob_constructor( - BOB_CID, - version, - opts, - cx_alice.stage0_alice().unwrap(), - psks, - ) - .unwrap(); - cx_alice - .stage1_alice(cx_bob.stage0_bob().unwrap(), psks) - .unwrap(); - - (cx_alice.finish().unwrap(), cx_bob.finish().unwrap()) - } - - pub(crate) fn setup_endpoint_containers( - security_level: SecurityLevel, - enx: EncryptionAlgorithm, - kem: KemAlgorithm, - ) -> ( - PeerSessionCrypto, - PeerSessionCrypto, - ) { - let opts = ConstructorOpts::new_vec_init(Some(enx + kem), security_level); - let (hr_alice, hr_bob) = gen(START_VERSION, opts, TEST_PSKS); - assert_eq!(hr_alice.version(), START_VERSION); - assert_eq!(hr_bob.version(), START_VERSION); - assert_eq!(hr_alice.get_cid(), ALICE_CID); - assert_eq!(hr_bob.get_cid(), BOB_CID); - let alice_container = PeerSessionCrypto::new(Toolset::new(ALICE_CID, hr_alice), true); - let bob_container = PeerSessionCrypto::new(Toolset::new(BOB_CID, hr_bob), false); - (alice_container, bob_container) - } - - const START_VERSION: u32 = 0; - - pub(crate) fn pre_round_assertions( - alice_container: &PeerSessionCrypto, - alice_cid: u64, - bob_container: &PeerSessionCrypto, - bob_cid: u64, - ) -> (u32, u32) { - assert_eq!( - alice_container.get_ratchet(None).unwrap().get_cid(), - alice_cid - ); - assert_eq!(bob_container.get_ratchet(None).unwrap().get_cid(), bob_cid); - - let start_version = alice_container - .toolset - .get_most_recent_stacked_ratchet_version(); - let new_version = start_version + 1; - let new_version_bob = bob_container - .toolset - .get_most_recent_stacked_ratchet_version() - + 1; - assert_eq!(new_version, new_version_bob); - (start_version, new_version) - } - - fn expect_truncation_checks( - expects_truncation: bool, - container: &PeerSessionCrypto, - status: ToolsetUpdateStatus, - requires_truncation: Option, - ) -> Option { - if expects_truncation { - if container.toolset.len() >= MAX_RATCHETS_IN_MEMORY { - let ToolsetUpdateStatus::CommittedNeedsSynchronization { - new_version: _, - oldest_version, - } = status - else { - panic!("Expected ToolsetUpdateStatus::CommittedNeedsSynchronization"); - }; - return Some(oldest_version); - } else { - assert!(requires_truncation.is_none()); - } - } - - None - } - - /// `expect_truncation` should be set to false if the round is expected to complete without truncation, meaning - /// there are less than MAX_HYPER_RATCHETS_IN_MEMORY HR's in memory and thus no truncation is needed. Otherwise, - /// set to true if truncation is expected - fn run_round_no_race( - container_0: &mut PeerSessionCrypto, - container_1: &mut PeerSessionCrypto, - expect_truncation: bool, - ) { - let cid_0 = container_0.toolset.cid; - let cid_1 = container_1.toolset.cid; - - let (start_version, next_version) = - pre_round_assertions(container_0, cid_0, container_1, cid_1); - - let mut alice_constructor = container_0.get_next_constructor(false).unwrap(); - let alice_to_bob_transfer = alice_constructor.stage0_alice().unwrap(); - - // Bob must generate his next opts recursively to continue ratcheting appropriately - let next_opts = container_1 - .get_ratchet(None) - .unwrap() - .get_next_constructor_opts(); - - let bob_constructor = StackedRatchetConstructor::new_bob_constructor( - cid_1, - next_version, - next_opts, - alice_to_bob_transfer, - TEST_PSKS, - ) - .unwrap(); - - // Perform update on Bob's side, container_1 - let kem_transfer_status_bob = container_1 - .update_sync_safe(bob_constructor, false, cid_1, true) - .unwrap(); - - let requires_truncation_bob = kem_transfer_status_bob.requires_truncation(); - - match kem_transfer_status_bob { - KemTransferStatus::Some(bob_to_alice_transfer, toolset_status_bob) => { - if !expect_truncation { - assert!(requires_truncation_bob.is_none()); - assert!( - matches!(toolset_status_bob, ToolsetUpdateStatus::Committed { new_version } if new_version == next_version) - ); - } - - // In this case, bob expects truncation, but, alice will too, in which case we let alice handle the truncation logic - let _do_nothing = expect_truncation_checks( - expect_truncation, - container_1, - toolset_status_bob, - requires_truncation_bob, - ); - - // Flow in the protocol: primary_group_packet.rs:fn attempt_kem_as_alice_finish - // alice: stage1_alice - // alice: update - // alice: if truncation is required, we call deregister_oldest_stacked_ratchet - // alice: call post_alice_stage1_or_post_stage1_bob - // alice: if truncation not required, unlock(requires_locked_by_alice=true) and end, else: - // alice: send TRUNCATE packet to bob - // bob: call deregister_oldest_stacked_ratchet on that truncated version in the packet - // bob: call post_alice_stage1_or_post_stage1_bob - // bob: call unlock(requires_locked_by_alice=false), providing false since bob is not alice - // bob: send TRUNCATE_ACK packet to alice - // alice: call unlock(requires_locked_by_alice=true) and end - - alice_constructor - .stage1_alice(bob_to_alice_transfer, TEST_PSKS) - .unwrap(); - - let kem_transfer_status_alice = container_0 - .update_sync_safe(alice_constructor, true, cid_0, false) - .unwrap(); - - let requires_truncation_alice = kem_transfer_status_alice.requires_truncation(); - - if !expect_truncation { - assert!(requires_truncation_alice.is_none()); - assert!(matches!(kem_transfer_status_alice, - KemTransferStatus::StatusNoTransfer( - ToolsetUpdateStatus::Committed { - new_version - } - ) if new_version == next_version - )); - } - - assert_eq!(requires_truncation_alice, requires_truncation_bob, "Asymmetry not allowed:requires_truncation_alice: {requires_truncation_alice:?}, requires_truncation_bob: {requires_truncation_bob:?}"); - - // Continue to follow protocol flow - - // If no truncation, we runs some tests and short-circuit here - if requires_truncation_alice.is_none() { - // Test message encryption/decryption. Since post_alice_stage1_or_post_stage1_bob has not yet been called, - // the latest usable version should still be the start version - ratchet_encrypt_decrypt_test( - container_0, - cid_0, - container_1, - cid_1, - start_version, - ); - simulate_protocol_resolution_no_truncation(container_0, container_1); - - assert_eq!(start_version + 1, next_version); - - // Test message encryption/decryption again. Now that post_alice_stage1_or_post_stage1_bob has been called, - // the latest usable version should be the new version - ratchet_encrypt_decrypt_test( - container_0, - cid_0, - container_1, - cid_1, - next_version, - ); - return; - } - - match kem_transfer_status_alice { - KemTransferStatus::StatusNoTransfer(toolset_status_alice) => { - if let Some(version_to_truncate) = expect_truncation_checks( - expect_truncation, - container_0, - toolset_status_alice, - requires_truncation_alice, - ) { - container_0 - .deregister_oldest_stacked_ratchet(version_to_truncate) - .unwrap(); - container_0.post_alice_stage1_or_post_stage1_bob(); - // Assume alice then sends a TRUNCATE packet to bob, telling him to remove this version - // send_to_bob(version_to_truncate); - container_1 - .deregister_oldest_stacked_ratchet(version_to_truncate) - .unwrap(); - container_1.post_alice_stage1_or_post_stage1_bob(); - let ratchet_bob = container_1.maybe_unlock(false).unwrap(); - // Assume bob then sends a TRUNCATE_ACK packet to alice, telling her to remove her local lock - // send_to_alice(version_to_truncate); - let ratchet_alice = container_0.maybe_unlock(true).unwrap(); - assert_eq!(ratchet_alice.version(), ratchet_bob.version()); - let expected_version = ratchet_alice.version(); - ratchet_encrypt_decrypt_test( - container_0, - cid_0, - container_1, - cid_1, - expected_version, - ); - } - } - status => { - log::warn!(target: "citadel", "KemTransferStatus for Alice is not handled in this test: {status:?}"); - } - } - } - status => { - log::warn!(target: "citadel", "KemTransferStatus for Bob is not handled in this test: {status:?}") - } - } - } - - pub(crate) fn ratchet_encrypt_decrypt_test( - container_0: &PeerSessionCrypto, - cid_0: u64, - container_1: &PeerSessionCrypto, - cid_1: u64, - expected_version: u32, - ) { - let test_message = b"Hello, World!"; - let alice_ratchet = container_0.get_ratchet(None).unwrap(); - assert_eq!(alice_ratchet.version(), expected_version); - assert_eq!(alice_ratchet.get_cid(), cid_0); - let encrypted = alice_ratchet.encrypt(test_message).unwrap(); - - let bob_ratchet = container_1.get_ratchet(None).unwrap(); - assert_eq!(bob_ratchet.version(), expected_version); - assert_eq!(bob_ratchet.get_cid(), cid_1); - let decrypted = bob_ratchet.decrypt(&encrypted).unwrap(); - assert_eq!(test_message.to_vec(), decrypted); - } - - fn simulate_protocol_resolution_no_truncation( - container_0: &mut PeerSessionCrypto, - container_1: &mut PeerSessionCrypto, - ) { - container_0.post_alice_stage1_or_post_stage1_bob(); - container_1.post_alice_stage1_or_post_stage1_bob(); - let _ = container_0.maybe_unlock(false); - let _ = container_1.maybe_unlock(false); - } - - fn endpoint_container_test( - limit: usize, - expect_truncation: bool, - fx: impl for<'a> Fn( - usize, - &'a mut PeerSessionCrypto, - &'a mut PeerSessionCrypto, - ) -> ( - &'a mut PeerSessionCrypto, - &'a mut PeerSessionCrypto, - ), - ) { - citadel_logging::setup_log(); - let security_level = SecurityLevel::Standard; - - let (mut alice_container, mut bob_container) = setup_endpoint_containers( - security_level, - EncryptionAlgorithm::AES_GCM_256, - KemAlgorithm::Kyber, - ); - - // Start at 1 since we already have 1 HR in memory per node. We go to the limit of - // MAX_RATCHETS_IN_MEMORY - 1 since we need to account for the SR's already in memory - for idx in 1..(limit - 1) { - let (container_0, container_1) = fx(idx, &mut alice_container, &mut bob_container); - run_round_no_race(container_0, container_1, expect_truncation); - } - } - - /// The next three tests focus on either alice, bob, or alternating between the two - /// updating the state serially and never in parallel. Additionally, the tests expect - /// no truncation to occur since the number of SR's in memory is never exceeded - #[test] - fn test_endpoint_container_no_truncation_only_alice_no_race() { - citadel_logging::setup_log(); - endpoint_container_test( - MAX_RATCHETS_IN_MEMORY, - false, - |_, alice_container, bob_container| (alice_container, bob_container), - ); - } - - #[test] - fn test_endpoint_container_no_truncation_only_bob_no_race() { - citadel_logging::setup_log(); - endpoint_container_test( - MAX_RATCHETS_IN_MEMORY, - false, - |_, alice_container, bob_container| (bob_container, alice_container), - ); - } - - #[test] - fn test_endpoint_container_no_truncation_alternating_no_race() { - citadel_logging::setup_log(); - endpoint_container_test( - MAX_RATCHETS_IN_MEMORY, - false, - |idx, alice_container, bob_container| { - if idx % 2 == 0 { - (bob_container, alice_container) - } else { - (alice_container, bob_container) - } - }, - ); - } - - /// The next three tests focus on either alice, bob, or alternating between the two - /// updating the state serially and never in parallel. Unlike the previous three tests, - /// these tests expect truncation to occur since the number of SR's in memory is exceeded - const TEST_RATCHET_LIMIT: usize = MAX_RATCHETS_IN_MEMORY + 100; - - #[test] - fn test_endpoint_container_truncation_only_alice_no_race() { - citadel_logging::setup_log(); - endpoint_container_test( - TEST_RATCHET_LIMIT, - true, - |_, alice_container, bob_container| (alice_container, bob_container), - ); - } - - #[test] - fn test_endpoint_container_truncation_only_bob_no_race() { - citadel_logging::setup_log(); - endpoint_container_test( - TEST_RATCHET_LIMIT, - true, - |_, alice_container, bob_container| (bob_container, alice_container), - ); - } - - #[test] - fn test_endpoint_container_truncation_alternating_no_race() { - citadel_logging::setup_log(); - endpoint_container_test( - TEST_RATCHET_LIMIT, - true, - |idx, alice_container, bob_container| { - if idx % 2 == 0 { - (bob_container, alice_container) - } else { - (alice_container, bob_container) - } - }, - ); - } -} diff --git a/citadel_crypt/src/lib.rs b/citadel_crypt/src/lib.rs index 1861b1d88..0d90816f0 100644 --- a/citadel_crypt/src/lib.rs +++ b/citadel_crypt/src/lib.rs @@ -59,18 +59,16 @@ pub mod argon; pub mod endpoint_crypto_container; /// Organizes the different types of entropy_banks that can be used. Currently, there is only one: The Standard Drill pub mod entropy_bank; -/// Contains the cryptographic primitives for handling FCM interactions on the network -pub mod fcm; /// Error type pub mod misc; /// For endowing packets with coordinates pub mod packet_vector; +/// Contains the cryptographic primitives for handling FCM interactions on the network +pub mod ratchets; /// Contains the subroutines for network-related functionality pub mod scramble; /// For secure byte handling pub mod secure_buffer; -/// This is a container for holding the entropy_bank and PQC, and is intended to replace the separate use of the entropy_bank/PQC -pub mod stacked_ratchet; /// Allows thread-pooled asynchronous and parallel file processing pub mod streaming_crypt_scrambler; diff --git a/citadel_crypt/src/ratchet_manager.rs b/citadel_crypt/src/ratchet_manager.rs index 1fa6aab68..7e7cff457 100644 --- a/citadel_crypt/src/ratchet_manager.rs +++ b/citadel_crypt/src/ratchet_manager.rs @@ -52,7 +52,7 @@ use crate::endpoint_crypto_container::{ EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, }; use crate::misc::CryptError; -use crate::stacked_ratchet::Ratchet; +use crate::ratchets::Ratchet; use atomic::Atomic; use bytemuck::NoUninit; use citadel_io::tokio::sync::Mutex as TokioMutex; @@ -116,20 +116,26 @@ pub enum RekeyRole { #[derive(Serialize, Deserialize)] pub enum RatchetMessage { - AliceToBob(Vec), // Serialized transfer + AliceToBob { + payload: Vec, + earliest_ratchet_version: u32, + latest_ratchet_version: u32, + }, // Serialized transfer BobToAlice(Vec, RekeyRole), // Serialized transfer + sender's role Truncate(u32), // Version to truncate - LeaderCanFinish, + LeaderCanFinish { + latest_version: u32, + }, LoserCanFinish, } impl Debug for RatchetMessage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - RatchetMessage::AliceToBob(_) => write!(f, "AliceToBob"), + RatchetMessage::AliceToBob { .. } => write!(f, "AliceToBob"), RatchetMessage::BobToAlice(_, role) => write!(f, "BobToAlice(sender_role: {:?})", role), RatchetMessage::Truncate(_) => write!(f, "Truncate"), - RatchetMessage::LeaderCanFinish => write!(f, "LeaderCanFinish"), + RatchetMessage::LeaderCanFinish { .. } => write!(f, "LeaderCanFinish"), RatchetMessage::LoserCanFinish => write!(f, "LoserCanFinish"), } } @@ -169,27 +175,48 @@ where } /// Returns true if the re-key was a success, false if no re-key was needed - pub async fn trigger_rekey(&mut self) -> Result { - if self.state() == RekeyState::Halted { + pub async fn trigger_rekey(&self) -> Result { + log::info!(target: "citadel", "Client {} manually triggering rekey", self.cid); + let state = self.state(); + if state == RekeyState::Halted { return Err(CryptError::RekeyUpdateError( "Rekey process is halted".to_string(), )); } - let constructor = { self.container.write().get_next_constructor(false) }; + if self.role() != RekeyRole::Idle { + // We are already in a rekey process + return Ok(false); + } + + let (constructor, earliest_ratchet_version, latest_ratchet_version) = { + let mut container = self.container.write(); + let constructor = container.get_next_constructor(); + let earliest_ratchet_version = container.toolset.get_oldest_stacked_ratchet_version(); + let latest_ratchet_version = container.latest_usable_version; + ( + constructor, + earliest_ratchet_version, + latest_ratchet_version, + ) + }; if let Some(constructor) = constructor { let transfer = constructor.stage0_alice().ok_or_else(|| { CryptError::RekeyUpdateError("Failed to get initial transfer".to_string()) })?; - let serialized = bincode::serialize(&transfer) + let payload = bincode::serialize(&transfer) .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; self.sender .lock() .await - .send(RatchetMessage::AliceToBob(serialized)) + .send(RatchetMessage::AliceToBob { + payload, + earliest_ratchet_version, + latest_ratchet_version, + }) .await .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; log::debug!(target: "citadel", "Client {} sent initial AliceToBob transfer", self.cid); @@ -232,6 +259,7 @@ where self.set_state(RekeyState::Running); let result = self.rekey(&mut listener).await; self.set_state(RekeyState::Idle); + self.set_role(RekeyRole::Idle); let err = result.err(); @@ -241,7 +269,7 @@ where if let Some(err) = err { log::error!("cid {} rekey error: {err:?}", self.cid); - break; + continue; } } }; @@ -253,23 +281,43 @@ where /// once a single re-key occurs. This function is intended to be used in a loop /// to continuously be ready for re-keying. async fn rekey(&self, receiver: &mut I) -> Result<(), CryptError> { + log::trace!(target: "citadel", "Client {} starting rekey with initial role {:?}", self.cid, self.role()); let is_initiator = self.is_initiator; let mut completed_as_leader = false; let mut completed_as_loser = false; loop { let msg = receiver.next().await; - log::debug!(target: "citadel", "Client {} received message {msg:?}", self.cid); + // log::debug!(target: "citadel", "Client {} received message {msg:?}", self.cid); match msg { - Some(RatchetMessage::AliceToBob(transfer_data)) => { - log::debug!("cid {} received AliceToBob", self.cid); - - // Process the AliceToBob message as Bob - let transfer = bincode::deserialize(&transfer_data) - .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; - + Some(RatchetMessage::AliceToBob { + payload, + earliest_ratchet_version, + latest_ratchet_version, + }) => { let status = { + log::debug!(target: "citadel", "Client {} received AliceToBob", self.cid); let mut container = self.container.write(); + + // Process the AliceToBob message as Bob + let transfer = bincode::deserialize(&payload) + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; + + let cid = container.toolset.cid; + let local_earliest_ratchet_version = + container.toolset.get_oldest_stacked_ratchet_version(); + let local_latest_ratchet_version = container.latest_usable_version; + + if earliest_ratchet_version != local_earliest_ratchet_version { + log::warn!(target: "citadel", "Client {cid}: Earliest declared ratchet versions do not match. Local: {local_earliest_ratchet_version}, Peer: {earliest_ratchet_version}"); + continue; + } + + if latest_ratchet_version != local_latest_ratchet_version { + log::warn!(target: "citadel", "Client {cid}: Latest usable ratchet versions do not match. Local: {local_latest_ratchet_version}, Peer: {latest_ratchet_version}"); + continue; + } + // Get next_opts from the container let next_opts = container .get_ratchet(None) @@ -291,12 +339,7 @@ where ) })?; - container.update_sync_safe( - bob_constructor, - self.role() != RekeyRole::Loser, - self.cid, - false, - )? + container.update_sync_safe(bob_constructor, false)? }; match status { @@ -304,13 +347,15 @@ where let serialized = bincode::serialize(&transfer) .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + log::trace!(target: "citadel", "Client {} must send BobToAlice", self.cid); + { let container = self.container.write(); let _ = container.update_in_progress.toggle_on_if_untoggled(); + self.set_role(RekeyRole::Loser); drop(container); } - self.set_role(RekeyRole::Loser); self.sender .lock() .await @@ -321,32 +366,37 @@ where target: "citadel", "Client {} is {:?}. Sent BobToAlice", self.cid, - RekeyRole::Loser + self.role(), ); } KemTransferStatus::Contended => { // The package that we received did not result in a re-key. OUR package will result in a re-key. // Therefore, we will wait for the adjacent node to drive us to completion so we both have the same ratchet self.set_role(RekeyRole::Leader); - log::debug!("cid {} is {:?}. contention detected. We will wait for the adjacent node to drive us to completion", self.cid, RekeyRole::Leader); + log::debug!(target: "citadel", "[Contention] Client {} is {:?}. contention detected. We will wait for the adjacent node to drive us to completion", self.cid, RekeyRole::Leader); } _ => { log::warn!( + target: "citadel", "cid {} unexpected status for AliceToBob Transfer: {status:?}", self.cid ); } } } + Some(RatchetMessage::BobToAlice(transfer_data, sender_role)) => { + log::debug!(target: "citadel", "Client {} received BobToAlice", self.cid); // If the sender became a Loser, they expect us to be Leader - if sender_role == RekeyRole::Loser && self.role() != RekeyRole::Leader { + let initial_role = self.role(); + if sender_role == RekeyRole::Loser && initial_role != RekeyRole::Leader { + self.set_role(RekeyRole::Leader); + log::trace!(target: "citadel", "cid {} changing role from {:?} to {:?}", self.cid, initial_role, RekeyRole::Leader); log::debug!( target: "citadel", - "Client {} transitioning to Leader as peer became Loser before we were able to transition", + "Client {} transitioning from {initial_role:?} to Leader as peer became Loser before we were able to transition", self.cid ); - self.set_role(RekeyRole::Leader); } // Now verify we're in a valid state to process the message @@ -357,9 +407,12 @@ where ))); } + if initial_role == RekeyRole::Idle { + log::warn!(target: "citadel", "Initial role was idle. Their role: {sender_role:?}"); + } + let mut constructor = { self.constructor.lock().take() }; - log::debug!(target: "citadel", "Client {} received BobToAlice", self.cid); if let Some(mut alice_constructor) = constructor.take() { let transfer = bincode::deserialize(&transfer_data).map_err(|e| { CryptError::RekeyUpdateError(format!( @@ -369,12 +422,9 @@ where alice_constructor.stage1_alice(transfer, &self.psks)?; let status = { - self.container.write().update_sync_safe( - alice_constructor, - false, - self.cid, - true, - )? + self.container + .write() + .update_sync_safe(alice_constructor, true)? }; let truncation_required = status.requires_truncation(); @@ -402,7 +452,7 @@ where })?; // We need to wait to be marked as complete } else { - // Send TruncateAck to Bob so he can finish + // Send LoserCanFinish to Bob so he can finish self.sender .lock() .await @@ -424,81 +474,112 @@ where } Some(RatchetMessage::Truncate(version_to_truncate)) => { + let role = self.role(); // Allow Loser if contention, or Idle if no contention - if self.role() == RekeyRole::Leader { + log::debug!(target: "citadel", "Client {} received Truncate", self.cid); + if role != RekeyRole::Loser { return Err(CryptError::RekeyUpdateError(format!( - "Unexpected Truncate message since our role is not Bob, but {:?}", - self.role() + "Unexpected Truncate message since our role is not Loser, but {:?}", + role ))); } - log::debug!(target: "citadel", "Client {} received Truncate", self.cid); - - { + let latest_version = { let mut container = self.container.write(); container.deregister_oldest_stacked_ratchet(version_to_truncate)?; - container.post_alice_stage1_or_post_stage1_bob(); - let _ = container.maybe_unlock(false); - } + let latest_actual_ratchet_version = container + .maybe_unlock() + .expect("Failed to fetch ratchet") + .version(); + let latest_version = container.latest_usable_version; + if latest_actual_ratchet_version != latest_version { + log::warn!(target:"citadel", "Client {} received Truncate, but, update failed. Actual: {latest_actual_ratchet_version}, Expected: {latest_version} ", self.cid); + } + latest_version + }; completed_as_loser = true; self.sender .lock() .await - .send(RatchetMessage::LeaderCanFinish) + .send(RatchetMessage::LeaderCanFinish { latest_version }) .await .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; break; } - Some(RatchetMessage::LeaderCanFinish) => { - // Allow Leader if contention, or Idle if no contention - if self.role() == RekeyRole::Loser { - return Err(CryptError::RekeyUpdateError(format!( - "Unexpected AliceCanFinish message since our role is not Bob, but {:?}", - self.role() - ))); - } - log::debug!(target: "citadel", "Client {} received LeaderCanFinish", self.cid); - - { - let mut container = self.container.write(); - container.post_alice_stage1_or_post_stage1_bob(); - let _ = container.maybe_unlock(false); - } - - completed_as_leader = true; - break; - } Some(RatchetMessage::LoserCanFinish) => { // Allow Loser if contention, or Idle if no contention - if self.role() == RekeyRole::Leader { + let role = self.role(); + if role != RekeyRole::Loser { return Err(CryptError::RekeyUpdateError( - format!("Unexpected LoserCanFinish message since our role is not Loser, but {:?}", self.role()) + format!("Unexpected LoserCanFinish message since our role is not Loser, but {:?}", role) )); } log::debug!(target: "citadel", "Client {} received LoserCanFinish", self.cid); - { + let latest_version = { let mut container = self.container.write(); container.post_alice_stage1_or_post_stage1_bob(); - let _ = container.maybe_unlock(false); - } + let latest_actual_ratchet_version = container + .maybe_unlock() + .expect("Failed to fetch ratchet") + .version(); + let latest_version = container.latest_usable_version; + if latest_actual_ratchet_version != latest_version { + log::warn!(target:"citadel", "Client {} received LoserCanFinish but, update failed. Actual: {latest_actual_ratchet_version}, Expected: {latest_version} ", self.cid); + } + latest_version + }; + completed_as_loser = true; // Send a LeaderCanFinish to unlock them self.sender .lock() .await - .send(RatchetMessage::LeaderCanFinish) + .send(RatchetMessage::LeaderCanFinish { latest_version }) .await .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; break; } + Some(RatchetMessage::LeaderCanFinish { latest_version }) => { + let our_latest_version = { + let container = self.container.read(); + container.latest_usable_version + }; + + log::debug!("Client {} received LeaderCanFinish w/ latest_version = {latest_version} | our latest version: {our_latest_version}", self.cid); + // Allow Leader if contention, or Idle if no contention + let role = self.role(); + if role != RekeyRole::Leader { + return Err(CryptError::RekeyUpdateError(format!( + "Unexpected AliceCanFinish message since our role is not Leader, but {:?}", + role + ))); + } + + { + let mut container = self.container.write(); + container.post_alice_stage1_or_post_stage1_bob(); + let latest_actual_ratchet_version = container + .maybe_unlock() + .expect("Failed to fetch ratchet") + .version(); + let latest_declared_version = container.latest_usable_version; + if latest_actual_ratchet_version != latest_declared_version { + log::warn!(target:"citadel", "Client {} received Truncate, desynced. Actual: {latest_actual_ratchet_version}, Expected: {latest_declared_version} ", self.cid); + } + } + + completed_as_leader = true; + break; + } + None => { return Err(CryptError::RekeyUpdateError( "Unexpected end of stream".to_string(), @@ -509,20 +590,15 @@ where log::debug!( target: "citadel", - "Client {} completed re-key. Alice: {}, Bob: {}. Final version: {}. Is initiator: {}", + "Client {} completed re-key. Alice: {}, Bob: {}. Final version: {}. Final Declared Version: {}. Is initiator: {}", self.cid, completed_as_leader, completed_as_loser, self.get_ratchet(None).unwrap().version(), + self.container.read().latest_usable_version, is_initiator ); - debug_assert_eq!( - completed_as_leader, !is_initiator, - "Client {} completed wrong role. Is initiator: {is_initiator}, Completed as Leader: {completed_as_leader}", - self.cid - ); - log::debug!(target: "citadel", "*** cid {} rekey completed", self.cid); Ok(()) @@ -537,7 +613,8 @@ where } fn set_role(&self, role: RekeyRole) { - self.role.store(role, Ordering::Relaxed); + log::trace!(target: "citadel", "Client {} changing role from {:?} to {:?}", self.cid, self.role(), role); + self.role.store(role, Ordering::SeqCst); } pub fn state(&self) -> RekeyState { @@ -550,16 +627,100 @@ where } #[cfg(test)] -mod racy { - use crate::endpoint_crypto_container::no_race::TEST_PSKS; - use crate::ratchet_manager::{RatchetManager, RatchetMessage, RekeyRole}; - use crate::stacked_ratchet::Ratchet; +mod tests { + use crate::endpoint_crypto_container::{EndpointRatchetConstructor, PeerSessionCrypto}; + use crate::prelude::Toolset; + use crate::ratchet_manager::{RatchetManager, RatchetMessage}; + use crate::ratchets::stacked::stacked_ratchet::StackedRatchet; + use crate::ratchets::Ratchet; use citadel_io::tokio; + use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_types::prelude::{EncryptionAlgorithm, KemAlgorithm, SecurityLevel}; + use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures::{Sink, Stream}; use rstest::rstest; use std::time::Duration; + const ALICE_CID: u64 = 10; + const BOB_CID: u64 = 20; + pub const TEST_PSKS: &[&[u8]] = &[b"test_psk_1", b"test_psk_2"]; + const START_VERSION: u32 = 0; + + fn gen>( + version: u32, + opts: Vec, + psks: &[T], + ) -> (R, R) { + let mut cx_alice = R::Constructor::new_alice(opts.clone(), ALICE_CID, version).unwrap(); + let mut cx_bob = + R::Constructor::new_bob(BOB_CID, opts, cx_alice.stage0_alice().unwrap(), psks).unwrap(); + cx_alice + .stage1_alice(cx_bob.stage0_bob().unwrap(), psks) + .unwrap(); + + (cx_alice.finish().unwrap(), cx_bob.finish().unwrap()) + } + + pub(crate) fn setup_endpoint_containers( + security_level: SecurityLevel, + enx: EncryptionAlgorithm, + kem: KemAlgorithm, + ) -> (PeerSessionCrypto, PeerSessionCrypto) { + let opts = ConstructorOpts::new_vec_init(Some(enx + kem), security_level); + let (hr_alice, hr_bob) = gen::(START_VERSION, opts, TEST_PSKS); + assert_eq!(hr_alice.version(), START_VERSION); + assert_eq!(hr_bob.version(), START_VERSION); + assert_eq!(hr_alice.get_cid(), ALICE_CID); + assert_eq!(hr_bob.get_cid(), BOB_CID); + let alice_container = PeerSessionCrypto::new(Toolset::new(ALICE_CID, hr_alice), true); + let bob_container = PeerSessionCrypto::new(Toolset::new(BOB_CID, hr_bob), false); + (alice_container, bob_container) + } + + type TestRatchetManager = + RatchetManager, UnboundedReceiver, R>; + + fn create_ratchet_managers() -> (TestRatchetManager, TestRatchetManager) { + let security_level = SecurityLevel::Standard; + + let (alice_container, bob_container) = setup_endpoint_containers::( + security_level, + EncryptionAlgorithm::AES_GCM_256, + KemAlgorithm::Kyber, + ); + + let (tx_alice, rx_bob) = futures::channel::mpsc::unbounded(); + let (tx_bob, rx_alice) = futures::channel::mpsc::unbounded(); + + let alice_manager = RatchetManager::new(tx_alice, rx_alice, alice_container, TEST_PSKS); + let bob_manager = RatchetManager::new(tx_bob, rx_bob, bob_container, TEST_PSKS); + (alice_manager, bob_manager) + } + + pub(crate) fn pre_round_assertions( + alice_container: &PeerSessionCrypto, + alice_cid: u64, + bob_container: &PeerSessionCrypto, + bob_cid: u64, + ) -> (u32, u32) { + assert_eq!( + alice_container.get_ratchet(None).unwrap().get_cid(), + alice_cid + ); + assert_eq!(bob_container.get_ratchet(None).unwrap().get_cid(), bob_cid); + + let start_version = alice_container + .toolset + .get_most_recent_stacked_ratchet_version(); + let new_version = start_version + 1; + let new_version_bob = bob_container + .toolset + .get_most_recent_stacked_ratchet_version() + + 1; + assert_eq!(new_version, new_version_bob); + (start_version, new_version) + } + async fn run_round_racy< S: Sink + Unpin + Send + 'static, I: Stream + Unpin + Send + 'static, @@ -574,15 +735,14 @@ mod racy { let cid_0 = container_0.cid; let cid_1 = container_1.cid; - let (_start_version, _next_version) = - crate::endpoint_crypto_container::no_race::pre_round_assertions( - &*container_0.container.read(), - cid_0, - &*container_1.container.read(), - cid_1, - ); + let (_start_version, _next_version) = pre_round_assertions( + &*container_0.container.read(), + cid_0, + &*container_1.container.read(), + cid_1, + ); - let task = |mut container: RatchetManager, delay: Option| async move { + let task = |container: RatchetManager, delay: Option| async move { if let Some(delay) = delay { tokio::time::sleep(delay).await; } @@ -602,6 +762,7 @@ mod racy { (None, None) }; + log::info!(target: "citadel", "~~~~ Beginning next round! ~~~~"); // Spawn Alice's task let alice_handle = tokio::spawn(task(container_0.clone(), delay_0)); @@ -611,21 +772,113 @@ mod racy { // Wait for both tasks to complete let (alice_result, bob_result) = tokio::join!(alice_handle, bob_handle); + // Update original containers with final state + let _rekey_0_res = alice_result.unwrap().unwrap(); + let _rekey_1_res = bob_result.unwrap().unwrap(); + + post_checks(&container_0, &container_1); + log::info!(target: "citadel", "~~~~ Round ended! ~~~~"); + } + + async fn run_round_one_node_only< + S: Sink + Unpin + Send + 'static, + I: Stream + Unpin + Send + 'static, + R: Ratchet, + >( + container_0: RatchetManager, + container_1: RatchetManager, + ) where + >::Error: std::fmt::Debug, + { + let cid_0 = container_0.cid; + let cid_1 = container_1.cid; + + let (_start_version, _next_version) = pre_round_assertions( + &*container_0.container.read(), + cid_0, + &*container_1.container.read(), + cid_1, + ); + + let task = |container: RatchetManager, skip: bool| async move { + if skip { + return Ok(false); + } + let res = container.trigger_rekey().await; + log::debug!(target: "citadel", "*** [FINISHED] Client {} rekey result: {res:?}", container.cid); + res + }; + + // Randomly assign a delay to Alice or Bob, if applicable + let (alice_skips, bob_skips) = { + if rand::random::() % 2 == 0 { + (true, false) + } else { + (false, true) + } + }; + + // Spawn Alice's task + let alice_handle = tokio::spawn(task(container_0.clone(), alice_skips)); + + // Spawn Bob's task + let bob_handle = tokio::spawn(task(container_1.clone(), bob_skips)); + + // Wait for both tasks to complete + let (alice_result, bob_result) = tokio::join!(alice_handle, bob_handle); + // Update original containers with final state let rekey_0_res = alice_result.unwrap().unwrap(); let rekey_1_res = bob_result.unwrap().unwrap(); + assert_eq!(rekey_0_res, !alice_skips); + assert_eq!(rekey_1_res, !bob_skips); + + post_checks(&container_0, &container_1); + } + + pub(crate) fn ratchet_encrypt_decrypt_test( + container_0: &PeerSessionCrypto, + cid_0: u64, + container_1: &PeerSessionCrypto, + cid_1: u64, + expected_version: u32, + ) { + let test_message = b"Hello, World!"; + let alice_ratchet = container_0.get_ratchet(None).unwrap(); + assert_eq!(alice_ratchet.version(), expected_version); + assert_eq!(alice_ratchet.get_cid(), cid_0); + let encrypted = alice_ratchet.encrypt(test_message).unwrap(); - assert!(rekey_0_res, "Alice failed to rekey"); - assert!(rekey_1_res, "Bob failed to rekey"); + let bob_ratchet = container_1.get_ratchet(None).unwrap(); + assert_eq!(bob_ratchet.version(), expected_version); + assert_eq!(bob_ratchet.get_cid(), cid_1); + let decrypted = bob_ratchet.decrypt(&encrypted).unwrap(); + assert_eq!(test_message.to_vec(), decrypted); + } + fn post_checks< + S: Sink + Unpin + Send + 'static, + I: Stream + Unpin + Send + 'static, + R: Ratchet, + >( + container_0: &RatchetManager, + container_1: &RatchetManager, + ) where + >::Error: std::fmt::Debug, + { // Verify final state + let cid_0 = container_0.cid; + let cid_1 = container_1.cid; + let alice_declared_latest_version = container_0.container.read().latest_usable_version; + let bob_declared_latest_version = container_1.container.read().latest_usable_version; + assert_eq!(alice_declared_latest_version, bob_declared_latest_version); let alice_ratchet = container_0.get_ratchet(None).unwrap(); let bob_ratchet = container_1.get_ratchet(None).unwrap(); assert_eq!(alice_ratchet.version(), bob_ratchet.version()); let alice_ratchet_version = alice_ratchet.version(); - crate::endpoint_crypto_container::no_race::ratchet_encrypt_decrypt_test( + ratchet_encrypt_decrypt_test( &*container_0.container.read(), cid_0, &*container_1.container.read(), @@ -635,77 +888,60 @@ mod racy { } #[rstest] - #[timeout(std::time::Duration::from_secs(30))] + #[timeout(std::time::Duration::from_secs(60))] #[tokio::test] - async fn test_endpoint_container_racy_basic() { + async fn test_ratchet_manager_racy_contentious() { citadel_logging::setup_log(); - let security_level = SecurityLevel::Standard; - - let (alice_container, bob_container) = - crate::endpoint_crypto_container::no_race::setup_endpoint_containers( - security_level, - EncryptionAlgorithm::AES_GCM_256, - KemAlgorithm::Kyber, - ); - - let (tx_alice, rx_bob) = futures::channel::mpsc::unbounded(); - let (tx_bob, rx_alice) = futures::channel::mpsc::unbounded(); - - let alice_manager = RatchetManager::new(tx_alice, rx_alice, alice_container, TEST_PSKS); - let bob_manager = RatchetManager::new(tx_bob, rx_bob, bob_container, TEST_PSKS); - + let (alice_manager, bob_manager) = create_ratchet_managers::(); const ROUNDS: usize = 100; for _ in 0..ROUNDS { run_round_racy(alice_manager.clone(), bob_manager.clone(), None).await; } + + assert_eq!( + alice_manager.container.read().latest_usable_version, + ROUNDS as u32 + ); + assert_eq!( + bob_manager.container.read().latest_usable_version, + ROUNDS as u32 + ); } #[rstest] - #[timeout(std::time::Duration::from_secs(30))] + #[timeout(std::time::Duration::from_secs(360))] #[tokio::test(flavor = "multi_thread")] - async fn test_endpoint_container_racy_with_random_start_lag() { + async fn test_ratchet_manager_racy_with_random_start_lag( + #[values(0, 1, 10, 100, 500)] min_delay: u64, + ) { citadel_logging::setup_log(); - let security_level = SecurityLevel::Standard; - - let (alice_container, bob_container) = - crate::endpoint_crypto_container::no_race::setup_endpoint_containers( - security_level, - EncryptionAlgorithm::AES_GCM_256, - KemAlgorithm::Kyber, - ); - - let (tx_alice, rx_bob) = futures::channel::mpsc::unbounded(); - let (tx_bob, rx_alice) = futures::channel::mpsc::unbounded(); - - let alice_manager = RatchetManager::new(tx_alice, rx_alice, alice_container, TEST_PSKS); - let bob_manager = RatchetManager::new(tx_bob, rx_bob, bob_container, TEST_PSKS); - - let mut initiator_leader_count = 0; - let mut non_initiator_leader_count = 0; - + let (alice_manager, bob_manager) = create_ratchet_managers::(); const ROUNDS: usize = 100; for _ in 0..ROUNDS { - let delay = rand::random::() % 100; - let delay = Duration::from_millis(delay as u64); + let delay = rand::random::() % 5; + let delay = Duration::from_millis(min_delay + delay); run_round_racy(alice_manager.clone(), bob_manager.clone(), Some(delay)).await; + } + } - if alice_manager.role() == RekeyRole::Leader { - if alice_manager.is_initiator { - initiator_leader_count += 1; - } else { - non_initiator_leader_count += 1; - } - } - - if bob_manager.role() == RekeyRole::Leader { - if bob_manager.is_initiator { - initiator_leader_count += 1; - } else { - non_initiator_leader_count += 1; - } - } + #[rstest] + #[timeout(std::time::Duration::from_secs(60))] + #[tokio::test(flavor = "multi_thread")] + async fn test_ratchet_manager_one_at_a_time() { + citadel_logging::setup_log(); + let (alice_manager, bob_manager) = create_ratchet_managers::(); + const ROUNDS: usize = 100; + for _ in 0..ROUNDS { + run_round_one_node_only(alice_manager.clone(), bob_manager.clone()).await; } - log::info!("initiator_leader_count: {initiator_leader_count}, non_initiator_leader_count: {non_initiator_leader_count}"); + assert_eq!( + alice_manager.container.read().latest_usable_version, + ROUNDS as u32 + ); + assert_eq!( + bob_manager.container.read().latest_usable_version, + ROUNDS as u32 + ); } } diff --git a/citadel_crypt/src/ratchets/mod.rs b/citadel_crypt/src/ratchets/mod.rs new file mode 100644 index 000000000..2153649fc --- /dev/null +++ b/citadel_crypt/src/ratchets/mod.rs @@ -0,0 +1,204 @@ +use crate::endpoint_crypto_container::EndpointRatchetConstructor; +use crate::entropy_bank::EntropyBank; +use crate::misc::CryptError; +use bytes::BytesMut; +use citadel_pqcrypto::bytes_in_place::EzBuffer; +use citadel_pqcrypto::constructor_opts::ConstructorOpts; +use citadel_pqcrypto::PostQuantumContainer; +use citadel_types::crypto::SecurityLevel; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; + +pub mod mono; +pub mod stacked; + +/// For allowing registration inside the toolset +pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + 'static { + type Constructor: EndpointRatchetConstructor + Serialize + for<'a> Deserialize<'a>; + + /// Returns the client ID + fn get_cid(&self) -> u64 { + self.get_message_pqc_and_entropy_bank_at_layer(None) + .expect("StackedRatchet::get_cid") + .1 + .cid + } + + /// Returns the version + fn version(&self) -> u32 { + self.get_message_pqc_and_entropy_bank_at_layer(None) + .expect("StackedRatchet::version") + .1 + .version + } + + /// Determines if any of the ratchets have verified packets + fn has_verified_packets(&self) -> bool { + let max = self.message_ratchet_count(); + for n in 0..max { + if let Ok((pqc, _entropy_bank)) = + self.get_message_pqc_and_entropy_bank_at_layer(Some(n)) + { + if pqc.has_verified_packets() { + return true; + } + } + } + + self.get_scramble_pqc_and_entropy_bank() + .0 + .has_verified_packets() + } + + /// Resets the anti-replay attack counters + fn reset_ara(&self) { + let max = self.message_ratchet_count(); + for n in 0..max { + if let Ok((pqc, _entropy_bank)) = + self.get_message_pqc_and_entropy_bank_at_layer(Some(n)) + { + pqc.reset_counters(); + } + } + + self.get_scramble_pqc_and_entropy_bank().0.reset_counters() + } + + /// Returns the default security level + fn get_default_security_level(&self) -> SecurityLevel; + + /// Returns the message PQC and entropy_bank for the specified index + fn get_message_pqc_and_entropy_bank_at_layer( + &self, + idx: Option, + ) -> Result<(&PostQuantumContainer, &EntropyBank), CryptError>; + + /// Returns the scramble entropy_bank + fn get_scramble_pqc_and_entropy_bank(&self) -> (&PostQuantumContainer, &EntropyBank); + + /// Returns the next constructor options + fn get_next_constructor_opts(&self) -> Vec; + + /// Protects a message packet using the entire ratchet's security features + fn protect_message_packet( + &self, + security_level: Option, + header_len_bytes: usize, + packet: &mut T, + ) -> Result<(), CryptError> { + let idx = self.verify_level(security_level)?; + + for n in 0..=idx { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + entropy_bank.protect_packet(pqc, header_len_bytes, packet)?; + } + + Ok(()) + } + + /// Validates a message packet using the entire ratchet's security features + fn validate_message_packet, T: EzBuffer>( + &self, + security_level: Option, + header: H, + packet: &mut T, + ) -> Result<(), CryptError> { + let idx = self.verify_level(security_level)?; + for n in (0..=idx).rev() { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + entropy_bank.validate_packet_in_place_split(pqc, &header, packet)?; + } + + Ok(()) + } + + /// Returns the next Alice constructor + fn next_alice_constructor(&self) -> Option { + Self::Constructor::new_alice( + self.get_next_constructor_opts(), + self.get_cid(), + self.version().wrapping_add(1), + ) + } + + /// Encrypts using a local key that is not shared with anyone. Relevant for RE-VFS + fn local_encrypt<'a, T: Into>>( + &self, + contents: T, + security_level: SecurityLevel, + ) -> Result, CryptError> { + let idx = self.verify_level(Some(security_level))?; + let mut data = contents.into(); + for n in 0..=idx { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + data = Cow::Owned(entropy_bank.local_encrypt(pqc, &data)?); + } + + Ok(data.into_owned()) + } + + /// Decrypts using a local key that is not shared with anyone. Relevant for RE-VFS + fn local_decrypt<'a, T: Into>>( + &self, + contents: T, + security_level: SecurityLevel, + ) -> Result, CryptError> { + let mut data = contents.into(); + if data.is_empty() { + return Ok(vec![]); + } + + let idx = self.verify_level(Some(security_level))?; + for n in (0..=idx).rev() { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + data = Cow::Owned(entropy_bank.local_decrypt(pqc, &data)?); + } + + Ok(data.into_owned()) + } + + fn message_ratchet_count(&self) -> usize; + + /// Verifies the target security level, returning the corresponding idx + fn verify_level( + &self, + security_level: Option, + ) -> Result> { + let security_level = security_level.unwrap_or(SecurityLevel::Standard); + let message_ratchet_count = self.message_ratchet_count(); + if security_level.value() as usize >= message_ratchet_count { + log::warn!(target: "citadel", "OOB: Security value: {}, max: {} (default: {:?})|| Version: {}", security_level.value() as usize, message_ratchet_count- 1, self.get_default_security_level(), self.version()); + Err(CryptError::OutOfBoundsError) + } else { + Ok(security_level.value() as usize) + } + } + + /// Validates in-place when the header + payload have already been split + fn validate_message_packet_in_place_split>( + &self, + security_level: Option, + header: H, + packet: &mut BytesMut, + ) -> Result<(), CryptError> { + let idx = self.verify_level(security_level)?; + for n in (0..=idx).rev() { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; + entropy_bank.validate_packet_in_place_split(pqc, &header, packet)?; + } + + Ok(()) + } + + /// decrypts using a custom nonce configuration + fn decrypt>(&self, contents: T) -> Result, CryptError> { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; + entropy_bank.decrypt(pqc, contents) + } + + /// Encrypts the data into a Vec + fn encrypt>(&self, contents: T) -> Result, CryptError> { + let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; + entropy_bank.encrypt(pqc, contents) + } +} diff --git a/citadel_crypt/src/fcm/keys.rs b/citadel_crypt/src/ratchets/mono/keys.rs similarity index 98% rename from citadel_crypt/src/fcm/keys.rs rename to citadel_crypt/src/ratchets/mono/keys.rs index be29dfb08..2fd01cde5 100644 --- a/citadel_crypt/src/fcm/keys.rs +++ b/citadel_crypt/src/ratchets/mono/keys.rs @@ -13,7 +13,7 @@ //! //! ## Usage Example //! ```rust -//! use citadel_crypt::fcm::keys::FcmKeys; +//! use citadel_crypt::ratchets::keys::FcmKeys; //! //! // Create new FCM keys //! let keys = FcmKeys::new( diff --git a/citadel_crypt/src/fcm/mod.rs b/citadel_crypt/src/ratchets/mono/mod.rs similarity index 77% rename from citadel_crypt/src/fcm/mod.rs rename to citadel_crypt/src/ratchets/mono/mod.rs index 9be161df2..476c21ab4 100644 --- a/citadel_crypt/src/fcm/mod.rs +++ b/citadel_crypt/src/ratchets/mono/mod.rs @@ -1,3 +1,2 @@ -/// opts pub mod keys; pub mod ratchet; diff --git a/citadel_crypt/src/fcm/ratchet.rs b/citadel_crypt/src/ratchets/mono/ratchet.rs similarity index 78% rename from citadel_crypt/src/fcm/ratchet.rs rename to citadel_crypt/src/ratchets/mono/ratchet.rs index 8bbf67915..46a3a77d1 100644 --- a/citadel_crypt/src/fcm/ratchet.rs +++ b/citadel_crypt/src/ratchets/mono/ratchet.rs @@ -14,7 +14,7 @@ //! //! ## Usage Example //! ```rust -//! use citadel_crypt::fcm::fcm_ratchet::{ThinRatchet, ThinRatchetConstructor}; +//! use citadel_crypt::ratchets::fcm_ratchet::{ThinRatchet, ThinRatchetConstructor}; //! use citadel_crypt::stacked_ratchet::Ratchet; //! use citadel_pqcrypto::constructor_opts::ConstructorOpts; //! use citadel_types::crypto::SecurityLevel; @@ -50,14 +50,14 @@ //! - [`FcmKeys`](super::keys::FcmKeys): FCM credential management //! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Entropy source //! - [`PostQuantumContainer`](citadel_pqcrypto::PostQuantumContainer): PQ crypto operations -//! - [`Ratchet`](crate::stacked_ratchet::Ratchet): Base ratchet trait +//! - [`Ratchet`](crate::ratchets::Ratchet): Base ratchet trait use crate::endpoint_crypto_container::{ AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, }; use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; -use crate::stacked_ratchet::Ratchet; +use crate::ratchets::Ratchet; use arrayvec::ArrayVec; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_pqcrypto::wire::{AliceToBobTransferParameters, BobToAliceTransferParameters}; @@ -70,33 +70,19 @@ use std::fmt::Debug; use std::sync::Arc; #[derive(Clone, Serialize, Deserialize)] -/// A compact ratchet meant for thin protocol messages -pub struct ThinRatchet { - inner: Arc, -} - -impl ThinRatchet { - /// decrypts using a custom nonce configuration - pub fn decrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; - entropy_bank.decrypt(pqc, contents) - } - - /// Encrypts the data into a Vec - pub fn encrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; - entropy_bank.encrypt(pqc, contents) - } +/// A compact ratchet meant for protocols that require smaller payloads +pub struct MonoRatchet { + inner: Arc, } #[derive(Serialize, Deserialize)] -pub struct ThinRatchetInner { +pub struct MonoRatchetInner { entropy_bank: EntropyBank, pqc: PostQuantumContainer, } -impl Ratchet for ThinRatchet { - type Constructor = ThinRatchetConstructor; +impl Ratchet for MonoRatchet { + type Constructor = MonoRatchetConstructor; fn get_default_security_level(&self) -> SecurityLevel { SecurityLevel::Standard @@ -135,7 +121,7 @@ impl Ratchet for ThinRatchet { /// Used for constructing the ratchet #[derive(Serialize, Deserialize)] -pub struct ThinRatchetConstructor { +pub struct MonoRatchetConstructor { params: CryptoParameters, pqc: PostQuantumContainer, entropy_bank: Option, @@ -144,7 +130,7 @@ pub struct ThinRatchetConstructor { version: u32, } -impl Debug for ThinRatchetConstructor { +impl Debug for MonoRatchetConstructor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ThinRatchetConstructor") .field("params", &self.params) @@ -154,12 +140,12 @@ impl Debug for ThinRatchetConstructor { } } -impl EndpointRatchetConstructor for ThinRatchetConstructor { - type AliceToBobWireTransfer = FcmAliceToBobTransfer; - type BobToAliceWireTransfer = FcmBobToAliceTransfer; +impl EndpointRatchetConstructor for MonoRatchetConstructor { + type AliceToBobWireTransfer = MonoAliceToBobTransfer; + type BobToAliceWireTransfer = MonoBobToAliceTransfer; fn new_alice(opts: Vec, cid: u64, new_version: u32) -> Option { - ThinRatchetConstructor::new_alice_constructor(cid, new_version, opts.into_iter().next()?) + MonoRatchetConstructor::new_alice_constructor(cid, new_version, opts.into_iter().next()?) } fn new_bob>( @@ -168,7 +154,7 @@ impl EndpointRatchetConstructor for ThinRatchetConstructor { transfer: Self::AliceToBobWireTransfer, psks: &[T], ) -> Option { - ThinRatchetConstructor::new_bob(opts.into_iter().next()?, transfer, psks) + MonoRatchetConstructor::new_bob(opts.into_iter().next()?, transfer, psks) } fn stage0_alice(&self) -> Option { @@ -191,17 +177,17 @@ impl EndpointRatchetConstructor for ThinRatchetConstructor { self.update_version(version) } - fn finish_with_custom_cid(self, cid: u64) -> Option { + fn finish_with_custom_cid(self, cid: u64) -> Option { self.finish_with_custom_cid(cid) } - fn finish(self) -> Option { + fn finish(self) -> Option { self.finish() } } #[derive(Serialize, Deserialize)] -pub struct FcmAliceToBobTransfer { +pub struct MonoAliceToBobTransfer { transfer_params: AliceToBobTransferParameters, pub params: CryptoParameters, nonce: ArrayVec, @@ -211,31 +197,31 @@ pub struct FcmAliceToBobTransfer { pub version: u32, } -impl AssociatedSecurityLevel for FcmAliceToBobTransfer { +impl AssociatedSecurityLevel for MonoAliceToBobTransfer { fn security_level(&self) -> SecurityLevel { SecurityLevel::Standard } } -impl AssociatedCryptoParams for FcmAliceToBobTransfer { +impl AssociatedCryptoParams for MonoAliceToBobTransfer { fn crypto_params(&self) -> CryptoParameters { self.params } } #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct FcmBobToAliceTransfer { +pub struct MonoBobToAliceTransfer { params_tx: BobToAliceTransferParameters, encrypted_entropy_bank_bytes: Vec, } -impl AssociatedSecurityLevel for FcmBobToAliceTransfer { +impl AssociatedSecurityLevel for MonoBobToAliceTransfer { fn security_level(&self) -> SecurityLevel { SecurityLevel::Standard } } -impl ThinRatchetConstructor { +impl MonoRatchetConstructor { /// FCM limits messages to 4Kb, so we need to use firesaber alone pub fn new_alice_constructor(cid: u64, version: u32, opts: ConstructorOpts) -> Option { let params = opts.cryptography.unwrap_or_default(); @@ -253,7 +239,7 @@ impl ThinRatchetConstructor { pub fn new_bob>( opts: ConstructorOpts, - transfer: FcmAliceToBobTransfer, + transfer: MonoAliceToBobTransfer, psks: &[T], ) -> Option { let params = transfer.params; @@ -271,9 +257,9 @@ impl ThinRatchetConstructor { }) } - pub fn stage0_alice(&self) -> Option { + pub fn stage0_alice(&self) -> Option { let pk = self.pqc.generate_alice_to_bob_transfer().ok()?; - Some(FcmAliceToBobTransfer { + Some(MonoAliceToBobTransfer { params: self.params, transfer_params: pk, nonce: self.nonce.clone(), @@ -282,8 +268,8 @@ impl ThinRatchetConstructor { }) } - pub fn stage0_bob(&mut self) -> Option { - Some(FcmBobToAliceTransfer { + pub fn stage0_bob(&mut self) -> Option { + Some(MonoBobToAliceTransfer { params_tx: self.pqc.generate_bob_to_alice_transfer().ok()?, encrypted_entropy_bank_bytes: self .pqc @@ -297,7 +283,7 @@ impl ThinRatchetConstructor { pub fn stage1_alice>( &mut self, - transfer: FcmBobToAliceTransfer, + transfer: MonoBobToAliceTransfer, psks: &[T], ) -> Result<(), CryptError> { self.pqc @@ -318,25 +304,25 @@ impl ThinRatchetConstructor { Some(()) } - pub fn finish_with_custom_cid(mut self, cid: u64) -> Option { + pub fn finish_with_custom_cid(mut self, cid: u64) -> Option { self.cid = cid; self.entropy_bank.as_mut()?.cid = cid; self.finish() } - pub fn finish(self) -> Option { - ThinRatchet::try_from(self).ok() + pub fn finish(self) -> Option { + MonoRatchet::try_from(self).ok() } } -impl TryFrom for ThinRatchet { +impl TryFrom for MonoRatchet { type Error = (); - fn try_from(value: ThinRatchetConstructor) -> Result { + fn try_from(value: MonoRatchetConstructor) -> Result { let entropy_bank = value.entropy_bank.ok_or(())?; let pqc = value.pqc; - let inner = ThinRatchetInner { entropy_bank, pqc }; - Ok(ThinRatchet { + let inner = MonoRatchetInner { entropy_bank, pqc }; + Ok(MonoRatchet { inner: Arc::new(inner), }) } diff --git a/citadel_crypt/src/ratchets/stacked/mod.rs b/citadel_crypt/src/ratchets/stacked/mod.rs new file mode 100644 index 000000000..d22235106 --- /dev/null +++ b/citadel_crypt/src/ratchets/stacked/mod.rs @@ -0,0 +1,2 @@ +/// This is a container for holding the entropy_bank and PQC, and is intended to replace the separate use of the entropy_bank/PQC +pub mod stacked_ratchet; diff --git a/citadel_crypt/src/stacked_ratchet.rs b/citadel_crypt/src/ratchets/stacked/stacked_ratchet.rs similarity index 57% rename from citadel_crypt/src/stacked_ratchet.rs rename to citadel_crypt/src/ratchets/stacked/stacked_ratchet.rs index eade07518..c399c06dc 100644 --- a/citadel_crypt/src/stacked_ratchet.rs +++ b/citadel_crypt/src/ratchets/stacked/stacked_ratchet.rs @@ -59,18 +59,15 @@ //! - [`crate::endpoint_crypto_container`] - Endpoint state management //! -use crate::endpoint_crypto_container::EndpointRatchetConstructor; use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; -use crate::stacked_ratchet::constructor::StackedRatchetConstructor; -use bytes::BytesMut; -use citadel_pqcrypto::bytes_in_place::EzBuffer; +use crate::ratchets::stacked::stacked_ratchet::constructor::StackedRatchetConstructor; +use crate::ratchets::Ratchet; use citadel_pqcrypto::constructor_opts::{ConstructorOpts, RecursiveChain}; use citadel_pqcrypto::PostQuantumContainer; use citadel_types::crypto::SecurityLevel; use serde::{Deserialize, Serialize}; use sha3::Digest; -use std::borrow::Cow; use std::sync::Arc; /// A container meant to establish perfect forward secrecy AND scrambling w/ an independent key @@ -81,197 +78,6 @@ pub struct StackedRatchet { pub(crate) inner: Arc, } -/// For allowing registration inside the toolset -pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + 'static { - type Constructor: EndpointRatchetConstructor + Serialize + for<'a> Deserialize<'a>; - - /// Returns the client ID - fn get_cid(&self) -> u64 { - self.get_message_pqc_and_entropy_bank_at_layer(None) - .expect("StackedRatchet::get_cid") - .1 - .cid - } - - /// Returns the version - fn version(&self) -> u32 { - self.get_message_pqc_and_entropy_bank_at_layer(None) - .expect("StackedRatchet::version") - .1 - .version - } - - /// Determines if any of the ratchets have verified packets - fn has_verified_packets(&self) -> bool { - let max = self.message_ratchet_count(); - for n in 0..max { - if let Ok((pqc, _entropy_bank)) = - self.get_message_pqc_and_entropy_bank_at_layer(Some(n)) - { - if pqc.has_verified_packets() { - return true; - } - } - } - - self.get_scramble_pqc_and_entropy_bank() - .0 - .has_verified_packets() - } - - /// Resets the anti-replay attack counters - fn reset_ara(&self) { - let max = self.message_ratchet_count(); - for n in 0..max { - if let Ok((pqc, _entropy_bank)) = - self.get_message_pqc_and_entropy_bank_at_layer(Some(n)) - { - pqc.reset_counters(); - } - } - - self.get_scramble_pqc_and_entropy_bank().0.reset_counters() - } - - /// Returns the default security level - fn get_default_security_level(&self) -> SecurityLevel; - - /// Returns the message PQC and entropy_bank for the specified index - fn get_message_pqc_and_entropy_bank_at_layer( - &self, - idx: Option, - ) -> Result<(&PostQuantumContainer, &EntropyBank), CryptError>; - - /// Returns the scramble entropy_bank - fn get_scramble_pqc_and_entropy_bank(&self) -> (&PostQuantumContainer, &EntropyBank); - - /// Returns the next constructor options - fn get_next_constructor_opts(&self) -> Vec; - - /// Protects a message packet using the entire ratchet's security features - fn protect_message_packet( - &self, - security_level: Option, - header_len_bytes: usize, - packet: &mut T, - ) -> Result<(), CryptError> { - let idx = self.verify_level(security_level)?; - - for n in 0..=idx { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; - entropy_bank.protect_packet(pqc, header_len_bytes, packet)?; - } - - Ok(()) - } - - /// Validates a message packet using the entire ratchet's security features - fn validate_message_packet, T: EzBuffer>( - &self, - security_level: Option, - header: H, - packet: &mut T, - ) -> Result<(), CryptError> { - let idx = self.verify_level(security_level)?; - for n in (0..=idx).rev() { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; - entropy_bank.validate_packet_in_place_split(pqc, &header, packet)?; - } - - Ok(()) - } - - /// Returns the next Alice constructor - fn next_alice_constructor(&self) -> Option { - Self::Constructor::new_alice( - self.get_next_constructor_opts(), - self.get_cid(), - self.version().wrapping_add(1), - ) - } - - /// Encrypts using a local key that is not shared with anyone. Relevant for RE-VFS - fn local_encrypt<'a, T: Into>>( - &self, - contents: T, - security_level: SecurityLevel, - ) -> Result, CryptError> { - let idx = self.verify_level(Some(security_level))?; - let mut data = contents.into(); - for n in 0..=idx { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; - data = Cow::Owned(entropy_bank.local_encrypt(pqc, &data)?); - } - - Ok(data.into_owned()) - } - - /// Decrypts using a local key that is not shared with anyone. Relevant for RE-VFS - fn local_decrypt<'a, T: Into>>( - &self, - contents: T, - security_level: SecurityLevel, - ) -> Result, CryptError> { - let mut data = contents.into(); - if data.is_empty() { - return Ok(vec![]); - } - - let idx = self.verify_level(Some(security_level))?; - for n in (0..=idx).rev() { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; - data = Cow::Owned(entropy_bank.local_decrypt(pqc, &data)?); - } - - Ok(data.into_owned()) - } - - fn message_ratchet_count(&self) -> usize; - - /// Verifies the target security level, returning the corresponding idx - fn verify_level( - &self, - security_level: Option, - ) -> Result> { - let security_level = security_level.unwrap_or(SecurityLevel::Standard); - let message_ratchet_count = self.message_ratchet_count(); - if security_level.value() as usize >= message_ratchet_count { - log::warn!(target: "citadel", "OOB: Security value: {}, max: {} (default: {:?})|| Version: {}", security_level.value() as usize, message_ratchet_count- 1, self.get_default_security_level(), self.version()); - Err(CryptError::OutOfBoundsError) - } else { - Ok(security_level.value() as usize) - } - } - - /// Validates in-place when the header + payload have already been split - fn validate_message_packet_in_place_split>( - &self, - security_level: Option, - header: H, - packet: &mut BytesMut, - ) -> Result<(), CryptError> { - let idx = self.verify_level(security_level)?; - for n in (0..=idx).rev() { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(Some(n))?; - entropy_bank.validate_packet_in_place_split(pqc, &header, packet)?; - } - - Ok(()) - } - - /// decrypts using a custom nonce configuration - fn decrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; - entropy_bank.decrypt(pqc, contents) - } - - /// Encrypts the data into a Vec - fn encrypt>(&self, contents: T) -> Result, CryptError> { - let (pqc, entropy_bank) = self.get_message_pqc_and_entropy_bank_at_layer(None)?; - entropy_bank.encrypt(pqc, contents) - } -} - impl Ratchet for StackedRatchet { type Constructor = StackedRatchetConstructor; @@ -370,10 +176,8 @@ pub mod constructor { }; use crate::entropy_bank::EntropyBank; use crate::prelude::CryptError; - use crate::stacked_ratchet::StackedRatchet; + use crate::ratchets::stacked::stacked_ratchet::StackedRatchet; use arrayvec::ArrayVec; - use bytes::BufMut; - use bytes::BytesMut; use citadel_pqcrypto::constructor_opts::{ConstructorOpts, ImpliedSecurityLevel}; use citadel_pqcrypto::wire::{AliceToBobTransferParameters, BobToAliceTransferParameters}; use citadel_pqcrypto::PostQuantumContainer; @@ -386,8 +190,8 @@ pub mod constructor { /// Used during the key exchange process #[derive(Serialize, Deserialize)] pub struct StackedRatchetConstructor { - pub(super) message: MessageRatchetConstructor, - pub(super) scramble: ScrambleRatchetConstructor, + pub(crate) message: MessageRatchetConstructor, + pub(crate) scramble: ScrambleRatchetConstructor, nonce_message: ArrayVec, nonce_scramble: ArrayVec, cid: u64, @@ -411,255 +215,8 @@ pub mod constructor { type BobToAliceWireTransfer = BobToAliceTransfer; fn new_alice(opts: Vec, cid: u64, new_version: u32) -> Option { - StackedRatchetConstructor::new_alice_constructor(opts, cid, new_version) - } - - fn new_bob>( - cid: u64, - opts: Vec, - transfer: Self::AliceToBobWireTransfer, - psks: &[T], - ) -> Option { - StackedRatchetConstructor::new_bob_constructor( - cid, - transfer.new_version, - opts, - transfer, - psks, - ) - } - - fn stage0_alice(&self) -> Option { - self.stage0_alice() - } - - fn stage0_bob(&mut self) -> Option { - self.stage0_bob() - } - - fn stage1_alice>( - &mut self, - transfer: Self::BobToAliceWireTransfer, - psks: &[T], - ) -> Result<(), CryptError> { - let nonce_msg = &self.nonce_message; - for (container, bob_param_tx) in self - .message - .inner - .iter_mut() - .zip(transfer.msg_bob_params_txs.clone()) - { - container - .pqc - .alice_on_receive_ciphertext(bob_param_tx, psks) - .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; - } - - for (idx, container) in self.message.inner.iter_mut().enumerate() { - // now, using the message pqc, decrypt the message entropy_bank - let decrypted_msg_entropy_bank = match container.pqc.decrypt( - &transfer - .encrypted_msg_entropy_banks - .get(idx) - .ok_or_else(|| { - CryptError::RekeyUpdateError( - "Unable to get encrypted_msg_entropy_banks".to_string(), - ) - })?[..], - nonce_msg, - ) { - Ok(entropy_bank) => entropy_bank, - Err(err) => { - return Err(CryptError::RekeyUpdateError(err.to_string())); - } - }; - let decrypted_entropy_bank = - EntropyBank::deserialize_from(&decrypted_msg_entropy_bank[..])?; - container.entropy_bank = Some(decrypted_entropy_bank); - } - - let nonce_scramble = &self.nonce_scramble; - self.scramble - .pqc - .alice_on_receive_ciphertext(transfer.scramble_bob_params_tx, psks) - .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; - // do the same as above - let decrypted_scramble_entropy_bank = self - .scramble - .pqc - .decrypt( - &transfer.encrypted_scramble_entropy_bank[..], - nonce_scramble, - ) - .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; - - let decrypted_entropy_bank = - EntropyBank::deserialize_from(&decrypted_scramble_entropy_bank[..])?; - self.scramble.entropy_bank = Some(decrypted_entropy_bank); - - // version check - if self - .scramble - .entropy_bank - .as_ref() - .ok_or_else(|| { - CryptError::RekeyUpdateError( - "Unable to get encrypted_msg_entropy_banks".to_string(), - ) - })? - .version - != self.message.inner[0] - .entropy_bank - .as_ref() - .ok_or_else(|| { - CryptError::RekeyUpdateError( - "Unable to get encrypted_msg_entropy_banks".to_string(), - ) - })? - .version - { - return Err(CryptError::RekeyUpdateError( - "Message entropy_bank version != scramble entropy_bank version".to_string(), - )); - } - - if self - .scramble - .entropy_bank - .as_ref() - .ok_or_else(|| { - CryptError::RekeyUpdateError( - "Unable to get encrypted_msg_entropy_banks".to_string(), - ) - })? - .cid - != self.message.inner[0] - .entropy_bank - .as_ref() - .ok_or_else(|| { - CryptError::RekeyUpdateError( - "Unable to get encrypted_msg_entropy_banks".to_string(), - ) - })? - .cid - { - return Err(CryptError::RekeyUpdateError( - "Message entropy_bank cid != scramble entropy_bank cid".to_string(), - )); - } - - Ok(()) - } - - fn update_version(&mut self, version: u32) -> Option<()> { - self.new_version = version; - - for container in self.message.inner.iter_mut() { - container.entropy_bank.as_mut()?.version = version; - } - - self.scramble.entropy_bank.as_mut()?.version = version; - Some(()) - } - - fn finish_with_custom_cid(mut self, cid: u64) -> Option { - for container in self.message.inner.iter_mut() { - container.entropy_bank.as_mut()?.cid = cid; - } - - self.scramble.entropy_bank.as_mut()?.cid = cid; - - self.finish() - } - - fn finish(self) -> Option { - StackedRatchet::try_from(self).ok() - } - } - - #[derive(Serialize, Deserialize, Debug)] - /// Transferred during KEM - pub struct AliceToBobTransfer { - pub params: CryptoParameters, - params_txs: Vec, - scramble_alice_params: AliceToBobTransferParameters, - scramble_nonce: ArrayVec, - msg_nonce: ArrayVec, - pub security_level: SecurityLevel, - cid: u64, - new_version: u32, - } - - impl AssociatedSecurityLevel for AliceToBobTransfer { - fn security_level(&self) -> SecurityLevel { - self.security_level - } - } - - impl AssociatedCryptoParams for AliceToBobTransfer { - fn crypto_params(&self) -> CryptoParameters { - self.params - } - } - - #[derive(Serialize, Deserialize, Debug)] - /// Transferred during KEM - pub struct BobToAliceTransfer { - msg_bob_params_txs: Vec, - scramble_bob_params_tx: BobToAliceTransferParameters, - encrypted_msg_entropy_banks: Vec>, - encrypted_scramble_entropy_bank: Vec, - // the security level - pub security_level: SecurityLevel, - } - - impl AssociatedSecurityLevel for BobToAliceTransfer { - fn security_level(&self) -> SecurityLevel { - self.security_level - } - } - - impl BobToAliceTransfer { - pub fn serialize_into(&self, buf: &mut BytesMut) -> Option<()> { - let len = bincode::serialized_size(self).ok()?; - buf.reserve(len as usize); - bincode::serialize_into(buf.writer(), self).ok() - } - - pub fn deserialize_from>(source: T) -> Option { - bincode::deserialize(source.as_ref()).ok() - } - } - - impl AliceToBobTransfer { - pub fn serialize_to_vec(&self) -> Option> { - bincode::serialize(self).ok() - } - - pub fn deserialize_from(source: &[u8]) -> Option { - bincode::deserialize(source).ok() - } - - /// Gets the declared new version - pub fn get_declared_new_version(&self) -> u32 { - self.new_version - } - - /// Gets the declared cid - pub fn get_declared_cid(&self) -> u64 { - self.cid - } - } - - impl StackedRatchetConstructor { - /// Called during the initialization stage - pub fn new_alice_constructor( - opts: Vec, - cid: u64, - new_version: u32, - ) -> Option { let security_level = opts.implied_security_level(); - log::trace!(target: "citadel", "[ALICE] creating container with {:?} security level", security_level); + log::trace!(target: "citadel", "[ALICE] Client {cid} creating container with {:?} security level", security_level); //let count = security_level.value() as usize + 1; let len = opts.len(); let params = opts[0].cryptography.unwrap_or_default(); @@ -693,15 +250,14 @@ pub mod constructor { }) } - /// Called when bob receives alice's pk's - pub fn new_bob_constructor>( + fn new_bob>( cid: u64, - new_version: u32, opts: Vec, - transfer: AliceToBobTransfer, + transfer: Self::AliceToBobWireTransfer, psks: &[T], ) -> Option { - log::trace!(target: "citadel", "[BOB] creating container with {:?} security level", transfer.security_level); + let new_version = transfer.new_version; + log::trace!(target: "citadel", "[BOB] Client {cid} creating container with {:?} security level", transfer.security_level); let count = transfer.security_level.value() as usize + 1; let params = transfer.params; let keys: Vec = transfer @@ -746,8 +302,7 @@ pub mod constructor { }) } - /// Generates the public key for the (message_pk, scramble_pk, nonce) - pub fn stage0_alice(&self) -> Option { + fn stage0_alice(&self) -> Option { let pks = self .message .inner @@ -779,8 +334,7 @@ pub mod constructor { }) } - /// Returns the (message_bob_ct, scramble_bob_ct, msg_entropy_bank_serialized, scramble_entropy_bank_serialized) - pub fn stage0_bob(&mut self) -> Option { + fn stage0_bob(&mut self) -> Option { let expected_count = self.message.inner.len(); let security_level = self.security_level; let msg_bob_cts: Vec = self @@ -830,10 +384,9 @@ pub mod constructor { Some(transfer) } - /// Returns Ok(()) if process succeeded - pub fn stage1_alice>( + fn stage1_alice>( &mut self, - transfer: BobToAliceTransfer, + transfer: Self::BobToAliceWireTransfer, psks: &[T], ) -> Result<(), CryptError> { let nonce_msg = &self.nonce_message; @@ -869,7 +422,6 @@ pub mod constructor { }; let mut decrypted_entropy_bank = EntropyBank::deserialize_from(&decrypted_msg_entropy_bank[..])?; - // Overwrite the CID since the entropy bank bob encrypted had his CID decrypted_entropy_bank.cid = self.cid; container.entropy_bank = Some(decrypted_entropy_bank); } @@ -878,13 +430,7 @@ pub mod constructor { self.scramble .pqc .alice_on_receive_ciphertext(transfer.scramble_bob_params_tx, psks) - .map_err(|err| { - println!( - "[DEBUG] Checkpoint 3 - Alice on receive scramble ciphertext error: {:?}", - err - ); - CryptError::RekeyUpdateError(err.to_string()) - })?; + .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; // do the same as above let decrypted_scramble_entropy_bank = self .scramble @@ -894,11 +440,12 @@ pub mod constructor { nonce_scramble, ) .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; + let mut decrypted_entropy_bank = EntropyBank::deserialize_from(&decrypted_scramble_entropy_bank[..])?; - // Overwrite the CID since the entropy bank bob encrypted had his CID decrypted_entropy_bank.cid = self.cid; self.scramble.entropy_bank = Some(decrypted_entropy_bank); + // version check if self .scramble @@ -953,13 +500,7 @@ pub mod constructor { Ok(()) } - /// Upgrades the construction into the StackedRatchet - pub fn finish(self) -> Option { - StackedRatchet::try_from(self).ok() - } - - /// Updates the internal version - pub fn update_version(&mut self, version: u32) -> Option<()> { + fn update_version(&mut self, version: u32) -> Option<()> { self.new_version = version; for container in self.message.inner.iter_mut() { @@ -970,10 +511,7 @@ pub mod constructor { Some(()) } - /// Sometimes, replacing the CID is useful such as during peer KEM exchange wherein - /// the CIDs between both parties are different. If a version is supplied, the version - /// will be updated - pub fn finish_with_custom_cid(mut self, cid: u64) -> Option { + fn finish_with_custom_cid(mut self, cid: u64) -> Option { for container in self.message.inner.iter_mut() { container.entropy_bank.as_mut()?.cid = cid; } @@ -982,23 +520,69 @@ pub mod constructor { self.finish() } + + fn finish(self) -> Option { + StackedRatchet::try_from(self).ok() + } + } + + #[derive(Serialize, Deserialize, Debug)] + /// Transferred during KEM + pub struct AliceToBobTransfer { + pub params: CryptoParameters, + params_txs: Vec, + scramble_alice_params: AliceToBobTransferParameters, + scramble_nonce: ArrayVec, + msg_nonce: ArrayVec, + pub security_level: SecurityLevel, + cid: u64, + new_version: u32, + } + + impl AssociatedSecurityLevel for AliceToBobTransfer { + fn security_level(&self) -> SecurityLevel { + self.security_level + } + } + + impl AssociatedCryptoParams for AliceToBobTransfer { + fn crypto_params(&self) -> CryptoParameters { + self.params + } + } + + #[derive(Serialize, Deserialize, Debug)] + /// Transferred during KEM + pub struct BobToAliceTransfer { + msg_bob_params_txs: Vec, + scramble_bob_params_tx: BobToAliceTransferParameters, + encrypted_msg_entropy_banks: Vec>, + encrypted_scramble_entropy_bank: Vec, + // the security level + pub security_level: SecurityLevel, + } + + impl AssociatedSecurityLevel for BobToAliceTransfer { + fn security_level(&self) -> SecurityLevel { + self.security_level + } } #[derive(Serialize, Deserialize, Debug)] - pub(super) struct MessageRatchetConstructor { - pub(super) inner: Vec, + pub(crate) struct MessageRatchetConstructor { + pub(crate) inner: Vec, } #[derive(Serialize, Deserialize, Debug)] - pub(super) struct MessageRatchetConstructorInner { - pub(super) entropy_bank: Option, - pub(super) pqc: PostQuantumContainer, + pub(crate) struct MessageRatchetConstructorInner { + pub(crate) entropy_bank: Option, + pub(crate) pqc: PostQuantumContainer, } #[derive(Serialize, Deserialize, Debug)] - pub(super) struct ScrambleRatchetConstructor { - pub(super) entropy_bank: Option, - pub(super) pqc: PostQuantumContainer, + pub(crate) struct ScrambleRatchetConstructor { + pub(crate) entropy_bank: Option, + pub(crate) pqc: PostQuantumContainer, } } diff --git a/citadel_crypt/src/scramble/crypt_splitter.rs b/citadel_crypt/src/scramble/crypt_splitter.rs index f8d1a412e..cad30ebc5 100644 --- a/citadel_crypt/src/scramble/crypt_splitter.rs +++ b/citadel_crypt/src/scramble/crypt_splitter.rs @@ -53,7 +53,7 @@ //! ## Related Components //! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Provides cryptographic entropy //! - [`PacketVector`](crate::packet_vector::PacketVector): Handles packet orientation -//! - [`Ratchet`](crate::stacked_ratchet::Ratchet): Manages encryption keys +//! - [`Ratchet`](crate::ratchets::Ratchet): Manages encryption keys //! - [`PostQuantumContainer`](citadel_pqcrypto::PostQuantumContainer): Post-quantum cryptography use std::borrow::Cow; @@ -70,7 +70,7 @@ use rand::Rng; use crate::entropy_bank::EntropyBank; use crate::packet_vector::{generate_packet_vector, PacketVector}; use crate::prelude::CryptError; -use crate::stacked_ratchet::Ratchet; +use crate::ratchets::Ratchet; pub use citadel_types::prelude::ObjectId; #[cfg(not(target_family = "wasm"))] use rayon::prelude::*; diff --git a/citadel_crypt/src/streaming_crypt_scrambler.rs b/citadel_crypt/src/streaming_crypt_scrambler.rs index 1b5eb2cf8..cbb1feafd 100644 --- a/citadel_crypt/src/streaming_crypt_scrambler.rs +++ b/citadel_crypt/src/streaming_crypt_scrambler.rs @@ -68,7 +68,7 @@ //! - [`crypt_splitter`](crate::scramble::crypt_splitter): Core encryption and packet splitting //! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Cryptographic entropy source //! - [`PacketVector`](crate::packet_vector::PacketVector): Packet orientation management -//! - [`StackedRatchet`](crate::stacked_ratchet::StackedRatchet): Key management +//! - [`StackedRatchet`](crate::ratchets::stacked::stacked_ratchet::StackedRatchet): Key management use bytes::BytesMut; use citadel_io::tokio; @@ -84,7 +84,7 @@ use crate::packet_vector::PacketVector; use crate::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupSenderDevice}; use crate::misc::CryptError; -use crate::stacked_ratchet::Ratchet; +use crate::ratchets::Ratchet; use citadel_io::tokio_stream::{Stream, StreamExt}; use citadel_io::Mutex; use citadel_types::crypto::SecurityLevel; diff --git a/citadel_crypt/src/sync_toggle.rs b/citadel_crypt/src/sync_toggle.rs index 611217135..c730dc0b9 100644 --- a/citadel_crypt/src/sync_toggle.rs +++ b/citadel_crypt/src/sync_toggle.rs @@ -48,7 +48,7 @@ //! # Related Components //! //! - [`crate::entropy_bank`] - Uses toggle for state transitions -//! - [`crate::stacked_ratchet`] - Ratchet state management +//! - [`crate::ratchets::stacked::stacked_ratchet`] - Ratchet state management //! use serde::{Deserialize, Serialize}; diff --git a/citadel_crypt/src/toolset.rs b/citadel_crypt/src/toolset.rs index 8e3d8d792..9f72e8d2a 100644 --- a/citadel_crypt/src/toolset.rs +++ b/citadel_crypt/src/toolset.rs @@ -49,7 +49,7 @@ //! - Thread-safe operations for concurrent access //! //! ## Related Components -//! - [`StackedRatchet`](crate::stacked_ratchet::StackedRatchet): Core ratchet implementation +//! - [`StackedRatchet`](crate::ratchets::stacked::stacked_ratchet::StackedRatchet): Core ratchet implementation //! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Entropy source for ratchets //! - [`CryptError`](crate::misc::CryptError): Error handling for cryptographic operations //! - [`ClientNetworkAccount`]: High-level account management @@ -59,7 +59,8 @@ use std::collections::VecDeque; use serde::{Deserialize, Serialize}; use crate::misc::CryptError; -use crate::stacked_ratchet::{Ratchet, StackedRatchet}; +use crate::ratchets::stacked::stacked_ratchet::StackedRatchet; +use crate::ratchets::Ratchet; use std::ops::RangeInclusive; /// The maximum number of ratchets to store in memory. Note that, most of the time, the true number in memory @@ -178,7 +179,7 @@ impl Toolset { self.most_recent_stacked_ratchet_version = cur_version; let prev_version = self.most_recent_stacked_ratchet_version.wrapping_sub(1); - log::trace!(target: "citadel", "[{}] Upgraded {} to {}. Adjusted index of current: {}. Adjusted index of (current - 1): {} || OLDEST: {} || LEN: {}", MAX_RATCHETS_IN_MEMORY, prev_version, cur_version, self.get_adjusted_index(cur_version), self.get_adjusted_index(prev_version), self.get_oldest_stacked_ratchet_version(), self.map.len()); + log::trace!(target: "citadel", "[{}] Upgraded {} to {} for cid={}. Adjusted index of current: {}. Adjusted index of (current - 1): {} || OLDEST: {} || LEN: {}", MAX_RATCHETS_IN_MEMORY, prev_version, cur_version, self.cid, self.get_adjusted_index(cur_version), self.get_adjusted_index(prev_version), self.get_oldest_stacked_ratchet_version(), self.map.len()); Some(update_status) } @@ -225,7 +226,7 @@ impl Toolset { self.map.pop_back().ok_or(CryptError::OutOfBoundsError)?; self.oldest_stacked_ratchet_version = self.oldest_stacked_ratchet_version.wrapping_add(1); - log::trace!(target: "citadel", "[Toolset] Deregistered version {}. New oldest: {} | LEN: {}", version, self.oldest_stacked_ratchet_version, self.len()); + log::trace!(target: "citadel", "[Toolset] Deregistered version {} for cid={}. New oldest: {} | LEN: {}", version, self.cid, self.oldest_stacked_ratchet_version, self.len()); Ok(()) } } diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index c4af09ed0..44fac7acc 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -10,8 +10,9 @@ mod tests { use citadel_crypt::entropy_bank::EntropyBank; #[cfg(not(target_family = "wasm"))] use citadel_crypt::packet_vector::PacketVector; + use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; + use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupReceiver}; - use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; use citadel_crypt::toolset::{Toolset, ToolsetUpdateStatus, MAX_RATCHETS_IN_MEMORY}; #[cfg(not(target_family = "wasm"))] use citadel_io::tokio; @@ -153,8 +154,8 @@ mod tests { #[test] fn onion_packets() { onion_packet::(); - #[cfg(feature = "fcm")] - onion_packet::(); + #[cfg(feature = "ratchets")] + onion_packet::(); } fn onion_packet() { @@ -223,17 +224,15 @@ mod tests { citadel_logging::setup_log(); for x in 0u8..KEM_ALGORITHM_COUNT { for sec in 0..SecurityLevel::Extreme.value() { - let _ = stacked_ratchet::( + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::AES_GCM_256, Some(sec.into()), - false, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); - let _ = stacked_ratchet::( + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::ChaCha20Poly_1305, Some(sec.into()), - false, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); @@ -242,21 +241,40 @@ mod tests { } #[test] - fn stacked_ratchets_fcm() { + fn mono_ratchets() { citadel_logging::setup_log(); for x in 0u8..KEM_ALGORITHM_COUNT { - for sec in 0..SecurityLevel::Extreme.value() { - let _ = stacked_ratchet::( + for _sec in 0..SecurityLevel::Extreme.value() { + let _ = gen_ratchet::( + KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::AES_GCM_256, + None, + &PRE_SHARED_KEYS, + &PRE_SHARED_KEYS, + ); + let _ = gen_ratchet::( + KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::ChaCha20Poly_1305, + None, + &PRE_SHARED_KEYS, + &PRE_SHARED_KEYS, + ); + } + } + } + + #[test] + #[should_panic(expected = "[CryptError] Out of bounds exception")] + fn mono_ratchets_fail() { + for x in 0u8..KEM_ALGORITHM_COUNT { + for sec in 1..SecurityLevel::Extreme.value() { + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::AES_GCM_256, Some(sec.into()), - true, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); - let _ = stacked_ratchet::( + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::ChaCha20Poly_1305, Some(sec.into()), - true, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); @@ -268,10 +286,9 @@ mod tests { fn security_levels() { citadel_logging::setup_log(); for sec in 0..SecurityLevel::Extreme.value() { - let ratchet = stacked_ratchet::( + let ratchet = gen_ratchet::( KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256, Some(sec.into()), - false, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); @@ -285,15 +302,14 @@ mod tests { } } - fn stacked_ratchet>( + fn gen_ratchet>( algorithm: Z, security_level: Option, - is_fcm: bool, bob_psks: &[Vec], alice_psks: &[Vec], ) -> R { let algorithm = algorithm.into(); - log::trace!(target: "citadel", "Using {:?} with {:?} @ {:?} security level | is FCM: {}", algorithm.kem_algorithm, algorithm.encryption_algorithm, security_level, is_fcm); + log::trace!(target: "citadel", "Using {:?} with {:?} @ {:?} security level", algorithm.kem_algorithm, algorithm.encryption_algorithm, security_level); let algorithm = Some(algorithm); let security_level = security_level.unwrap_or_default(); let mut alice_stacked_ratchet = R::Constructor::new_alice( @@ -377,7 +393,7 @@ mod tests { ) { toolset::(enx, kem, sig); #[cfg(feature = "fcm")] - toolset::(enx, kem, sig); + toolset::(enx, kem, sig); } fn toolset(enx: EncryptionAlgorithm, kem: KemAlgorithm, sig: SigAlgorithm) { @@ -527,7 +543,7 @@ mod tests { ) { toolset_wrapping_vers::(enx, kem, sig); #[cfg(feature = "fcm")] - toolset_wrapping_vers::(enx, kem, sig); + toolset_wrapping_vers::(enx, kem, sig); } fn toolset_wrapping_vers( @@ -631,7 +647,7 @@ mod tests { |decrypted, plaintext, _, _| debug_assert_eq!(decrypted, plaintext), ); #[cfg(feature = "fcm")] - scrambler_transmission_spectrum::( + scrambler_transmission_spectrum::( enx, kem, sig, @@ -689,7 +705,7 @@ mod tests { scrambler_transmission_spectrum::(enx, kem, sig, tx_type, verifier); #[cfg(feature = "fcm")] - scrambler_transmission_spectrum::( + scrambler_transmission_spectrum::( enx, kem, sig, tx_type, verifier, ); } diff --git a/citadel_proto/src/kernel/kernel_executor.rs b/citadel_proto/src/kernel/kernel_executor.rs index 20d3865ee..3802db502 100644 --- a/citadel_proto/src/kernel/kernel_executor.rs +++ b/citadel_proto/src/kernel/kernel_executor.rs @@ -40,7 +40,7 @@ use std::pin::Pin; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::runtime::Handle; use citadel_user::account_manager::AccountManager; use futures::TryStreamExt; diff --git a/citadel_proto/src/kernel/kernel_trait.rs b/citadel_proto/src/kernel/kernel_trait.rs index bd47907ef..4c2e2f3c0 100644 --- a/citadel_proto/src/kernel/kernel_trait.rs +++ b/citadel_proto/src/kernel/kernel_trait.rs @@ -32,7 +32,7 @@ use crate::error::NetworkError; use crate::proto::node_result::NodeResult; use crate::proto::remote::NodeRemote; use auto_impl::auto_impl; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; /// The [`NetKernel`] is the thread-safe interface between the single-threaded OR multi-threaded async /// protocol and your network application diff --git a/citadel_proto/src/kernel/mod.rs b/citadel_proto/src/kernel/mod.rs index d31f9095b..bb8948db0 100644 --- a/citadel_proto/src/kernel/mod.rs +++ b/citadel_proto/src/kernel/mod.rs @@ -54,7 +54,7 @@ use crate::error::NetworkError; use crate::macros::ContextRequirements; use crate::prelude::{PreSharedKey, ServerUnderlyingProtocol}; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::macros::support::Future; use citadel_io::tokio::runtime::Handle; use citadel_user::account_manager::AccountManager; diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index 8b71ff112..10ea33336 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -503,9 +503,10 @@ pub mod prelude { pub use citadel_crypt::argon::argon_container::ArgonDefaultServerSettings; #[cfg(not(coverage))] pub use citadel_crypt::argon::autotuner::calculate_optimal_argon_params; - pub use citadel_crypt::fcm::keys::FcmKeys; - pub use citadel_crypt::fcm::ratchet::ThinRatchet; - pub use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; + pub use citadel_crypt::ratchets::mono::keys::FcmKeys; + pub use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; + pub use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; + pub use citadel_crypt::ratchets::Ratchet; pub use citadel_types::crypto::AlgorithmsExt; pub use citadel_types::crypto::SecBuffer; pub use citadel_user::account_manager::AccountManager; diff --git a/citadel_proto/src/proto/endpoint_crypto_accessor.rs b/citadel_proto/src/proto/endpoint_crypto_accessor.rs index cc5fc6d05..69df52e15 100644 --- a/citadel_proto/src/proto/endpoint_crypto_accessor.rs +++ b/citadel_proto/src/proto/endpoint_crypto_accessor.rs @@ -25,7 +25,7 @@ use crate::error::NetworkError; use crate::inner_arg::ExpectedInnerTargetMut; use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; use crate::proto::state_container::{StateContainer, StateContainerInner}; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; #[derive(Clone)] pub enum EndpointCryptoAccessor { diff --git a/citadel_proto/src/proto/mod.rs b/citadel_proto/src/proto/mod.rs index afa68aee5..e8f06b90b 100644 --- a/citadel_proto/src/proto/mod.rs +++ b/citadel_proto/src/proto/mod.rs @@ -37,7 +37,7 @@ use crate::proto::packet::HdpHeader; use crate::proto::session::CitadelSession; use crate::proto::state_container::StateContainerInner; use bytes::BytesMut; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; /// For the custom BytesCodec that doesn't overflow pub(crate) mod codec; diff --git a/citadel_proto/src/proto/node.rs b/citadel_proto/src/proto/node.rs index 5b5d08a4a..beb27061c 100644 --- a/citadel_proto/src/proto/node.rs +++ b/citadel_proto/src/proto/node.rs @@ -32,7 +32,7 @@ use std::net::ToSocketAddrs; use std::pin::Pin; use std::sync::Arc; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::io::AsyncRead; use citadel_types::crypto::SecurityLevel; use citadel_user::account_manager::AccountManager; diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs index f2a7c77ad..e5da2d2b6 100644 --- a/citadel_proto/src/proto/node_request.rs +++ b/citadel_proto/src/proto/node_request.rs @@ -109,7 +109,7 @@ pub enum NodeRequest { PeerCommand(PeerCommand), /// For submitting a de-register request DeregisterFromHypernode(DeregisterFromHypernode), - /// Implicated CID, creds, connect mode, fcm keys, TCP/TLS only, keep alive timeout, security settings + /// Implicated CID, creds, connect mode, ratchets keys, TCP/TLS only, keep alive timeout, security settings ConnectToHypernode(ConnectToHypernode), /// Updates the entropy_bank for the given CID ReKey(ReKey), diff --git a/citadel_proto/src/proto/packet_crafter.rs b/citadel_proto/src/proto/packet_crafter.rs index 7fa3e8ea4..dc0269dc6 100644 --- a/citadel_proto/src/proto/packet_crafter.rs +++ b/citadel_proto/src/proto/packet_crafter.rs @@ -68,9 +68,9 @@ use crate::error::NetworkError; use crate::proto::outbound_sender::OutboundPrimaryStreamSender; use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualTargetType; +use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::oneshot_unencrypted_group_unified; use citadel_crypt::secure_buffer::sec_packet::SecureMessagePacket; -use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::prelude::ObjectId; /// A secure packet container that provides zero-copy buffer management and @@ -190,8 +190,8 @@ pub struct GroupTransmitter { /// The base ratchet is always required, whether between HyperLAN peer to server or hyperlan p2p. /// base_constructor may not be present, since a concurrent update may already be occurring -/// Fcm may be present, in which case, the innermost encryption pass goes through the fcm ratchet to ensure -/// Google can't see the information. The fcm constructor may not be present either, since a concurrent update may +/// Fcm may be present, in which case, the innermost encryption pass goes through the ratchets ratchet to ensure +/// Google can't see the information. The ratchets constructor may not be present either, since a concurrent update may /// be occurring pub struct RatchetPacketCrafterContainer { pub base: R, @@ -355,7 +355,7 @@ pub(crate) mod group { use crate::proto::state_container::VirtualTargetType; use crate::proto::validation::group::{GroupHeader, GroupHeaderAck, WaveAck}; use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::ObjectId; use citadel_user::serialization::SyncIO; use std::ops::RangeInclusive; @@ -556,7 +556,7 @@ pub(crate) mod do_connect { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use crate::proto::peer::peer_layer::MailboxTransfer; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::user::MutualPeer; use citadel_user::auth::proposed_credentials::ProposedCredentials; @@ -727,7 +727,7 @@ pub(crate) mod keep_alive { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use bytes::BytesMut; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use zerocopy::{I64, U128, U32, U64}; @@ -767,7 +767,7 @@ pub(crate) mod do_register { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_user::auth::proposed_credentials::ProposedCredentials; use citadel_user::serialization::SyncIO; @@ -970,7 +970,7 @@ pub(crate) mod do_disconnect { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use crate::proto::remote::Ticket; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; /// Crafts a do-disconnect stage 0 packet for a given ticket, timestamp, and security level @@ -1043,7 +1043,7 @@ pub(crate) mod do_entropy_bank_update { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, packet_sizes, HdpHeader}; use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_user::serialization::SyncIO; use serde::{Deserialize, Serialize}; @@ -1211,7 +1211,7 @@ pub(crate) mod do_deregister { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use bytes::BytesMut; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use zerocopy::{I64, U128, U32, U64}; @@ -1293,7 +1293,7 @@ pub(crate) mod pre_connect { use crate::proto::packet::packet_flags::payload_identifiers; use crate::proto::packet::{packet_flags, HdpHeader}; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ConnectMode; use citadel_types::proto::SessionSecuritySettings; @@ -1555,8 +1555,8 @@ pub(crate) mod peer_cmd { use crate::proto::peer::peer_layer::ChannelPacket; use crate::proto::remote::Ticket; use bytes::BytesMut; + use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::AES_GCM_GHASH_OVERHEAD; - use citadel_crypt::stacked_ratchet::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_user::serialization::SyncIO; use serde::Serialize; @@ -1726,7 +1726,7 @@ pub(crate) mod file { use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualTargetType; use bytes::BytesMut; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::prelude::TransferType; use citadel_types::proto::{ObjectId, VirtualObjectMetadata}; @@ -2069,7 +2069,7 @@ pub(crate) mod udp { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use bytes::BytesMut; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use zerocopy::{U32, U64}; @@ -2112,7 +2112,7 @@ pub(crate) mod hole_punch { use crate::constants::HDP_HEADER_BYTE_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; use bytes::{BufMut, BytesMut}; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use zerocopy::{U32, U64}; diff --git a/citadel_proto/src/proto/packet_processor/connect_packet.rs b/citadel_proto/src/proto/packet_processor/connect_packet.rs index 1ec42437d..5f56aeb8b 100644 --- a/citadel_proto/src/proto/packet_processor/connect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/connect_packet.rs @@ -36,7 +36,7 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{ConnectFail, ConnectSuccess, MailboxDelivery}; use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::ConnectMode; use citadel_user::backend::BackendType; use citadel_user::external_services::ServicesObject; @@ -418,7 +418,7 @@ pub async fn process_connect( if let ConnectMode::Fetch { .. } = connect_mode { log::trace!(target: "citadel", "[FETCH] complete ..."); - // we can end the session now. The fcm packets have already been sent alongside the connect signal above + // we can end the session now. The ratchets packets have already been sent alongside the connect signal above return Ok(PrimaryProcessorResult::EndSession( "Fetch succeeded", )); diff --git a/citadel_proto/src/proto/packet_processor/deregister_packet.rs b/citadel_proto/src/proto/packet_processor/deregister_packet.rs index 156b7fb54..4598c737e 100644 --- a/citadel_proto/src/proto/packet_processor/deregister_packet.rs +++ b/citadel_proto/src/proto/packet_processor/deregister_packet.rs @@ -51,7 +51,7 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::DeRegistration; use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; /// processes a deregister packet. The client must be connected to the HyperLAN Server in order to DeRegister #[cfg_attr(feature = "localhost-testing", tracing::instrument( diff --git a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs index 793d078e6..e5ed93457 100644 --- a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs @@ -50,7 +50,7 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; pub const SUCCESS_DISCONNECT: &str = "Successfully Disconnected"; diff --git a/citadel_proto/src/proto/packet_processor/file_packet.rs b/citadel_proto/src/proto/packet_processor/file_packet.rs index 7d6af7264..62b8883e9 100644 --- a/citadel_proto/src/proto/packet_processor/file_packet.rs +++ b/citadel_proto/src/proto/packet_processor/file_packet.rs @@ -58,7 +58,7 @@ use crate::proto::packet_processor::primary_group_packet::{ get_orientation_safe_ratchet, get_resp_target_cid_from_header, }; use crate::proto::{get_preferred_primary_stream, send_with_error_logging}; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::TransferType; #[cfg_attr(feature = "localhost-testing", tracing::instrument( diff --git a/citadel_proto/src/proto/packet_processor/hole_punch.rs b/citadel_proto/src/proto/packet_processor/hole_punch.rs index 16919e17f..717d951b4 100644 --- a/citadel_proto/src/proto/packet_processor/hole_punch.rs +++ b/citadel_proto/src/proto/packet_processor/hole_punch.rs @@ -53,7 +53,7 @@ use crate::error::NetworkError; use crate::proto::packet_processor::primary_group_packet::{ get_orientation_safe_ratchet, get_resp_target_cid_from_header, }; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; /// This will handle an inbound group packet #[cfg_attr(feature = "localhost-testing", tracing::instrument( diff --git a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs index 130f53075..16ad7b3e6 100644 --- a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs +++ b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs @@ -35,7 +35,7 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; /// This will handle a keep alive packet. It will automatically send a keep packet after it sleeps for a period of time #[allow(unused_results, unused_must_use)] diff --git a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs index ac4b4d495..ca9e94f84 100644 --- a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs +++ b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs @@ -50,7 +50,7 @@ use crate::proto::node_result::{GroupChannelCreated, GroupEvent}; use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; use crate::proto::peer::group_channel::GroupBroadcastPayload; use crate::proto::remote::Ticket; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::{ GroupMemberAlterMode, MemberState, MessageGroupKey, MessageGroupOptions, }; diff --git a/citadel_proto/src/proto/packet_processor/peer/mod.rs b/citadel_proto/src/proto/packet_processor/peer/mod.rs index 6e9bbdb7e..b1008a90a 100644 --- a/citadel_proto/src/proto/packet_processor/peer/mod.rs +++ b/citadel_proto/src/proto/packet_processor/peer/mod.rs @@ -29,7 +29,7 @@ use crate::error::NetworkError; use crate::prelude::{ConnectFail, NodeResult, Ticket}; use crate::proto::session::CitadelSession; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; pub mod group_broadcast; pub mod peer_cmd_packet; diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index a9d11ece3..73973932a 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -68,7 +68,7 @@ use bytes::BytesMut; use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, PeerSessionCrypto}; use citadel_crypt::prelude::ConstructorOpts; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_crypt::toolset::Toolset; use citadel_types::proto::UdpMode; use citadel_user::backend::BackendType; diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs index 1768117ff..b33ddf6ba 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs @@ -36,7 +36,7 @@ use crate::proto::packet_processor::peer::peer_cmd_packet::route_signal_response use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::{SessionSecuritySettings, UdpMode}; diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs index 8ddfe5674..22ce1fe43 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs @@ -35,7 +35,7 @@ use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::peer::peer_layer::Username; use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecurityLevel; #[cfg_attr(feature = "localhost-testing", tracing::instrument( diff --git a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs index f75169497..2ac30a2ed 100644 --- a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs @@ -48,7 +48,7 @@ //! ``` use citadel_crypt::endpoint_crypto_container::AssociatedSecurityLevel; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; use citadel_wire::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; use netbeam::sync::RelativeNodeType; diff --git a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs index 4bf2a145c..e402ee044 100644 --- a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs +++ b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs @@ -65,7 +65,7 @@ use citadel_crypt::endpoint_crypto_container::{ EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, }; use citadel_crypt::misc::CryptError; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecrecyMode; use citadel_types::prelude::ObjectId; use citadel_types::proto::UdpMode; diff --git a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs index 9da639679..bd40ebdda 100644 --- a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs +++ b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs @@ -39,7 +39,7 @@ use crate::proto::packet_processor::peer::peer_cmd_packet; use bytes::BytesMut; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use super::includes::*; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/register_packet.rs b/citadel_proto/src/proto/packet_processor/register_packet.rs index 31dd1b5ba..e1d2a031b 100644 --- a/citadel_proto/src/proto/packet_processor/register_packet.rs +++ b/citadel_proto/src/proto/packet_processor/register_packet.rs @@ -59,7 +59,7 @@ use citadel_crypt::endpoint_crypto_container::{ AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, }; use citadel_crypt::prelude::ConstructorOpts; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_user::serialization::SyncIO; /// This will handle a registration packet diff --git a/citadel_proto/src/proto/packet_processor/rekey_packet.rs b/citadel_proto/src/proto/packet_processor/rekey_packet.rs index 93c52cae5..cad87286f 100644 --- a/citadel_proto/src/proto/packet_processor/rekey_packet.rs +++ b/citadel_proto/src/proto/packet_processor/rekey_packet.rs @@ -58,7 +58,7 @@ use crate::proto::packet_processor::primary_group_packet::{ attempt_kem_as_alice_finish, attempt_kem_as_bob, get_orientation_safe_ratchet, get_resp_target_cid_from_header, ToolsetUpdate, }; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecrecyMode; use std::ops::Deref; diff --git a/citadel_proto/src/proto/packet_processor/udp_packet.rs b/citadel_proto/src/proto/packet_processor/udp_packet.rs index 3f9f031c8..e2a9a3db4 100644 --- a/citadel_proto/src/proto/packet_processor/udp_packet.rs +++ b/citadel_proto/src/proto/packet_processor/udp_packet.rs @@ -31,7 +31,7 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; use crate::proto::packet_processor::primary_group_packet::get_resp_target_cid_from_header; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; /// This will handle an inbound group packet #[cfg_attr(feature = "localhost-testing", tracing::instrument( diff --git a/citadel_proto/src/proto/peer/channel.rs b/citadel_proto/src/proto/peer/channel.rs index 086d0fd20..ff0db9752 100644 --- a/citadel_proto/src/proto/peer/channel.rs +++ b/citadel_proto/src/proto/peer/channel.rs @@ -68,7 +68,7 @@ use crate::proto::peer::peer_layer::{PeerConnectionType, PeerSignal}; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::SessionRequest; use crate::proto::state_container::VirtualConnectionType; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::macros::support::Pin; use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecurityLevel; diff --git a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs index f086ab06e..efadfb5e5 100644 --- a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs +++ b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs @@ -52,7 +52,7 @@ use crate::proto::peer::p2p_conn_handler::generic_error; use crate::proto::state_container::StateContainerInner; use async_trait::async_trait; use bytes::Bytes; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::Mutex; use citadel_types::crypto::SecurityLevel; use netbeam::reliable_conn::{ConnAddr, ReliableOrderedStreamToTarget}; diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index f7cb2dad2..bbdb608cb 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -79,7 +79,7 @@ use crate::proto::peer::peer_layer::PeerConnectionType; use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; use crate::proto::state_container::VirtualConnectionType; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::prelude::UdpMode; use citadel_user::re_exports::__private::Formatter; use citadel_wire::exports::tokio_rustls::rustls; diff --git a/citadel_proto/src/proto/peer/peer_layer.rs b/citadel_proto/src/proto/peer/peer_layer.rs index f863278b2..6d863b227 100644 --- a/citadel_proto/src/proto/peer/peer_layer.rs +++ b/citadel_proto/src/proto/peer/peer_layer.rs @@ -56,7 +56,7 @@ use crate::proto::peer::message_group::{MessageGroup, MessageGroupPeer}; use crate::proto::peer::peer_crypt::KeyExchangeProcess; use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualConnectionType; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::time::error::Error; use citadel_io::tokio::time::Duration; use citadel_io::tokio_util::time::{delay_queue, delay_queue::DelayQueue}; diff --git a/citadel_proto/src/proto/remote.rs b/citadel_proto/src/proto/remote.rs index 8bfdc8070..87ea22664 100644 --- a/citadel_proto/src/proto/remote.rs +++ b/citadel_proto/src/proto/remote.rs @@ -49,7 +49,7 @@ use crate::prelude::NodeRequest; use crate::proto::node::CitadelNodeRemoteInner; use crate::proto::outbound_sender::BoundedSender; use bytemuck::NoUninit; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::error::TrySendError; use citadel_user::account_manager::AccountManager; use citadel_wire::hypernode_type::NodeType; diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index 4f4d4bd6e..9cc331506 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -58,7 +58,7 @@ use bytes::{Bytes, BytesMut}; use citadel_io::tokio_util::codec::LengthDelimitedCodec; use futures::{SinkExt, StreamExt, TryFutureExt, TryStreamExt}; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::UdpMode; use citadel_user::account_manager::AccountManager; use citadel_user::auth::proposed_credentials::ProposedCredentials; diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index e67a06655..2898c0baa 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -42,7 +42,7 @@ use std::sync::atomic::Ordering; use bytes::BytesMut; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_user::account_manager::AccountManager; use citadel_user::auth::proposed_credentials::ProposedCredentials; use citadel_user::prelude::{ConnectProtocol, UserIdentifierExt}; diff --git a/citadel_proto/src/proto/session_queue_handler.rs b/citadel_proto/src/proto/session_queue_handler.rs index d60be8619..41e313a32 100644 --- a/citadel_proto/src/proto/session_queue_handler.rs +++ b/citadel_proto/src/proto/session_queue_handler.rs @@ -43,7 +43,7 @@ use std::task::{Context, Poll, Waker}; use crate::inner_arg::ExpectedInnerTargetMut; use crate::proto::state_container::{StateContainer, StateContainerInner}; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index 402f87b26..3909d83dd 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -131,7 +131,7 @@ use bytes::Bytes; use citadel_crypt::endpoint_crypto_container::{ EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, }; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecrecyMode; use citadel_types::crypto::SecurityLevel; diff --git a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs index bacc53747..374748494 100644 --- a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs @@ -52,7 +52,7 @@ if state.local_is_initiator { use crate::prelude::PreSharedKey; use crate::proto::state_subcontainers::preconnect_state_container::UdpChannelSender; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::SessionSecuritySettings; pub struct PeerKemStateContainer { diff --git a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs index 7a60f3763..6f2a2c8c6 100644 --- a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs @@ -54,7 +54,7 @@ if let Some(ratchet) = state.generated_ratchet { use crate::proto::packet_processor::includes::Instant; use crate::proto::peer::channel::UdpChannel; use crate::proto::remote::Ticket; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::oneshot::{channel, Receiver, Sender}; use citadel_wire::hypernode_type::NodeType; diff --git a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs index 9400da0ba..dbeb6e911 100644 --- a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs @@ -50,7 +50,7 @@ let stage_state = RegisterState::from(stage_number); use citadel_io::tokio::time::Instant; use crate::proto::packet::packet_flags; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; /// These values should correlate directly to the packet_flags::cmd::aux::do_register::* pub struct RegisterState { diff --git a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs index 48312e88c..3f8f30f40 100644 --- a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs @@ -47,7 +47,7 @@ use crate::error::NetworkError; use crate::prelude::{NodeResult, ReKeyResult, ReKeyReturnType, Ticket, VirtualTargetType}; use crate::proto::outbound_sender::UnboundedSender; use crate::proto::transfer_stats::TransferStats; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use std::collections::HashMap; pub struct RatchetUpdateState { diff --git a/citadel_proto/src/proto/validation.rs b/citadel_proto/src/proto/validation.rs index d816b965e..d55fe0e59 100644 --- a/citadel_proto/src/proto/validation.rs +++ b/citadel_proto/src/proto/validation.rs @@ -31,7 +31,7 @@ //! - [`crypto`]: Cryptographic operations //! pub(crate) mod do_connect { - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_user::client_account::ClientNetworkAccount; use crate::error::NetworkError; @@ -73,7 +73,7 @@ pub(crate) mod group { use crate::proto::packet_crafter::SecureProtocolPacket; use crate::proto::state_container::VirtualTargetType; use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ObjectId; @@ -175,7 +175,7 @@ pub(crate) mod do_register { use crate::proto::packet_crafter::do_register::{DoRegisterStage0, DoRegisterStage2Packet}; use bytes::BytesMut; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_user::prelude::ConnectionInfo; use citadel_user::serialization::SyncIO; @@ -233,7 +233,7 @@ pub(crate) mod do_stacked_ratchet_update { Stage1UpdatePacket, TruncateAckPacket, TruncatePacket, }; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_user::serialization::SyncIO; pub(crate) fn validate_stage0( @@ -269,7 +269,7 @@ pub(crate) mod pre_connect { use crate::proto::packet_crafter::pre_connect::{PreConnectStage0, SynPacket}; use crate::proto::packet_processor::includes::packet_crafter::pre_connect::SynAckPacket; use crate::proto::session_manager::CitadelSessionManager; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::ConnectMode; use citadel_types::proto::SessionSecuritySettings; use citadel_types::proto::UdpMode; @@ -480,7 +480,7 @@ pub(crate) mod aead { use zerocopy::Ref; use crate::proto::packet::HdpHeader; - use citadel_crypt::stacked_ratchet::Ratchet; + use citadel_crypt::ratchets::Ratchet; pub(crate) type AeadValidationResult<'a, R> = (Ref<&'a [u8], HdpHeader>, Bytes, R); diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs index b224f6c03..e86b763b0 100644 --- a/citadel_sdk/src/builder/node_builder.rs +++ b/citadel_sdk/src/builder/node_builder.rs @@ -67,7 +67,7 @@ pub struct NodeBuilder { /// Default node builder type pub type DefaultNodeBuilder = NodeBuilder; -pub type LightweightNodeBuilder = NodeBuilder; +pub type LightweightNodeBuilder = NodeBuilder; impl Default for NodeBuilder { fn default() -> Self { diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index fe0eade7f..8d0cc6417 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -44,8 +44,9 @@ //! - [`NetworkError`]: Error handling //! -use crate::prelude::{ObjectSource, ProtocolRemoteTargetExt, Ratchet, TargetLockedRemote}; +use crate::prelude::{ObjectSource, ProtocolRemoteTargetExt, TargetLockedRemote}; +use citadel_crypt::ratchets::Ratchet; use citadel_proto::prelude::NetworkError; use citadel_types::crypto::SecurityLevel; use std::path::PathBuf; diff --git a/citadel_sdk/src/prefabs/shared/internal_service.rs b/citadel_sdk/src/prefabs/shared/internal_service.rs index 7e9ef5539..c393f901c 100644 --- a/citadel_sdk/src/prefabs/shared/internal_service.rs +++ b/citadel_sdk/src/prefabs/shared/internal_service.rs @@ -38,8 +38,9 @@ //! - [`SecBuffer`]: Secure data handling //! -use crate::prelude::{ConnectionSuccess, Ratchet, TargetLockedRemote}; +use crate::prelude::{ConnectionSuccess, TargetLockedRemote}; use bytes::Bytes; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use citadel_proto::prelude::NetworkError; use citadel_proto::re_imports::{StreamReader, UnboundedReceiverStream}; diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index 0c83ff724..3308f39c3 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -1211,8 +1211,9 @@ pub mod results { use crate::prefabs::client::peer_connection::FileTransferHandleRx; use crate::prelude::{PeerChannel, UdpChannel}; use crate::remote_ext::remote_specialization::PeerRemote; + use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::oneshot::Receiver; - use citadel_proto::prelude::{NetworkError, Ratchet}; + use citadel_proto::prelude::NetworkError; use std::fmt::Debug; pub struct PeerConnectSuccess { diff --git a/citadel_sdk/tests/stress_tests.rs b/citadel_sdk/tests/stress_tests.rs index 731b4338b..efc459b45 100644 --- a/citadel_sdk/tests/stress_tests.rs +++ b/citadel_sdk/tests/stress_tests.rs @@ -384,7 +384,7 @@ mod tests { #[timeout(std::time::Duration::from_secs(240))] #[citadel_io::tokio::test(flavor = "multi_thread")] async fn stress_test_p2p_messaging_thin_ratchet() { - stress_test_p2p_messaging_with_ratchet::( + stress_test_p2p_messaging_with_ratchet::( 500, SecrecyMode::Perfect, None, diff --git a/citadel_user/src/account_loader.rs b/citadel_user/src/account_loader.rs index 7415b9285..0e06f1831 100644 --- a/citadel_user/src/account_loader.rs +++ b/citadel_user/src/account_loader.rs @@ -52,7 +52,7 @@ use crate::directory_store::*; use crate::hypernode_account::CNAC_SERIALIZED_EXTENSION; use crate::misc::AccountError; use crate::prelude::ClientNetworkAccount; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use std::collections::HashMap; /// Loads all locally-stored CNACs, as well as the highest CID (used to update local nac in case improper shutdown) diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs index 22d19cac3..d24d4a4f9 100644 --- a/citadel_user/src/account_manager.rs +++ b/citadel_user/src/account_manager.rs @@ -102,9 +102,9 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::ConnectionInfo; use crate::server_misc_settings::ServerMiscSettings; use citadel_crypt::argon::argon_container::{ArgonDefaultServerSettings, ArgonSettings}; -use citadel_crypt::fcm::ratchet::ThinRatchet; -use citadel_crypt::stacked_ratchet::Ratchet; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; +use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::prelude::PeerInfo; use citadel_types::user::MutualPeer; use citadel_types::user::UserIdentifier; @@ -117,7 +117,7 @@ use std::pin::Pin; /// The default manager for handling the list of users stored locally. It also allows for user creation, and is used especially /// for when creating a new user via the registration service. #[derive(Clone)] -pub struct AccountManager { +pub struct AccountManager { services_handler: ServicesHandler, persistence_handler: PersistenceHandler, node_argon_settings: ArgonSettings, diff --git a/citadel_user/src/backend/filesystem_backend.rs b/citadel_user/src/backend/filesystem_backend.rs index 73fb8e317..3550bdf3f 100644 --- a/citadel_user/src/backend/filesystem_backend.rs +++ b/citadel_user/src/backend/filesystem_backend.rs @@ -36,8 +36,8 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::CNAC_SERIALIZED_EXTENSION; use crate::serialization::SyncIO; use async_trait::async_trait; +use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::MAX_BYTES_PER_GROUP; -use citadel_crypt::stacked_ratchet::Ratchet; use citadel_crypt::streaming_crypt_scrambler::ObjectSource; use citadel_io::tokio; use citadel_io::tokio_stream::StreamExt; diff --git a/citadel_user/src/backend/memory.rs b/citadel_user/src/backend/memory.rs index a50d26ba6..cfe48d0d4 100644 --- a/citadel_user/src/backend/memory.rs +++ b/citadel_user/src/backend/memory.rs @@ -42,7 +42,7 @@ use crate::backend::BackendConnection; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; use async_trait::async_trait; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; diff --git a/citadel_user/src/backend/mod.rs b/citadel_user/src/backend/mod.rs index 4c518a94c..0115285c8 100644 --- a/citadel_user/src/backend/mod.rs +++ b/citadel_user/src/backend/mod.rs @@ -44,8 +44,9 @@ use std::sync::Arc; use async_trait::async_trait; -use citadel_crypt::fcm::ratchet::ThinRatchet; -use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; +use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; +use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::Ratchet; #[cfg(all(feature = "redis", not(coverage)))] use crate::backend::redis_backend::RedisConnectionOptions; @@ -359,7 +360,7 @@ pub trait BackendConnection: Send + Sync { } /// This is what every C/NAC gets. This gets called before making I/O operations -pub struct PersistenceHandler { +pub struct PersistenceHandler { inner: Arc>, } diff --git a/citadel_user/src/backend/redis_backend.rs b/citadel_user/src/backend/redis_backend.rs index e5f70b7f1..a0b376cca 100644 --- a/citadel_user/src/backend/redis_backend.rs +++ b/citadel_user/src/backend/redis_backend.rs @@ -36,7 +36,7 @@ use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX}; use crate::serialization::SyncIO; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; diff --git a/citadel_user/src/backend/sql_backend.rs b/citadel_user/src/backend/sql_backend.rs index d23c5e3d7..f1dbd6558 100644 --- a/citadel_user/src/backend/sql_backend.rs +++ b/citadel_user/src/backend/sql_backend.rs @@ -37,8 +37,9 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX}; use crate::serialization::SyncIO; use async_trait::async_trait; -use citadel_crypt::fcm::ratchet::ThinRatchet; -use citadel_crypt::stacked_ratchet::{Ratchet, StackedRatchet}; +use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; +use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; @@ -65,7 +66,7 @@ use std::time::Duration; /// /// * `R`: The ratchet type used for encryption /// * `Fcm`: The ratchet type used for FCM (Firebase Cloud Messaging) -pub struct SqlBackend { +pub struct SqlBackend { url: String, conn: Option, variant: SqlVariant, diff --git a/citadel_user/src/client_account.rs b/citadel_user/src/client_account.rs index 5a7352ced..b8b1d8362 100644 --- a/citadel_user/src/client_account.rs +++ b/citadel_user/src/client_account.rs @@ -95,16 +95,16 @@ use crate::prelude::ConnectionInfo; use multimap::MultiMap; use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::fmt::Formatter; use crate::auth::proposed_credentials::ProposedCredentials; use crate::auth::DeclaredAuthenticationMode; use crate::serialization::SyncIO; -use citadel_crypt::fcm::ratchet::ThinRatchet; use citadel_crypt::prelude::Toolset; -use citadel_crypt::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; +use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; use citadel_types::crypto::SecBuffer; use citadel_types::user::MutualPeer; use std::collections::HashMap; @@ -123,7 +123,7 @@ pub const HYPERLAN_IDX: u64 = 0; ///use futures::{TryFutureExt, TryStreamExt}; #[derive(Serialize, Deserialize)] /// Inner device -pub struct ClientNetworkAccountInner { +pub struct ClientNetworkAccountInner { /// The client identification number pub cid: u64, /// While this NAC should be session-oriented, it may be replaced if [PINNED_IP_MODE] is disabled, meaning, a new IP @@ -163,12 +163,12 @@ pub struct ClientNetworkAccountInner { +pub struct ClientNetworkAccount { /// The inner thread-safe device inner: Arc>, } -struct MetaInner { +struct MetaInner { cid: u64, is_personal: bool, passwordless: bool, diff --git a/citadel_user/src/hypernode_account.rs b/citadel_user/src/hypernode_account.rs index 078b6ce79..ec304bd45 100644 --- a/citadel_user/src/hypernode_account.rs +++ b/citadel_user/src/hypernode_account.rs @@ -76,7 +76,7 @@ use crate::account_manager::AccountManager; use crate::misc::AccountError; use crate::prelude::ClientNetworkAccount; use async_trait::async_trait; -use citadel_crypt::stacked_ratchet::Ratchet; +use citadel_crypt::ratchets::Ratchet; use citadel_types::user::MutualPeer; use citadel_types::user::UserIdentifier; diff --git a/citadel_user/tests/crypto.rs b/citadel_user/tests/crypto.rs index bb08bfb6a..53cbd1825 100644 --- a/citadel_user/tests/crypto.rs +++ b/citadel_user/tests/crypto.rs @@ -1,10 +1,10 @@ #[cfg(test)] #[cfg(feature = "jwt-testing")] mod tests { - use citadel_crypt::stacked_ratchet::constructor::{ + use citadel_crypt::ratchets::stacked::stacked_ratchet::constructor::{ BobToAliceTransferType, StackedRatchetConstructor, }; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use std::collections::HashMap; diff --git a/citadel_user/tests/primary.rs b/citadel_user/tests/primary.rs index f40211500..b24b35162 100644 --- a/citadel_user/tests/primary.rs +++ b/citadel_user/tests/primary.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests { use citadel_crypt::prelude::ConstructorOpts; - use citadel_crypt::stacked_ratchet::constructor::StackedRatchetConstructor; - use citadel_crypt::stacked_ratchet::StackedRatchet; + use citadel_crypt::ratchets::stacked::stacked_ratchet::constructor::StackedRatchetConstructor; + use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; use citadel_types::crypto::KemAlgorithm; use citadel_user::account_manager::AccountManager; use citadel_user::auth::proposed_credentials::ProposedCredentials; @@ -11,6 +11,7 @@ mod tests { use futures::Future; use std::str::FromStr; + use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; use citadel_io::tokio; use citadel_types::crypto::EncryptionAlgorithm; use citadel_types::crypto::SecBuffer; @@ -1447,11 +1448,9 @@ mod tests { Some(KemAlgorithm::Kyber + EncryptionAlgorithm::AES_GCM_256), Default::default(), ); - let mut alice = - StackedRatchetConstructor::new_alice_constructor(opts.clone(), cid, version).unwrap(); - let mut bob = StackedRatchetConstructor::new_bob_constructor::>( + let mut alice = StackedRatchetConstructor::new_alice(opts.clone(), cid, version).unwrap(); + let mut bob = StackedRatchetConstructor::new_bob::>( cid, - version, opts, alice.stage0_alice().unwrap(), &[], From 993439c0847f1c7690cf0653d1394b0f88627826 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sun, 22 Dec 2024 14:20:51 -0500 Subject: [PATCH 08/12] refactor: citadel_user clippy and docs compiling --- .github/workflows/validate.yml | 2 +- citadel_crypt/src/argon/argon_container.rs | 2 +- citadel_crypt/src/argon/autotuner.rs | 3 +- .../src/endpoint_crypto_container.rs | 32 +++++-------- citadel_crypt/src/entropy_bank.rs | 27 ----------- citadel_crypt/src/lib.rs | 2 +- citadel_crypt/src/misc.rs | 2 + citadel_crypt/src/packet_vector.rs | 3 +- citadel_crypt/src/ratchet_manager.rs | 11 +++-- citadel_crypt/src/ratchets/mono/keys.rs | 4 +- citadel_crypt/src/ratchets/mono/mod.rs | 3 +- citadel_crypt/src/ratchets/mono/ratchet.rs | 14 +++--- citadel_crypt/src/ratchets/stacked/mod.rs | 3 +- .../{stacked_ratchet.rs => ratchet.rs} | 9 ++-- citadel_crypt/src/scramble/crypt_splitter.rs | 32 ------------- .../src/streaming_crypt_scrambler.rs | 48 +------------------ citadel_crypt/src/sync_toggle.rs | 4 +- citadel_crypt/src/toolset.rs | 34 +------------ citadel_crypt/tests/primary.rs | 2 +- citadel_pqcrypto/src/lib.rs | 4 +- citadel_proto/src/lib.rs | 4 +- .../src/proto/misc/udp_internal_interface.rs | 2 +- citadel_proto/src/proto/node.rs | 4 +- .../packet_processor/preconnect_packet.rs | 2 +- .../packet_processor/primary_group_packet.rs | 16 +++---- .../proto/packet_processor/rekey_packet.rs | 5 +- .../src/proto/peer/p2p_conn_handler.rs | 2 +- citadel_proto/src/proto/session.rs | 2 +- citadel_proto/src/proto/session_manager.rs | 2 +- citadel_proto/src/proto/state_container.rs | 12 ++--- citadel_user/src/account_loader.rs | 9 ++-- citadel_user/src/account_manager.rs | 30 +++++------- citadel_user/src/auth/proposed_credentials.rs | 6 ++- citadel_user/src/backend/mod.rs | 4 +- citadel_user/src/backend/sql_backend.rs | 2 +- citadel_user/src/client_account.rs | 6 +-- citadel_user/src/connection_metadata.rs | 11 +++++ citadel_user/src/external_services/mod.rs | 3 ++ citadel_user/src/hypernode_account.rs | 5 +- citadel_user/src/misc.rs | 15 ++++-- citadel_user/tests/crypto.rs | 4 +- citadel_user/tests/primary.rs | 14 ++---- citadel_wire/src/error.rs | 8 +--- citadel_wire/src/standard/mod.rs | 6 +-- .../src/standard/nat_identification.rs | 9 ++-- citadel_wire/src/standard/quic.rs | 2 +- citadel_wire/src/standard/socket_helpers.rs | 4 +- citadel_wire/src/standard/tls.rs | 12 +++-- citadel_wire/src/standard/upnp_handler.rs | 4 +- .../src/udp_traversal/hole_punch_config.rs | 4 +- ..._socket_addr.rs => hole_punched_socket.rs} | 6 +-- .../src/udp_traversal/linear/method3.rs | 2 +- citadel_wire/src/udp_traversal/linear/mod.rs | 30 +----------- citadel_wire/src/udp_traversal/mod.rs | 2 +- citadel_wire/src/udp_traversal/multi/mod.rs | 8 +++- .../src/udp_traversal/udp_hole_puncher.rs | 29 +---------- 56 files changed, 177 insertions(+), 350 deletions(-) rename citadel_crypt/src/ratchets/stacked/{stacked_ratchet.rs => ratchet.rs} (98%) rename citadel_wire/src/udp_traversal/{targetted_udp_socket_addr.rs => hole_punched_socket.rs} (96%) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index af803f6d6..57a9914a7 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -152,7 +152,7 @@ jobs: - run: cargo clippy --tests --examples -- -D warnings - run: cargo fmt --check - run: RUSTDOCFLAGS="-D warnings" cargo make docs - - run: cargo test --package citadel_sdk --doc + - run: cargo test --doc # - name: cargo build pq_kems # run: cargo build --bin pq_kems_test # # Run with valgrind diff --git a/citadel_crypt/src/argon/argon_container.rs b/citadel_crypt/src/argon/argon_container.rs index dbfdea160..3f0996dd3 100644 --- a/citadel_crypt/src/argon/argon_container.rs +++ b/citadel_crypt/src/argon/argon_container.rs @@ -14,7 +14,7 @@ //! //! ## Usage Example //! ```rust -//! use citadel_crypt::argon::argon_container::{ArgonSettings, ClientArgonContainer, AsyncArgon}; +//! use citadel_crypt::argon::argon_container::{ServerArgonContainer, ArgonSettings, ClientArgonContainer, AsyncArgon}; //! use citadel_types::crypto::SecBuffer; //! //! async fn hash_password() { diff --git a/citadel_crypt/src/argon/autotuner.rs b/citadel_crypt/src/argon/autotuner.rs index 0919c16a4..d2bafd423 100644 --- a/citadel_crypt/src/argon/autotuner.rs +++ b/citadel_crypt/src/argon/autotuner.rs @@ -17,8 +17,9 @@ //! //! ```rust //! use citadel_crypt::argon::autotuner::calculate_optimal_argon_params; +//! use citadel_crypt::misc::CryptError; //! -//! async fn configure_argon() -> Result<(), CryptError> { +//! async fn configure_argon() -> Result<(), CryptError> { //! // Configure Argon2 to take at least 500ms //! let optimal_params = calculate_optimal_argon_params( //! 500, // minimum milliseconds diff --git a/citadel_crypt/src/endpoint_crypto_container.rs b/citadel_crypt/src/endpoint_crypto_container.rs index b925577af..6b956f6ad 100644 --- a/citadel_crypt/src/endpoint_crypto_container.rs +++ b/citadel_crypt/src/endpoint_crypto_container.rs @@ -19,30 +19,20 @@ //! ```rust //! use citadel_crypt::endpoint_crypto_container::{PeerSessionCrypto, KemTransferStatus}; //! use citadel_crypt::toolset::Toolset; -//! use citadel_crypt::stacked_ratchet::StackedRatchet; +//! use citadel_crypt::ratchets::stacked::StackedRatchet; +//! use citadel_crypt::misc::CryptError; //! +//! # fn get_ratchet() -> StackedRatchet { todo!() } //! fn setup_session() -> Result<(), CryptError> { +//! // Create ratchet +//! let ratchet = get_ratchet(); +//! let cid = 12345; //! // Create toolset with default parameters -//! let toolset = Toolset::new_with_defaults(); -//! +//! let toolset = Toolset::new(cid, ratchet); +//! //! // Initialize peer session (as initiator) -//! let mut session = PeerSessionCrypto::new(toolset, true); -//! -//! // Get constructor for next update -//! if let Some(constructor) = session.get_next_constructor(false) { -//! // Perform update -//! let status = session.update_sync_safe( -//! constructor, -//! true, // is_alice -//! 1234 // local_cid -//! )?; -//! -//! // Handle transfer status -//! if status.has_some() { -//! println!("Update requires transfer"); -//! } -//! } -//! +//! let session = PeerSessionCrypto::new(toolset, true); +//! // Set to "false" for the other peer //! Ok(()) //! } //! ``` @@ -58,7 +48,7 @@ //! # Related Components //! //! - [`crate::toolset::Toolset`] - Cryptographic toolset -//! - [`crate::ratchets::stacked::stacked_ratchet::StackedRatchet`] - Ratchet implementation +//! - [`crate::ratchets::stacked::ratchet::StackedRatchet`] - Ratchet implementation //! - [`crate::misc::CryptError`] - Error handling //! diff --git a/citadel_crypt/src/entropy_bank.rs b/citadel_crypt/src/entropy_bank.rs index 7a3c98546..fc9c12acb 100644 --- a/citadel_crypt/src/entropy_bank.rs +++ b/citadel_crypt/src/entropy_bank.rs @@ -14,33 +14,6 @@ //! - Post-quantum cryptography support //! - Transient counter management //! -//! # Examples -//! -//! ```rust -//! use citadel_crypt::entropy_bank::EntropyBank; -//! use citadel_pqcrypto::PostQuantumContainer; -//! -//! fn protect_data() -> Result<(), CryptError> { -//! // Create new entropy bank -//! let bank = EntropyBank::new( -//! 1234, // Client ID -//! 1, // Version -//! EncryptionAlgorithm::default() -//! )?; -//! -//! // Create quantum container -//! let container = PostQuantumContainer::new(); -//! -//! // Encrypt data -//! let ciphertext = bank.encrypt(&container, b"secret data")?; -//! -//! // Decrypt data -//! let plaintext = bank.decrypt(&container, &ciphertext)?; -//! -//! Ok(()) -//! } -//! ``` -//! //! # Important Notes //! //! - Nonce reuse is prevented by design diff --git a/citadel_crypt/src/lib.rs b/citadel_crypt/src/lib.rs index 0d90816f0..cfb3c5ad5 100644 --- a/citadel_crypt/src/lib.rs +++ b/citadel_crypt/src/lib.rs @@ -72,7 +72,7 @@ pub mod secure_buffer; /// Allows thread-pooled asynchronous and parallel file processing pub mod streaming_crypt_scrambler; -/// +/// `RatchetManager` provides a robust, sync-safe method to rekey across networks between two nodes pub mod ratchet_manager; pub mod sync_toggle; /// Provides entropy_bank management, update, and versioning. This is what's exposed to the citadel_user api. The entropy_banks themselves are abstracted beneath diff --git a/citadel_crypt/src/misc.rs b/citadel_crypt/src/misc.rs index 1aeeabbcd..c8dc45fa6 100644 --- a/citadel_crypt/src/misc.rs +++ b/citadel_crypt/src/misc.rs @@ -105,6 +105,8 @@ impl> Display for CryptError { } } +impl std::error::Error for CryptError {} + /// Creates a port pair mapping at random pub fn create_port_mapping() -> Vec<(u16, u16)> { let mut input_ports = Vec::with_capacity(DRILL_RANGE); diff --git a/citadel_crypt/src/packet_vector.rs b/citadel_crypt/src/packet_vector.rs index f9e454735..d0d50dba5 100644 --- a/citadel_crypt/src/packet_vector.rs +++ b/citadel_crypt/src/packet_vector.rs @@ -18,8 +18,9 @@ //! ```rust //! use citadel_crypt::packet_vector::{PacketVector, generate_packet_vector}; //! use citadel_crypt::entropy_bank::EntropyBank; +//! use citadel_crypt::misc::CryptError; //! -//! fn coordinate_packets() -> Result<(), anyhow::Error> { +//! fn coordinate_packets() -> Result<(), CryptError> { //! // Create entropy bank for port scrambling //! let bank = EntropyBank::new(1234, 1, Default::default())?; //! diff --git a/citadel_crypt/src/ratchet_manager.rs b/citadel_crypt/src/ratchet_manager.rs index 7e7cff457..29bc2b7bc 100644 --- a/citadel_crypt/src/ratchet_manager.rs +++ b/citadel_crypt/src/ratchet_manager.rs @@ -18,6 +18,8 @@ //! ```rust,no_run //! use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; //! use citadel_crypt::ratchet_manager::{RatchetManager, RatchetMessage}; +//! use futures::{Sink, Stream}; +//! use citadel_crypt::ratchets::Ratchet; //! //! async fn example( //! sender: S, @@ -26,13 +28,14 @@ //! psks: &[Vec] //! ) -> Result<(), Box> //! where -//! S: Sink + Unpin, -//! I: Stream + Unpin, +//! S: Sink + Unpin + Send + 'static, +//! >::Error: std::fmt::Debug, +//! I: Stream + Unpin + Send + 'static, //! R: Ratchet, //! { //! let mut manager = RatchetManager::new(sender, receiver, container, psks); //! // Trigger a ratchet update -//! manager.rekey().await?; +//! manager.trigger_rekey().await?; //! Ok(()) //! } //! ``` @@ -631,7 +634,7 @@ mod tests { use crate::endpoint_crypto_container::{EndpointRatchetConstructor, PeerSessionCrypto}; use crate::prelude::Toolset; use crate::ratchet_manager::{RatchetManager, RatchetMessage}; - use crate::ratchets::stacked::stacked_ratchet::StackedRatchet; + use crate::ratchets::stacked::ratchet::StackedRatchet; use crate::ratchets::Ratchet; use citadel_io::tokio; use citadel_pqcrypto::constructor_opts::ConstructorOpts; diff --git a/citadel_crypt/src/ratchets/mono/keys.rs b/citadel_crypt/src/ratchets/mono/keys.rs index 2fd01cde5..68c6ad692 100644 --- a/citadel_crypt/src/ratchets/mono/keys.rs +++ b/citadel_crypt/src/ratchets/mono/keys.rs @@ -13,7 +13,7 @@ //! //! ## Usage Example //! ```rust -//! use citadel_crypt::ratchets::keys::FcmKeys; +//! use citadel_crypt::ratchets::mono::keys::FcmKeys; //! //! // Create new FCM keys //! let keys = FcmKeys::new( @@ -29,7 +29,7 @@ //! let keys_clone = keys.clone(); //! //! // Serialize for storage if needed -//! let serialized = serde_json::to_string(&keys).unwrap(); +//! let serialized = bincode::serialize(&keys).unwrap(); //! ``` //! //! ## Important Notes diff --git a/citadel_crypt/src/ratchets/mono/mod.rs b/citadel_crypt/src/ratchets/mono/mod.rs index 476c21ab4..b98419284 100644 --- a/citadel_crypt/src/ratchets/mono/mod.rs +++ b/citadel_crypt/src/ratchets/mono/mod.rs @@ -1,2 +1,3 @@ pub mod keys; -pub mod ratchet; +pub(crate) mod ratchet; +pub use ratchet::*; diff --git a/citadel_crypt/src/ratchets/mono/ratchet.rs b/citadel_crypt/src/ratchets/mono/ratchet.rs index 46a3a77d1..34cb8d265 100644 --- a/citadel_crypt/src/ratchets/mono/ratchet.rs +++ b/citadel_crypt/src/ratchets/mono/ratchet.rs @@ -13,20 +13,22 @@ //! - Support for local encryption/decryption //! //! ## Usage Example -//! ```rust -//! use citadel_crypt::ratchets::fcm_ratchet::{ThinRatchet, ThinRatchetConstructor}; -//! use citadel_crypt::stacked_ratchet::Ratchet; +//! ```rust. no_run +//! use crate::citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; +//! use citadel_crypt::ratchets::mono::{MonoRatchet, MonoRatchetConstructor}; +//! use citadel_crypt::ratchets::Ratchet; //! use citadel_pqcrypto::constructor_opts::ConstructorOpts; //! use citadel_types::crypto::SecurityLevel; //! +//! # fn get_opts() -> Vec { todo!() } //! // Create a new ratchet for Alice //! let cid = 12345; //! let version = 0; -//! let opts = ConstructorOpts::new_default(); -//! let constructor = ThinRatchetConstructor::new_alice( +//! let opts = get_opts(); +//! let constructor = MonoRatchetConstructor::new_alice( +//! opts, //! cid, //! version, -//! opts, //! ).unwrap(); //! //! // Build the ratchet diff --git a/citadel_crypt/src/ratchets/stacked/mod.rs b/citadel_crypt/src/ratchets/stacked/mod.rs index d22235106..1cde0fb49 100644 --- a/citadel_crypt/src/ratchets/stacked/mod.rs +++ b/citadel_crypt/src/ratchets/stacked/mod.rs @@ -1,2 +1,3 @@ /// This is a container for holding the entropy_bank and PQC, and is intended to replace the separate use of the entropy_bank/PQC -pub mod stacked_ratchet; +pub(crate) mod ratchet; +pub use ratchet::*; diff --git a/citadel_crypt/src/ratchets/stacked/stacked_ratchet.rs b/citadel_crypt/src/ratchets/stacked/ratchet.rs similarity index 98% rename from citadel_crypt/src/ratchets/stacked/stacked_ratchet.rs rename to citadel_crypt/src/ratchets/stacked/ratchet.rs index c399c06dc..79009ac90 100644 --- a/citadel_crypt/src/ratchets/stacked/stacked_ratchet.rs +++ b/citadel_crypt/src/ratchets/stacked/ratchet.rs @@ -17,8 +17,10 @@ //! # Examples //! //! ```rust -//! use citadel_crypt::stacked_ratchet::{StackedRatchet, constructor::StackedRatchetConstructor}; +//! use citadel_crypt::ratchets::stacked::{StackedRatchet, constructor::StackedRatchetConstructor}; +//! use crate::citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; //! use citadel_pqcrypto::constructor_opts::ConstructorOpts; +//! use citadel_crypt::ratchets::Ratchet; //! use citadel_types::crypto::SecurityLevel; //! //! fn setup_ratchet() -> Option { @@ -30,7 +32,6 @@ //! opts, //! 1234, // Client ID //! 1, // Version -//! Some(SecurityLevel::Standard) //! )?; //! //! // Generate Alice's initial ratchet @@ -61,7 +62,7 @@ use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; -use crate::ratchets::stacked::stacked_ratchet::constructor::StackedRatchetConstructor; +use crate::ratchets::stacked::ratchet::constructor::StackedRatchetConstructor; use crate::ratchets::Ratchet; use citadel_pqcrypto::constructor_opts::{ConstructorOpts, RecursiveChain}; use citadel_pqcrypto::PostQuantumContainer; @@ -176,7 +177,7 @@ pub mod constructor { }; use crate::entropy_bank::EntropyBank; use crate::prelude::CryptError; - use crate::ratchets::stacked::stacked_ratchet::StackedRatchet; + use crate::ratchets::stacked::ratchet::StackedRatchet; use arrayvec::ArrayVec; use citadel_pqcrypto::constructor_opts::{ConstructorOpts, ImpliedSecurityLevel}; use citadel_pqcrypto::wire::{AliceToBobTransferParameters, BobToAliceTransferParameters}; diff --git a/citadel_crypt/src/scramble/crypt_splitter.rs b/citadel_crypt/src/scramble/crypt_splitter.rs index cad30ebc5..d53293dd2 100644 --- a/citadel_crypt/src/scramble/crypt_splitter.rs +++ b/citadel_crypt/src/scramble/crypt_splitter.rs @@ -12,38 +12,6 @@ //! - Implements timeout mechanisms for both individual waves and entire groups //! - Handles packet reconstruction with missing packet detection //! -//! ## Usage Example -//! ```rust -//! use citadel_crypt::scramble::crypt_splitter::{ -//! GroupSenderDevice, GroupReceiver, GroupReceiverConfig, -//! SecurityLevel, TransferType -//! }; -//! -//! // Create sender configuration -//! let cfg = GroupReceiverConfig::new_refresh( -//! group_id, // Unique group identifier -//! object_id, // Object being transferred -//! header_size, // Size of packet headers -//! plaintext_length, // Length of data to send -//! max_packet_size, // Maximum size per packet -//! full_waves, // Number of full waves -//! partial_waves, // Number of partial waves -//! max_bytes_per_wave,// Maximum bytes per wave -//! last_wave_bytes, // Bytes in last wave -//! packets_per_wave, // Packets per wave -//! last_cipher_len, // Last wave ciphertext length -//! &transfer_type, // Transfer type -//! false, // Empty transfer flag -//! ); -//! -//! // Create receiver -//! let mut receiver = GroupReceiver::new( -//! cfg.clone(), -//! wave_timeout_ms, // Wave timeout in milliseconds -//! group_timeout_ms, // Group timeout in milliseconds -//! ); -//! ``` -//! //! ## Important Notes //! - Maximum packet size is determined by the security level and encryption algorithm //! - Wave-based transmission allows for efficient packet organization and retransmission diff --git a/citadel_crypt/src/streaming_crypt_scrambler.rs b/citadel_crypt/src/streaming_crypt_scrambler.rs index cbb1feafd..a9c1816f6 100644 --- a/citadel_crypt/src/streaming_crypt_scrambler.rs +++ b/citadel_crypt/src/streaming_crypt_scrambler.rs @@ -12,52 +12,6 @@ //! - Custom header inscription for packets //! - Progress tracking and cancellation support //! -//! ## Usage Example -//! ```rust -//! use citadel_crypt::{ -//! streaming_crypt_scrambler::{scramble_encrypt_source, BytesSource}, -//! SecurityLevel, ObjectId, TransferType -//! }; -//! use tokio::sync::mpsc; -//! use tokio::sync::oneshot; -//! -//! async fn encrypt_stream() { -//! // Create channels for group sending and stopping -//! let (group_sender, mut group_receiver) = mpsc::channel(10); -//! let (stop_sender, stop_receiver) = oneshot::channel(); -//! -//! // Create a source (e.g., from bytes) -//! let data = vec![0u8; 1024]; -//! let source = BytesSource::from(data); -//! -//! // Header inscriber function -//! let header_inscriber = |vector: &PacketVector, -//! bank: &EntropyBank, -//! obj_id: ObjectId, -//! cid: u64, -//! bytes: &mut BytesMut| { -//! // Inscribe header data -//! }; -//! -//! // Start encryption -//! let result = scramble_encrypt_source( -//! source, -//! None, // Use default group size -//! ObjectId::new(), -//! group_sender, -//! stop_receiver, -//! SecurityLevel::Standard, -//! stacked_ratchet, -//! static_aux_ratchet, -//! header_size, -//! target_cid, -//! group_id, -//! TransferType::Standard, -//! header_inscriber, -//! ).await; -//! } -//! ``` -//! //! ## Important Notes //! - Maximum group size is limited to prevent excessive memory usage //! - Sources are consumed during streaming and cannot be reused @@ -68,7 +22,7 @@ //! - [`crypt_splitter`](crate::scramble::crypt_splitter): Core encryption and packet splitting //! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Cryptographic entropy source //! - [`PacketVector`](crate::packet_vector::PacketVector): Packet orientation management -//! - [`StackedRatchet`](crate::ratchets::stacked::stacked_ratchet::StackedRatchet): Key management +//! - [`StackedRatchet`](crate::ratchets::stacked::ratchet::StackedRatchet): Key management use bytes::BytesMut; use citadel_io::tokio; diff --git a/citadel_crypt/src/sync_toggle.rs b/citadel_crypt/src/sync_toggle.rs index c730dc0b9..3f4ac4f41 100644 --- a/citadel_crypt/src/sync_toggle.rs +++ b/citadel_crypt/src/sync_toggle.rs @@ -30,7 +30,7 @@ //! } //! //! // Check current state -//! let state = toggle.get(); +//! let state = toggle.state(); //! //! // Reset to off //! toggle.toggle_off(); @@ -48,7 +48,7 @@ //! # Related Components //! //! - [`crate::entropy_bank`] - Uses toggle for state transitions -//! - [`crate::ratchets::stacked::stacked_ratchet`] - Ratchet state management +//! - [`crate::ratchets::stacked::ratchet`] - Ratchet state management //! use serde::{Deserialize, Serialize}; diff --git a/citadel_crypt/src/toolset.rs b/citadel_crypt/src/toolset.rs index 9f72e8d2a..5607d268a 100644 --- a/citadel_crypt/src/toolset.rs +++ b/citadel_crypt/src/toolset.rs @@ -12,36 +12,6 @@ //! - Handles secure ratchet updates and deregistration //! - Ensures thread-safe access to cryptographic primitives //! -//! ## Usage Example -//! ```rust -//! use citadel_crypt::toolset::{Toolset, UpdateStatus}; -//! use citadel_crypt::stacked_ratchet::StackedRatchet; -//! -//! // Create a new toolset with initial ratchet -//! let cid = 12345; -//! let initial_ratchet = StackedRatchet::new(cid, 0); -//! let mut toolset = Toolset::new(cid, initial_ratchet); -//! -//! // Create and add a new ratchet version -//! let new_ratchet = StackedRatchet::new(cid, 1); -//! match toolset.update_from(new_ratchet) { -//! Some(UpdateStatus::Committed { new_version }) => { -//! println!("Updated to version {}", new_version); -//! } -//! Some(UpdateStatus::CommittedNeedsSynchronization { new_version, old_version }) => { -//! println!("Updated to {} but need to sync version {}", new_version, old_version); -//! // Implement synchronization logic -//! toolset.deregister_oldest_stacked_ratchet(old_version).unwrap(); -//! } -//! None => println!("Update failed"), -//! } -//! -//! // Access ratchets -//! if let Some(current) = toolset.get_most_recent_stacked_ratchet() { -//! // Use current ratchet for encryption -//! } -//! ``` -//! //! ## Important Notes //! - Maximum number of ratchets in memory is configurable and environment-dependent //! - Static auxiliary ratchet provides persistent encryption for stored data @@ -49,7 +19,7 @@ //! - Thread-safe operations for concurrent access //! //! ## Related Components -//! - [`StackedRatchet`](crate::ratchets::stacked::stacked_ratchet::StackedRatchet): Core ratchet implementation +//! - [`StackedRatchet`](crate::ratchets::stacked::ratchet::StackedRatchet): Core ratchet implementation //! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Entropy source for ratchets //! - [`CryptError`](crate::misc::CryptError): Error handling for cryptographic operations //! - [`ClientNetworkAccount`]: High-level account management @@ -59,7 +29,7 @@ use std::collections::VecDeque; use serde::{Deserialize, Serialize}; use crate::misc::CryptError; -use crate::ratchets::stacked::stacked_ratchet::StackedRatchet; +use crate::ratchets::stacked::ratchet::StackedRatchet; use crate::ratchets::Ratchet; use std::ops::RangeInclusive; diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index 44fac7acc..b403412ec 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -10,7 +10,7 @@ mod tests { use citadel_crypt::entropy_bank::EntropyBank; #[cfg(not(target_family = "wasm"))] use citadel_crypt::packet_vector::PacketVector; - use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; + use citadel_crypt::ratchets::stacked::ratchet::StackedRatchet; use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupReceiver}; use citadel_crypt::toolset::{Toolset, ToolsetUpdateStatus, MAX_RATCHETS_IN_MEMORY}; diff --git a/citadel_pqcrypto/src/lib.rs b/citadel_pqcrypto/src/lib.rs index 2246280d2..791c7f7e4 100644 --- a/citadel_pqcrypto/src/lib.rs +++ b/citadel_pqcrypto/src/lib.rs @@ -37,11 +37,11 @@ //! //! // Create a new Bob instance using Alice's parameters //! let params = alice.generate_alice_to_bob_transfer().unwrap(); -//! let bob = PostQuantumContainer::new_bob(opts, params, &[]).unwrap(); +//! let bob = PostQuantumContainer::new_bob(opts, params, &[b"my-psk"]).unwrap(); //! //! // Complete the key exchange //! let bob_params = bob.generate_bob_to_alice_transfer().unwrap(); -//! alice.alice_on_receive_ciphertext(bob_params, &[]).unwrap(); +//! alice.alice_on_receive_ciphertext(bob_params, &[b"my-psk"]).unwrap(); //! //! // Now both parties can communicate securely //! ``` diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index 10ea33336..94182a3b9 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -504,8 +504,8 @@ pub mod prelude { #[cfg(not(coverage))] pub use citadel_crypt::argon::autotuner::calculate_optimal_argon_params; pub use citadel_crypt::ratchets::mono::keys::FcmKeys; - pub use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; - pub use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; + pub use citadel_crypt::ratchets::mono::MonoRatchet; + pub use citadel_crypt::ratchets::stacked::StackedRatchet; pub use citadel_crypt::ratchets::Ratchet; pub use citadel_types::crypto::AlgorithmsExt; pub use citadel_types::crypto::SecBuffer; diff --git a/citadel_proto/src/proto/misc/udp_internal_interface.rs b/citadel_proto/src/proto/misc/udp_internal_interface.rs index 806916936..a1e562c86 100644 --- a/citadel_proto/src/proto/misc/udp_internal_interface.rs +++ b/citadel_proto/src/proto/misc/udp_internal_interface.rs @@ -37,7 +37,7 @@ use bytes::{Bytes, BytesMut}; use citadel_io::tokio::net::UdpSocket; use citadel_io::tokio_util::udp::UdpFramed; use citadel_wire::exports::Connection; -use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; +use citadel_wire::udp_traversal::hole_punched_socket::TargettedSocketAddr; use futures::stream::{SplitSink, SplitStream}; use futures::{Sink, Stream, StreamExt}; use std::net::SocketAddr; diff --git a/citadel_proto/src/proto/node.rs b/citadel_proto/src/proto/node.rs index beb27061c..77a7786bd 100644 --- a/citadel_proto/src/proto/node.rs +++ b/citadel_proto/src/proto/node.rs @@ -145,9 +145,7 @@ impl Node { stun_servers.clone(), ); - let nat_type = NatType::identify(stun_servers) - .await - .map_err(|err| err.std())?; + let nat_type = NatType::identify(stun_servers).await?; let inner = NodeInner { underlying_proto, diff --git a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs index 2ac30a2ed..c445b5ae3 100644 --- a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs @@ -49,8 +49,8 @@ use citadel_crypt::endpoint_crypto_container::AssociatedSecurityLevel; use citadel_crypt::ratchets::Ratchet; +use citadel_wire::udp_traversal::hole_punched_socket::HolePunchedUdpSocket; use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; -use citadel_wire::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; use netbeam::sync::RelativeNodeType; use crate::constants::HOLE_PUNCH_SYNC_TIME_MULTIPLIER; diff --git a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs index e402ee044..1200b5e9b 100644 --- a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs +++ b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs @@ -626,8 +626,7 @@ impl ToolsetUpdate<'_, R> { constructor: R::Constructor, local_is_alice: bool, ) -> Result, CryptError> { - self.crypt - .update_sync_safe(constructor, local_is_alice, self.local_cid) + self.crypt.update_sync_safe(constructor, local_is_alice) } /// This should only be called after an update @@ -642,11 +641,8 @@ impl ToolsetUpdate<'_, R> { } /// Unlocks the internal state, allowing future upgrades to the system. Returns the latest hyper ratchet - pub(crate) fn unlock(&mut self, requires_locked_by_alice: bool) -> Option<(R, Option)> { - let lock_src = self.crypt.lock_set_by_alice; - self.crypt - .maybe_unlock(requires_locked_by_alice) - .map(|r| (r.clone(), lock_src)) + pub(crate) fn unlock(&mut self, _requires_locked_by_alice: bool) -> Option { + self.crypt.maybe_unlock().map(|r| r.clone()) } pub(crate) fn get_local_cid(&self) -> u64 { @@ -747,7 +743,7 @@ pub(crate) fn attempt_kem_as_alice_finish( .ok_or(())?, )) } else { - Ok(Some(toolset_update_method.unlock(true).ok_or(())?.0)) + Ok(Some(toolset_update_method.unlock(true).ok_or(())?)) } } } @@ -758,8 +754,8 @@ pub(crate) fn attempt_kem_as_alice_finish( } KemTransferStatus::Contended => match secrecy_mode { - SecrecyMode::Perfect => Ok(Some(toolset_update_method.unlock(true).ok_or(())?.0)), - SecrecyMode::BestEffort => Ok(Some(toolset_update_method.unlock(true).ok_or(())?.0)), + SecrecyMode::Perfect => Ok(Some(toolset_update_method.unlock(true).ok_or(())?)), + SecrecyMode::BestEffort => Ok(Some(toolset_update_method.unlock(true).ok_or(())?)), }, KemTransferStatus::StatusNoTransfer(_status) => { diff --git a/citadel_proto/src/proto/packet_processor/rekey_packet.rs b/citadel_proto/src/proto/packet_processor/rekey_packet.rs index cad87286f..c10e07069 100644 --- a/citadel_proto/src/proto/packet_processor/rekey_packet.rs +++ b/citadel_proto/src/proto/packet_processor/rekey_packet.rs @@ -263,10 +263,11 @@ pub fn process_rekey( // We update the internal latest version usable method.post_stage1_alice_or_bob(); - let lock_set_by_alice = return_if_none!(method.unlock(false)).1; + let _lock_set_by_alice = return_if_none!(method.unlock(false)); // if lock set by bob, do poll - let do_poll = lock_set_by_alice.map(|r| !r).unwrap_or(false); + //let do_poll = lock_set_by_alice.map(|r| !r).unwrap_or(false); + let do_poll = true; // If we didn't have to deregister, then our job is done. alice does not need to hear from Bob // But, if deregistration occurred, we need to alert alice that way she can unlock hers diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index bbdb608cb..c31f24326 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -83,8 +83,8 @@ use citadel_crypt::ratchets::Ratchet; use citadel_types::prelude::UdpMode; use citadel_user::re_exports::__private::Formatter; use citadel_wire::exports::tokio_rustls::rustls; +use citadel_wire::udp_traversal::hole_punched_socket::TargettedSocketAddr; use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; -use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; use citadel_wire::udp_traversal::udp_hole_puncher::EndpointHolePunchExt; use netbeam::sync::network_endpoint::NetworkEndpoint; use std::fmt::Debug; diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index 9cc331506..34a493bd8 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -64,7 +64,7 @@ use citadel_user::account_manager::AccountManager; use citadel_user::auth::proposed_credentials::ProposedCredentials; use citadel_user::client_account::ClientNetworkAccount; use citadel_wire::hypernode_type::NodeType; -use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; +use citadel_wire::udp_traversal::hole_punched_socket::TargettedSocketAddr; use netbeam::time_tracker::TimeTracker; //use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender, channel, TrySendError}; diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index 2898c0baa..fc3653463 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -219,7 +219,7 @@ impl CitadelSessionManager { } => ( *server_addr, None, - ProposedCredentials::passwordless(username.clone()), + ProposedCredentials::transient(username.clone()), ), AuthenticationRequest::Credentialed { id, password } => { diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index 3909d83dd..e23b30387 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -1980,7 +1980,7 @@ impl StateContainerInner { let latest_stacked_ratchet = crypt_container.get_ratchet(None).cloned().unwrap(); latest_stacked_ratchet.verify_level(Some(security_level)).map_err(|_err| NetworkError::Generic(format!("Invalid security level. The maximum security level for this session is {:?}", latest_stacked_ratchet.get_default_security_level())))?; - let constructor = crypt_container.get_next_constructor(called_from_poll); + let constructor = crypt_container.get_next_constructor(); let result = match secrecy_mode { SecrecyMode::BestEffort => { @@ -2079,9 +2079,8 @@ impl StateContainerInner { .unwrap() .clone(); latest_usable_ratchet.verify_level(Some(security_level)).map_err(|_err| NetworkError::Generic(format!("Invalid security level. The maximum security level for this session is {:?}", latest_usable_ratchet.get_default_security_level())))?; - let constructor = endpoint_container - .endpoint_crypto - .get_next_constructor(called_from_poll); + let constructor = + endpoint_container.endpoint_crypto.get_next_constructor(); match secrecy_mode { SecrecyMode::BestEffort => { @@ -2147,7 +2146,6 @@ impl StateContainerInner { //assert!(!called_from_poll); // Being called from poll should only happen when a packet needs to be sent, and is ready to be sent. Further, being called from the poll adds a lock ensuring it gets sent if called_from_poll { - log::error!(target: "citadel", "Should not happen (CFP). {:?}", endpoint_container.endpoint_crypto.lock_set_by_alice.clone()); std::process::exit(1); // for dev purposes } @@ -2267,7 +2265,7 @@ impl StateContainerInner { .unwrap() .peer_session_crypto; - match crypt_container.get_next_constructor(false) { + match crypt_container.get_next_constructor() { Some(alice_constructor) => { let ratchet = crypt_container.get_ratchet(None).unwrap(); let stage0_packet = packet_crafter::do_entropy_bank_update::craft_stage0( @@ -2327,7 +2325,7 @@ impl StateContainerInner { .as_mut() .ok_or(MISSING)?; let crypt = &mut endpoint_container.endpoint_crypto; - let alice_constructor = crypt.get_next_constructor(false); + let alice_constructor = crypt.get_next_constructor(); let latest_stacked_ratchet = crypt .get_ratchet(None) .cloned() diff --git a/citadel_user/src/account_loader.rs b/citadel_user/src/account_loader.rs index 0e06f1831..03f2f6233 100644 --- a/citadel_user/src/account_loader.rs +++ b/citadel_user/src/account_loader.rs @@ -13,17 +13,18 @@ //! //! # Example //! -//! ```rust +//! ```rust, no_run //! use citadel_user::account_loader; //! use citadel_user::directory_store::DirectoryStore; -//! use citadel_crypt::stacked_ratchet::DefaultRatchet; +//! use citadel_crypt::ratchets::stacked::StackedRatchet; //! +//! # fn get_store() -> DirectoryStore { todo!() } //! # fn main() -> Result<(), Box> { //! // Create a directory store for account management -//! let store = DirectoryStore::new("./accounts")?; +//! let store = get_store(); //! //! // Load all client network accounts -//! let accounts = account_loader::load_cnac_files::(&store)?; +//! let accounts = account_loader::load_cnac_files::(&store)?; //! //! // Process loaded accounts //! for (cid, account) in accounts { diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs index d24d4a4f9..5c17fb60a 100644 --- a/citadel_user/src/account_manager.rs +++ b/citadel_user/src/account_manager.rs @@ -31,10 +31,14 @@ //! //! ## Usage Example //! -//! ```rust +//! ```rust, no_run //! use citadel_user::prelude::*; -//! use citadel_crypt::stacked_ratchet::StackedRatchet; +//! use citadel_user::backend::BackendType; +//! use citadel_user::account_manager::AccountManager; +//! use citadel_crypt::ratchets::stacked::StackedRatchet; +//! use citadel_user::auth::proposed_credentials::ProposedCredentials; //! +//! # fn get_ratchet() -> StackedRatchet { todo!() } //! async fn example() -> Result<(), Box> { //! // Initialize account manager with in-memory backend //! let manager = AccountManager::::new( @@ -45,28 +49,20 @@ //! ).await?; //! //! // Register a new client account -//! let conn_info = ConnectionInfo::new( -//! SecurityLevel::Standard, -//! None, -//! None -//! ); +//! let conn_info = ConnectionInfo::new("127.0.0.1:12345")?; //! -//! let creds = ProposedCredentials::new( -//! "username".to_string(), -//! "password".to_string(), -//! None -//! ); +//! let creds = ProposedCredentials::transient("some-unique-id"); //! -//! let ratchet = StackedRatchet::new(1234, 0); +//! let ratchet = get_ratchet(); //! //! let account = manager.register_impersonal_hyperlan_client_network_account( //! conn_info, //! creds, //! ratchet -//! )?; +//! ).await?; //! //! // Retrieve peer information -//! if let Some(peers) = manager.get_hyperlan_peer_list(account.get_cid())? { +//! if let Some(peers) = manager.get_hyperlan_peer_list(account.get_cid()).await? { //! for peer_cid in peers { //! println!("Connected to peer: {}", peer_cid); //! } @@ -102,8 +98,8 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::ConnectionInfo; use crate::server_misc_settings::ServerMiscSettings; use citadel_crypt::argon::argon_container::{ArgonDefaultServerSettings, ArgonSettings}; -use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; -use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::mono::MonoRatchet; +use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_crypt::ratchets::Ratchet; use citadel_types::prelude::PeerInfo; use citadel_types::user::MutualPeer; diff --git a/citadel_user/src/auth/proposed_credentials.rs b/citadel_user/src/auth/proposed_credentials.rs index 46e5be9db..f799e11dd 100644 --- a/citadel_user/src/auth/proposed_credentials.rs +++ b/citadel_user/src/auth/proposed_credentials.rs @@ -93,8 +93,10 @@ impl ProposedCredentials { } /// Generates an empty skeleton for authless mode - pub const fn passwordless(username: String) -> Self { - Self::Disabled { username } + pub fn transient>(username: T) -> Self { + Self::Disabled { + username: username.into(), + } } /// Generates the proper registration credentials. Trims the username, password, and full name, removing any whitespace from the ends. Should only be called client-side diff --git a/citadel_user/src/backend/mod.rs b/citadel_user/src/backend/mod.rs index 0115285c8..0945b2e8b 100644 --- a/citadel_user/src/backend/mod.rs +++ b/citadel_user/src/backend/mod.rs @@ -44,8 +44,8 @@ use std::sync::Arc; use async_trait::async_trait; -use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; -use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::mono::MonoRatchet; +use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_crypt::ratchets::Ratchet; #[cfg(all(feature = "redis", not(coverage)))] diff --git a/citadel_user/src/backend/sql_backend.rs b/citadel_user/src/backend/sql_backend.rs index f1dbd6558..9bd2be9bc 100644 --- a/citadel_user/src/backend/sql_backend.rs +++ b/citadel_user/src/backend/sql_backend.rs @@ -38,7 +38,7 @@ use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX}; use crate::serialization::SyncIO; use async_trait::async_trait; use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; -use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::stacked::ratchet::StackedRatchet; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; diff --git a/citadel_user/src/client_account.rs b/citadel_user/src/client_account.rs index b8b1d8362..93e88c598 100644 --- a/citadel_user/src/client_account.rs +++ b/citadel_user/src/client_account.rs @@ -103,8 +103,8 @@ use crate::auth::proposed_credentials::ProposedCredentials; use crate::auth::DeclaredAuthenticationMode; use crate::serialization::SyncIO; use citadel_crypt::prelude::Toolset; -use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; -use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; +use citadel_crypt::ratchets::mono::MonoRatchet; +use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_types::crypto::SecBuffer; use citadel_types::user::MutualPeer; use std::collections::HashMap; @@ -299,7 +299,7 @@ impl ClientNetworkAccount { username.clone(), ), DeclaredAuthenticationMode::Passwordless { username, .. } => { - return Ok(ProposedCredentials::passwordless(username.clone())) + return Ok(ProposedCredentials::transient(username.clone())) } } }; diff --git a/citadel_user/src/connection_metadata.rs b/citadel_user/src/connection_metadata.rs index 174fece15..0cf5a0b74 100644 --- a/citadel_user/src/connection_metadata.rs +++ b/citadel_user/src/connection_metadata.rs @@ -63,6 +63,7 @@ //! * `citadel_wire` - Network communication //! +use crate::misc::AccountError; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::net::SocketAddr; @@ -74,6 +75,16 @@ pub struct ConnectionInfo { pub addr: SocketAddr, } +impl ConnectionInfo { + pub fn new(addr: T) -> Result { + let addr = addr + .to_socket_addrs()? + .next() + .ok_or_else(|| AccountError::msg("No socket address"))?; + Ok(ConnectionInfo { addr }) + } +} + #[derive(Clone, Serialize, Deserialize, Debug)] /// For saving the state of client-side connections pub enum ConnectProtocol { diff --git a/citadel_user/src/external_services/mod.rs b/citadel_user/src/external_services/mod.rs index f145f5374..45b7559e2 100644 --- a/citadel_user/src/external_services/mod.rs +++ b/citadel_user/src/external_services/mod.rs @@ -16,6 +16,8 @@ //! ## Usage Example //! //! ```rust,no_run +//! #[cfg(feature = "google-services")] +//! # fn test() { //! use citadel_user::external_services::{ServicesConfig, RtdbConfig}; //! //! // Create service configuration @@ -26,6 +28,7 @@ //! //! // Initialize services handler //! let handler = config.into_services_handler()?; +//! # } //! ``` //! //! ## Important Notes diff --git a/citadel_user/src/hypernode_account.rs b/citadel_user/src/hypernode_account.rs index ec304bd45..e08c3c3da 100644 --- a/citadel_user/src/hypernode_account.rs +++ b/citadel_user/src/hypernode_account.rs @@ -25,11 +25,14 @@ //! //! ```rust //! use citadel_user::prelude::*; +//! use citadel_user::account_manager::AccountManager; +//! use citadel_crypt::ratchets::stacked::StackedRatchet; +//! use citadel_user::backend::BackendType; //! use citadel_types::user::UserIdentifier; //! //! async fn example() -> Result<(), Box> { //! // Create account manager -//! let manager = AccountManager::new( +//! let manager = AccountManager::::new( //! BackendType::InMemory, //! None, //! None, diff --git a/citadel_user/src/misc.rs b/citadel_user/src/misc.rs index 3927aed4f..8647998fc 100644 --- a/citadel_user/src/misc.rs +++ b/citadel_user/src/misc.rs @@ -65,7 +65,6 @@ impl AccountError { pub(crate) fn msg>(msg: T) -> Self { Self::Generic(msg.into()) } - /// Consumes self and returns the underlying error message pub fn into_string(self) -> String { match self { @@ -82,12 +81,20 @@ impl AccountError { } } -impl From for AccountError { - fn from(err: T) -> Self { - AccountError::Generic(err.to_string()) +impl std::fmt::Display for AccountError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) } } +impl From for AccountError { + fn from(e: std::io::Error) -> Self { + AccountError::IoError(format!("{e}")) + } +} + +impl std::error::Error for AccountError {} + /// For passing metadata from a cnac #[derive(Debug)] pub struct CNACMetadata { diff --git a/citadel_user/tests/crypto.rs b/citadel_user/tests/crypto.rs index 53cbd1825..68347c423 100644 --- a/citadel_user/tests/crypto.rs +++ b/citadel_user/tests/crypto.rs @@ -1,10 +1,10 @@ #[cfg(test)] #[cfg(feature = "jwt-testing")] mod tests { - use citadel_crypt::ratchets::stacked::stacked_ratchet::constructor::{ + use citadel_crypt::ratchets::stacked::ratchet::constructor::{ BobToAliceTransferType, StackedRatchetConstructor, }; - use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; + use citadel_crypt::ratchets::stacked::ratchet::StackedRatchet; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use std::collections::HashMap; diff --git a/citadel_user/tests/primary.rs b/citadel_user/tests/primary.rs index b24b35162..b9c53a86d 100644 --- a/citadel_user/tests/primary.rs +++ b/citadel_user/tests/primary.rs @@ -1,15 +1,14 @@ #[cfg(test)] mod tests { use citadel_crypt::prelude::ConstructorOpts; - use citadel_crypt::ratchets::stacked::stacked_ratchet::constructor::StackedRatchetConstructor; - use citadel_crypt::ratchets::stacked::stacked_ratchet::StackedRatchet; + use citadel_crypt::ratchets::stacked::constructor::StackedRatchetConstructor; + use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_types::crypto::KemAlgorithm; use citadel_user::account_manager::AccountManager; use citadel_user::auth::proposed_credentials::ProposedCredentials; use citadel_user::backend::{BackendType, PersistenceHandler}; use citadel_user::client_account::ClientNetworkAccount; use futures::Future; - use std::str::FromStr; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; use citadel_io::tokio; @@ -20,7 +19,6 @@ mod tests { use citadel_user::misc::{AccountError, CNACMetadata}; use citadel_user::prelude::ConnectionInfo; use std::collections::HashMap; - use std::net::SocketAddr; #[derive(Clone)] struct TestContainer { @@ -45,9 +43,7 @@ mod tests { password: &str, full_name: &str, ) -> (ClientNetworkAccount, ClientNetworkAccount) { - let conn_info = ConnectionInfo { - addr: SocketAddr::from_str("127.0.0.1:12345").unwrap(), - }; + let conn_info = ConnectionInfo::new("127.0.0.1:12345").unwrap(); let cid = self .server_acc_mgr .get_persistence_handler() @@ -95,9 +91,7 @@ mod tests { peer_backend: BackendType, ) -> (ClientNetworkAccount, TestContainer) { // we assume same server node - let conn_info = ConnectionInfo { - addr: SocketAddr::from_str("127.0.0.1:54321").unwrap(), - }; + let conn_info = ConnectionInfo::new("127.0.0.1:54321").unwrap(); let server_acc_mgr = self.server_acc_mgr.clone(); let client_acc_mgr = acc_mgr(peer_backend).await; diff --git a/citadel_wire/src/error.rs b/citadel_wire/src/error.rs index d7013d036..5a33ec0a1 100644 --- a/citadel_wire/src/error.rs +++ b/citadel_wire/src/error.rs @@ -51,12 +51,6 @@ pub enum FirewallError { LocalIPAddrFail, } -impl FirewallError { - pub fn std(self) -> std::io::Error { - std::io::Error::new(std::io::ErrorKind::Other, self.to_string()) - } -} - impl std::fmt::Display for FirewallError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( @@ -73,6 +67,8 @@ impl std::fmt::Display for FirewallError { } } +impl std::error::Error for FirewallError {} + impl From for std::io::Error { fn from(val: FirewallError) -> Self { std::io::Error::new(std::io::ErrorKind::Other, val.to_string()) diff --git a/citadel_wire/src/standard/mod.rs b/citadel_wire/src/standard/mod.rs index 567a24872..87b2c3b03 100644 --- a/citadel_wire/src/standard/mod.rs +++ b/citadel_wire/src/standard/mod.rs @@ -16,7 +16,7 @@ //! # Examples //! //! ```rust -//! use citadel_wire::standard::{ +//! use citadel_wire::{ //! nat_identification::NatType, //! socket_helpers, //! upnp_handler::UPnPHandler @@ -24,7 +24,7 @@ //! //! async fn setup_network() -> Result<(), anyhow::Error> { //! // Identify NAT type -//! let nat = NatType::identify(None)?; +//! let nat = NatType::identify(None).await?; //! //! // Setup UPnP if available //! if let Ok(upnp) = UPnPHandler::new(None).await { @@ -32,7 +32,7 @@ //! } //! //! // Create network sockets -//! let addr = "127.0.0.1:8080".parse()?; +//! let addr: std::net::SocketAddr = "127.0.0.1:8080".parse()?; //! let socket = socket_helpers::get_reuse_udp_socket(addr)?; //! //! Ok(()) diff --git a/citadel_wire/src/standard/nat_identification.rs b/citadel_wire/src/standard/nat_identification.rs index 278e8208a..9a71a0602 100644 --- a/citadel_wire/src/standard/nat_identification.rs +++ b/citadel_wire/src/standard/nat_identification.rs @@ -16,14 +16,15 @@ //! # Examples //! //! ```rust -//! use citadel_wire::standard::nat_identification::NatType; +//! use citadel_wire::nat_identification::NatType; +//! use citadel_wire::nat_identification::TraversalTypeRequired; //! -//! async fn identify_nat() -> Result<(), anyhow::Error> { +//! async fn identify_nat() -> Result<(), citadel_wire::error::FirewallError> { //! // Identify NAT type using default STUN servers -//! let nat = NatType::identify(None)?; +//! let nat = NatType::identify(None).await?; //! //! // Check if peer-to-peer connection is possible -//! if nat.traversal_type_required().is_direct() { +//! if nat.traversal_type_required() == TraversalTypeRequired::Direct { //! println!("Direct connection possible"); //! } //! diff --git a/citadel_wire/src/standard/quic.rs b/citadel_wire/src/standard/quic.rs index 8de28c1af..dd5a55fdd 100644 --- a/citadel_wire/src/standard/quic.rs +++ b/citadel_wire/src/standard/quic.rs @@ -17,7 +17,7 @@ //! # Examples //! //! ```rust -//! use citadel_wire::standard::quic::{QuicServer, QuicClient}; +//! use citadel_wire::quic::{QuicServer, QuicClient}; //! use citadel_io::tokio::net::UdpSocket; //! //! async fn setup_quic() -> Result<(), anyhow::Error> { diff --git a/citadel_wire/src/standard/socket_helpers.rs b/citadel_wire/src/standard/socket_helpers.rs index a4e901d2a..30ba4de88 100644 --- a/citadel_wire/src/standard/socket_helpers.rs +++ b/citadel_wire/src/standard/socket_helpers.rs @@ -17,7 +17,7 @@ //! # Examples //! //! ```rust -//! use citadel_wire::standard::socket_helpers; +//! use citadel_wire::socket_helpers; //! use std::net::SocketAddr; //! use std::time::Duration; //! @@ -31,7 +31,7 @@ //! let tcp = socket_helpers::get_tcp_listener(addr)?; //! //! // Create TCP client with timeout -//! let stream = socket_helpers::get_tcp_stream(addr, Duration::from_secs(5))?; +//! let stream = socket_helpers::get_tcp_stream(addr, Duration::from_secs(5)).await?; //! //! Ok(()) //! } diff --git a/citadel_wire/src/standard/tls.rs b/citadel_wire/src/standard/tls.rs index 42ae195d9..ab45c1cdc 100644 --- a/citadel_wire/src/standard/tls.rs +++ b/citadel_wire/src/standard/tls.rs @@ -18,7 +18,7 @@ //! # Examples //! //! ```rust -//! use citadel_wire::standard::tls; +//! use citadel_wire::tls; //! //! async fn setup_tls() -> Result<(), anyhow::Error> { //! // Create self-signed server config @@ -28,7 +28,7 @@ //! let certs = tls::load_native_certs_async().await?; //! //! // Create secure client config -//! let client_config = tls::create_client_config(&[&certs])?; +//! let client_config = tls::create_client_config(&certs).await?; //! //! Ok(()) //! } @@ -44,10 +44,10 @@ //! //! # Related Components //! -//! - [`crate::standard::quic`] - QUIC protocol support +//! - [`crate::quic`] - QUIC protocol support //! - [`crate::exports::Certificate`] - Certificate types //! - [`crate::exports::PrivateKey`] - Key management -//! - [`crate::standard::socket_helpers`] - Socket utilities +//! - [`crate::socket_helpers`] - Socket utilities //! use crate::exports::{Certificate, PrivateKey}; @@ -111,7 +111,9 @@ pub fn cert_vec_to_secure_client_config( Ok(crate::quic::secure::client_config(root_store)) } -pub async fn create_client_config(allowed_certs: &[&[u8]]) -> Result { +pub async fn create_client_config>( + allowed_certs: &[T], +) -> Result { Ok(client_config_to_tls_connector(Arc::new( create_rustls_client_config(allowed_certs)?, ))) diff --git a/citadel_wire/src/standard/upnp_handler.rs b/citadel_wire/src/standard/upnp_handler.rs index 7e5105c83..87e3a007e 100644 --- a/citadel_wire/src/standard/upnp_handler.rs +++ b/citadel_wire/src/standard/upnp_handler.rs @@ -16,11 +16,11 @@ //! # Examples //! //! ```rust -//! use citadel_wire::standard::upnp_handler::UPnPHandler; +//! use citadel_wire::upnp_handler::UPnPHandler; //! use igd::PortMappingProtocol; //! use std::time::Duration; //! -//! async fn setup_port_forwarding() -> Result<(), anyhow::Error> { +//! async fn setup_port_forwarding() -> Result<(), citadel_wire::error::FirewallError> { //! // Create UPnP handler with 5 second timeout //! let upnp = UPnPHandler::new(Some(Duration::from_secs(5))).await?; //! diff --git a/citadel_wire/src/udp_traversal/hole_punch_config.rs b/citadel_wire/src/udp_traversal/hole_punch_config.rs index ae9966869..ae3dfef99 100644 --- a/citadel_wire/src/udp_traversal/hole_punch_config.rs +++ b/citadel_wire/src/udp_traversal/hole_punch_config.rs @@ -44,7 +44,7 @@ //! use std::net::SocketAddr; //! //! async fn setup_hole_punch() -> Result<(), anyhow::Error> { -//! let peer_nat = NatType::identify(None)?; +//! let peer_nat = NatType::identify(None).await?; //! let peer_addr = "192.168.1.2:8080".parse()?; //! let socket = UdpSocket::bind("0.0.0.0:0").await?; //! @@ -56,7 +56,7 @@ //! //! for addrs in config { //! // Try connecting to predicted addresses -//! println!("Trying addresses: {:?}", addrs); +//! println!("Trying addresses: {addrs:?}"); //! } //! //! Ok(()) diff --git a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs b/citadel_wire/src/udp_traversal/hole_punched_socket.rs similarity index 96% rename from citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs rename to citadel_wire/src/udp_traversal/hole_punched_socket.rs index 6adbd605c..94ed6da0b 100644 --- a/citadel_wire/src/udp_traversal/targetted_udp_socket_addr.rs +++ b/citadel_wire/src/udp_traversal/hole_punched_socket.rs @@ -5,7 +5,7 @@ //! ## Example //! //! ```rust,no_run -//! use citadel_wire::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; +//! use citadel_wire::udp_traversal::hole_punched_socket::TargettedSocketAddr; //! use std::net::SocketAddr; //! //! fn example() { @@ -36,7 +36,7 @@ //! # Examples //! //! ```rust -//! use citadel_wire::udp_traversal::targetted_udp_socket_addr::{ +//! use citadel_wire::udp_traversal::hole_punched_socket::{ //! TargettedSocketAddr, HolePunchedUdpSocket //! }; //! use std::net::SocketAddr; @@ -182,7 +182,7 @@ impl HolePunchedUdpSocket { } // After hole-punching, some packets may be sent that need to be flushed // this cleanses the stream - pub(crate) fn cleanse(&self) -> std::io::Result<()> { + pub fn cleanse(&self) -> std::io::Result<()> { let buf = &mut [0u8; 4096]; loop { match self.socket.try_recv(buf) { diff --git a/citadel_wire/src/udp_traversal/linear/method3.rs b/citadel_wire/src/udp_traversal/linear/method3.rs index 7f4d0dc06..644e14da7 100644 --- a/citadel_wire/src/udp_traversal/linear/method3.rs +++ b/citadel_wire/src/udp_traversal/linear/method3.rs @@ -70,8 +70,8 @@ use serde::{Deserialize, Serialize}; use crate::error::FirewallError; use crate::socket_helpers::ensure_ipv6; +use crate::udp_traversal::hole_punched_socket::TargettedSocketAddr; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; -use crate::udp_traversal::targetted_udp_socket_addr::TargettedSocketAddr; use crate::udp_traversal::HolePunchID; use citadel_io::Mutex; use netbeam::sync::RelativeNodeType; diff --git a/citadel_wire/src/udp_traversal/linear/mod.rs b/citadel_wire/src/udp_traversal/linear/mod.rs index cfdaa352f..9df6de877 100644 --- a/citadel_wire/src/udp_traversal/linear/mod.rs +++ b/citadel_wire/src/udp_traversal/linear/mod.rs @@ -14,34 +14,6 @@ //! - Connection state tracking //! - Ping-based timing control //! -//! # Examples -//! -//! ```rust -//! use citadel_wire::udp_traversal::linear::{SingleUDPHolePuncher, NatTraversalMethod}; -//! use netbeam::sync::RelativeNodeType; -//! use std::net::SocketAddr; -//! -//! async fn establish_server_connection( -//! socket: UdpSocket, -//! server_addrs: Vec, -//! config: HolePunchConfigContainer -//! ) -> Result { -//! let mut puncher = SingleUDPHolePuncher::new( -//! RelativeNodeType::Client, -//! config, -//! socket, -//! server_addrs -//! )?; -//! -//! // Try UPnP first -//! if let Some(method) = puncher.get_next_method() { -//! puncher.try_method(method, kill_switch, post_kill_rebuild).await -//! } else { -//! Err(FirewallError::AllMethodsExhausted) -//! } -//! } -//! ``` -//! //! # Important Notes //! //! - Pre-process stage required @@ -67,9 +39,9 @@ use either::Either; use igd::PortMappingProtocol; use crate::error::FirewallError; +use crate::udp_traversal::hole_punched_socket::{HolePunchedUdpSocket, TargettedSocketAddr}; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; use crate::udp_traversal::linear::method3::Method3; -use crate::udp_traversal::targetted_udp_socket_addr::{HolePunchedUdpSocket, TargettedSocketAddr}; use crate::udp_traversal::{HolePunchID, NatTraversalMethod}; use crate::upnp_handler::UPnPHandler; use netbeam::sync::RelativeNodeType; diff --git a/citadel_wire/src/udp_traversal/mod.rs b/citadel_wire/src/udp_traversal/mod.rs index 08e7e0b11..1bab60269 100644 --- a/citadel_wire/src/udp_traversal/mod.rs +++ b/citadel_wire/src/udp_traversal/mod.rs @@ -60,7 +60,7 @@ use uuid::Uuid; /// Linear hole-punching pub mod linear; -pub mod targetted_udp_socket_addr; +pub mod hole_punched_socket; pub mod udp_hole_puncher; diff --git a/citadel_wire/src/udp_traversal/multi/mod.rs b/citadel_wire/src/udp_traversal/multi/mod.rs index a548f9f60..9e1394471 100644 --- a/citadel_wire/src/udp_traversal/multi/mod.rs +++ b/citadel_wire/src/udp_traversal/multi/mod.rs @@ -18,7 +18,11 @@ //! //! ```rust //! use citadel_wire::udp_traversal::multi::DualStackUdpHolePuncher; +//! use citadel_wire::udp_traversal::hole_punched_socket::HolePunchedUdpSocket; +//! use citadel_wire::udp_traversal::hole_punch_config::HolePunchConfig; +//! use citadel_wire::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; //! use netbeam::sync::RelativeNodeType; +//! use netbeam::sync::network_endpoint::NetworkEndpoint; //! //! async fn establish_connection( //! node_type: RelativeNodeType, @@ -55,9 +59,9 @@ use crate::error::FirewallError; use crate::udp_traversal::hole_punch_config::HolePunchConfig; +use crate::udp_traversal::hole_punched_socket::HolePunchedUdpSocket; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; use crate::udp_traversal::linear::SingleUDPHolePuncher; -use crate::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; use crate::udp_traversal::{HolePunchID, NatTraversalMethod}; use citadel_io::tokio::sync::mpsc::UnboundedReceiver; use futures::future::select_ok; @@ -78,7 +82,7 @@ use std::time::Duration; /// Punches a hole using IPv4/6 addrs. IPv6 is more traversal-friendly since IP-translation between external and internal is not needed (unless the NAT admins are evil) /// /// allows the inclusion of a "breadth" variable to allow opening multiple ports for traversing across multiple ports -pub(crate) struct DualStackUdpHolePuncher { +pub struct DualStackUdpHolePuncher { // the key is the local bind addr future: Pin> + Send + 'static>>, diff --git a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs index fd9a4b50a..7cf800b4d 100644 --- a/citadel_wire/src/udp_traversal/udp_hole_puncher.rs +++ b/citadel_wire/src/udp_traversal/udp_hole_puncher.rs @@ -14,31 +14,6 @@ //! - Encrypted configuration exchange //! - Network endpoint integration //! -//! # Examples -//! -//! ```rust,no_run -//! use citadel_wire::udp_traversal::udp_hole_puncher::UdpHolePuncher; -//! use citadel_wire::udp_traversal::hole_punch_config::HolePunchConfig; -//! use citadel_wire::nat_identification::NatType; -//! use citadel_wire::error::FirewallError; -//! use citadel_io::tokio::net::UdpSocket; -//! use std::net::SocketAddr; -//! use anyhow::Result; -//! -//! async fn example() -> Result<()> { -//! // Create socket and config -//! let socket = UdpSocket::bind("0.0.0.0:0").await?; -//! let target_addr = "127.0.0.1:8080".parse::().unwrap(); -//! let peer_nat = NatType::identify(None).await?; -//! let config = HolePunchConfig::new(&peer_nat, &[target_addr], vec![socket]); -//! -//! // Create hole puncher -//! let hole_puncher = UdpHolePuncher::new(config); -//! -//! Ok(()) -//! } -//! ``` -//! //! # Important Notes //! //! - Requires coordinated peer configuration @@ -52,14 +27,14 @@ //! - [`crate::nat_identification`] - NAT behavior analysis //! - [`crate::udp_traversal::hole_punch_config`] - Configuration //! - [`crate::standard::socket_helpers`] - Socket utilities -//! - [`crate::udp_traversal::targetted_udp_socket_addr`] - Socket management +//! - [`crate::udp_traversal::hole_punched_socket`] - Socket management //! use crate::nat_identification::{NatType, IDENTIFY_TIMEOUT}; use crate::udp_traversal::hole_punch_config::HolePunchConfig; +use crate::udp_traversal::hole_punched_socket::HolePunchedUdpSocket; use crate::udp_traversal::linear::encrypted_config_container::HolePunchConfigContainer; use crate::udp_traversal::multi::DualStackUdpHolePuncher; -use crate::udp_traversal::targetted_udp_socket_addr::HolePunchedUdpSocket; use citadel_io::tokio::net::UdpSocket; use futures::Future; use netbeam::reliable_conn::ReliableOrderedStreamToTargetExt; From 338e2316efdb7e2978a157fae0ff571fc0a05909 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Mon, 23 Dec 2024 15:40:08 -0500 Subject: [PATCH 09/12] docs: get cargo test --docs passing for all crates --- citadel_crypt/src/sync_toggle.rs | 1 + citadel_crypt/tests/primary.rs | 10 +-- citadel_logging/src/lib.rs | 12 ++- citadel_proto/Cargo.toml | 2 +- citadel_proto/src/constants.rs | 86 +++---------------- citadel_proto/src/error.rs | 7 +- citadel_proto/src/functional.rs | 16 ---- citadel_proto/src/inner_arg.rs | 20 ----- citadel_proto/src/kernel/mod.rs | 21 ----- citadel_proto/src/lib.rs | 5 +- citadel_proto/src/proto/codec.rs | 20 ----- .../proto/misc/session_security_settings.rs | 9 +- citadel_proto/src/proto/outbound_sender.rs | 15 ---- citadel_proto/src/proto/packet_crafter.rs | 42 +-------- .../packet_processor/deregister_packet.rs | 20 ----- .../packet_processor/disconnect_packet.rs | 20 ----- .../src/proto/packet_processor/file_packet.rs | 20 ----- .../src/proto/packet_processor/hole_punch.rs | 21 ----- .../src/proto/packet_processor/mod.rs | 13 --- .../packet_processor/peer/group_broadcast.rs | 13 --- .../packet_processor/peer/peer_cmd_packet.rs | 31 ------- .../packet_processor/preconnect_packet.rs | 20 ----- .../packet_processor/primary_group_packet.rs | 23 +---- .../proto/packet_processor/register_packet.rs | 24 ------ .../proto/packet_processor/rekey_packet.rs | 19 ---- citadel_proto/src/proto/peer/channel.rs | 28 +++--- citadel_proto/src/proto/peer/group_channel.rs | 19 ++-- .../peer/hole_punch_compat_sink_stream.rs | 18 ---- citadel_proto/src/proto/peer/message_group.rs | 19 ---- citadel_proto/src/proto/peer/mod.rs | 14 --- .../src/proto/peer/p2p_conn_handler.rs | 31 +------ citadel_proto/src/proto/peer/peer_crypt.rs | 22 ----- citadel_proto/src/proto/peer/peer_layer.rs | 18 ---- citadel_proto/src/proto/remote.rs | 16 ++-- citadel_proto/src/proto/session.rs | 25 +----- citadel_proto/src/proto/state_container.rs | 32 ------- .../connect_state_container.rs | 16 ---- .../deregister_state_container.rs | 13 --- .../meta_expiry_container.rs | 15 ---- .../src/proto/state_subcontainers/mod.rs | 20 ----- .../peer_kem_state_container.rs | 20 ----- .../preconnect_state_container.rs | 21 ----- .../register_state_container.rs | 17 ---- .../state_subcontainers/rekey_container.rs | 34 ++------ citadel_proto/src/proto/transfer_stats.rs | 16 ---- citadel_sdk/src/fs.rs | 2 +- .../server/accept_file_transfer_kernel.rs | 2 +- .../prefabs/server/client_connect_listener.rs | 2 +- citadel_sdk/src/prefabs/server/empty.rs | 2 +- .../src/prefabs/server/internal_service.rs | 2 +- citadel_sdk/src/prefabs/server/mod.rs | 8 +- .../src/prefabs/shared/internal_service.rs | 4 +- citadel_sdk/src/remote_ext.rs | 3 +- citadel_sdk/src/test_common.rs | 2 +- citadel_types/src/lib.rs | 8 +- .../c2s/client_basic_transient_connection.rs | 9 +- .../c2s/client_basic_with_server_password.rs | 2 +- example-library/examples/c2s/client_echo.rs | 4 +- example-library/examples/c2s/server_basic.rs | 2 +- .../c2s/server_basic_with_password.rs | 2 +- example-library/examples/c2s/server_echo.rs | 2 +- example-library/examples/p2p/chat.rs | 4 +- example-library/examples/p2p/file_transfer.rs | 2 +- example-library/examples/p2p/revfs_delete.rs | 4 +- .../examples/p2p/revfs_read_write.rs | 4 +- example-library/examples/p2p/revfs_take.rs | 2 +- 66 files changed, 111 insertions(+), 865 deletions(-) diff --git a/citadel_crypt/src/sync_toggle.rs b/citadel_crypt/src/sync_toggle.rs index 3f4ac4f41..6efdaf4cb 100644 --- a/citadel_crypt/src/sync_toggle.rs +++ b/citadel_crypt/src/sync_toggle.rs @@ -35,6 +35,7 @@ //! // Reset to off //! toggle.toggle_off(); //! } +//! # manage_state(); //! ``` //! //! # Important Notes diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index b403412ec..b1aeaee22 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -10,7 +10,7 @@ mod tests { use citadel_crypt::entropy_bank::EntropyBank; #[cfg(not(target_family = "wasm"))] use citadel_crypt::packet_vector::PacketVector; - use citadel_crypt::ratchets::stacked::ratchet::StackedRatchet; + use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupReceiver}; use citadel_crypt::toolset::{Toolset, ToolsetUpdateStatus, MAX_RATCHETS_IN_MEMORY}; @@ -245,13 +245,13 @@ mod tests { citadel_logging::setup_log(); for x in 0u8..KEM_ALGORITHM_COUNT { for _sec in 0..SecurityLevel::Extreme.value() { - let _ = gen_ratchet::( + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::AES_GCM_256, None, &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); - let _ = gen_ratchet::( + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::ChaCha20Poly_1305, None, &PRE_SHARED_KEYS, @@ -266,13 +266,13 @@ mod tests { fn mono_ratchets_fail() { for x in 0u8..KEM_ALGORITHM_COUNT { for sec in 1..SecurityLevel::Extreme.value() { - let _ = gen_ratchet::( + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::AES_GCM_256, Some(sec.into()), &PRE_SHARED_KEYS, &PRE_SHARED_KEYS, ); - let _ = gen_ratchet::( + let _ = gen_ratchet::( KemAlgorithm::from_u8(x).unwrap() + EncryptionAlgorithm::ChaCha20Poly_1305, Some(sec.into()), &PRE_SHARED_KEYS, diff --git a/citadel_logging/src/lib.rs b/citadel_logging/src/lib.rs index bc0a5ac33..1a2c1d325 100644 --- a/citadel_logging/src/lib.rs +++ b/citadel_logging/src/lib.rs @@ -21,16 +21,14 @@ //! setup_log(); //! //! // Log at different levels +//! # #[derive(Debug)] +//! # struct Config; +//! # let config = Config; +//! # let error = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "Connection failed"); +//! //! info!(target: "citadel", "Starting application..."); //! debug!(target: "citadel", "Configuration loaded: {:?}", config); //! error!(target: "citadel", "Failed to connect: {}", error); -//! -//! // Use instrumentation for async functions -//! #[instrument] -//! async fn process_request() { -//! debug!(target: "citadel", "Processing request..."); -//! // ... processing ... -//! } //! ``` //! //! ## Log Levels diff --git a/citadel_proto/Cargo.toml b/citadel_proto/Cargo.toml index 34b4b9bfe..baaf75c93 100644 --- a/citadel_proto/Cargo.toml +++ b/citadel_proto/Cargo.toml @@ -12,7 +12,7 @@ categories = ["cryptography", "network-programming", "asynchronous"] license = "MIT OR Apache-2.0" [features] -default = ["filesystem", "multi-threaded", "std"] +default = ["filesystem", "std"] filesystem = ["citadel_user/filesystem"] multi-threaded = [] sql = ["citadel_user/sql"] diff --git a/citadel_proto/src/constants.rs b/citadel_proto/src/constants.rs index a28c4fe76..f67b5e5ec 100644 --- a/citadel_proto/src/constants.rs +++ b/citadel_proto/src/constants.rs @@ -11,31 +11,6 @@ //! - **Port Configuration**: Network port ranges and defaults //! - **Security Levels**: Update frequency bases for different security levels //! -//! # Usage Example -//! ```rust -//! use citadel_proto::constants::{ -//! PROTOCOL_VERSION, -//! MTU, -//! MAX_PAYLOAD_SIZE_IPV4, -//! MAX_PAYLOAD_SIZE_IPV6, -//! PRIMARY_PORT -//! }; -//! -//! // Check protocol version compatibility -//! let version = *PROTOCOL_VERSION; -//! -//! // Calculate maximum payload size based on IP version -//! let max_payload = if uses_ipv6 { -//! MAX_PAYLOAD_SIZE_IPV6 -//! } else { -//! MAX_PAYLOAD_SIZE_IPV4 -//! }; -//! -//! // Ensure payload fits within MTU -//! assert!(payload.len() <= max_payload); -//! assert!(max_payload < MTU); -//! ``` -//! //! # Important Notes //! - Protocol version uses semantic versioning (major.minor.patch) //! - All timing constants are in nanoseconds unless specified @@ -70,44 +45,15 @@ lazy_static! { /// by default, the UDP is not initialized pub const UDP_MODE: UdpMode = UdpMode::Disabled; -/// Setting this option to zero will imply an RST gets sent once close() is called. This will lead to packets possibly being undelivered -pub const DEFAULT_SO_LINGER_TIME: std::time::Duration = std::time::Duration::from_millis(1000); /// For calculating network latency pub const NANOSECONDS_PER_SECOND: i64 = 1_000_000_000; -/// The length of an ethernet header. Source: `` -pub const LAYER2_ETHERNET_HEADER_BYTE_LEN: usize = 18; -/// The IPv4 Header len -pub const LAYER3_IPV4_HEADER_BYTE_LEN: usize = 20; -/// The IPv6 Header len -pub const LAYER3_IPV6_HEADER_BYTE_LEN: usize = 40; -/// The UDP header len -pub const UDP_HEADER_BYTE_LEN: usize = 8; /// The HDP header len pub const HDP_HEADER_BYTE_LEN: usize = std::mem::size_of::(); -/// Assuming IPv6, this is the smallest MTU possible -pub const MTU: usize = 1280; -/// Total length of a packet's header -pub const BASE_HEADER_LEN_IPV4: usize = LAYER2_ETHERNET_HEADER_BYTE_LEN - + LAYER3_IPV4_HEADER_BYTE_LEN - + UDP_HEADER_BYTE_LEN - + HDP_HEADER_BYTE_LEN; -/// Total length of a packet's header -pub const BASE_HEADER_LEN_IPV6: usize = LAYER2_ETHERNET_HEADER_BYTE_LEN - + LAYER3_IPV6_HEADER_BYTE_LEN - + UDP_HEADER_BYTE_LEN - + HDP_HEADER_BYTE_LEN; -/// This is the maximum size an IPv4's packet can be -pub const MAX_PAYLOAD_SIZE_IPV4: usize = MTU - BASE_HEADER_LEN_IPV4; -/// This is the maximum size an IPv6's packet can be -pub const MAX_PAYLOAD_SIZE_IPV6: usize = MTU - BASE_HEADER_LEN_IPV6; /// the initial reconnect delay pub const INITIAL_RECONNECT_LOCKOUT_TIME_NS: i64 = NANOSECONDS_PER_SECOND; pub const KEEP_ALIVE_INTERVAL_MS: u64 = 60000 * 15; // every 15 minutes /// The keep alive max interval pub const KEEP_ALIVE_TIMEOUT_NS: i64 = (KEEP_ALIVE_INTERVAL_MS * 3 * 1_000_000) as i64; -// 1ms = 1 million ns -/// Timeout for the entropy_bank update subroutine -pub const DRILL_UPDATE_TIMEOUT_NS: i64 = KEEP_ALIVE_TIMEOUT_NS; /// For setting up the GroupReceivers pub const GROUP_TIMEOUT_MS: usize = KEEP_ALIVE_INTERVAL_MS as usize; pub const INDIVIDUAL_WAVE_TIMEOUT_MS: usize = GROUP_TIMEOUT_MS / 2; @@ -115,8 +61,6 @@ pub const DO_DEREGISTER_EXPIRE_TIME_NS: i64 = KEEP_ALIVE_TIMEOUT_NS; /// The frequency at which KEEP_ALIVES need to be sent through the system pub const FIREWALL_KEEP_ALIVE_UDP: std::time::Duration = std::time::Duration::from_secs(60); -/// The largest size, in bytes, that a single group can hold (~8 Megs) -pub const MAX_GROUP_SIZE_BYTES: usize = 1_000_000 * 8; /// How many bytes are stored pub const CODEC_BUFFER_CAPACITY: usize = u16::MAX as usize; /// The minimum number of bytes allocated in the codec @@ -127,32 +71,22 @@ pub const GROUP_EXPIRE_TIME_MS: std::time::Duration = std::time::Duration::from_ pub const DO_REGISTER_EXPIRE_TIME_MS: std::time::Duration = std::time::Duration::from_millis(10000); /// After this time, the connect state is invalidated pub const DO_CONNECT_EXPIRE_TIME_MS: std::time::Duration = std::time::Duration::from_millis(8000); -/// After this timeout, -pub const UPNP_FIREWALL_LOAD_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(1500); -pub const MULTIPORT_START: u16 = 25000; -//pub const MULTIPORT_END: u16 = citadel_crypt::entropy_bank::PORT_RANGE as u16 + MULTIPORT_START; -pub const MULTIPORT_END: u16 = 1 + MULTIPORT_START; -pub const PRIMARY_PORT: u16 = 25021; -/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) -pub const DRILL_UPDATE_FREQUENCY_LOW_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) -pub const DRILL_UPDATE_FREQUENCY_MEDIUM_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) -pub const DRILL_UPDATE_FREQUENCY_HIGH_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) -pub const DRILL_UPDATE_FREQUENCY_ULTRA_BASE: u64 = 480 * 1_000_000_000; -/// The minimum time (in nanoseconds) per entropy_bank update (nanoseconds per update) -pub const DRILL_UPDATE_FREQUENCY_DIVINE_BASE: u64 = 480 * 1_000_000_000; +/// The minimum time (in nanoseconds) per rekey (nanoseconds per update) +pub const REKEY_UPDATE_FREQUENCY_STANDARD: u64 = 480 * 1_000_000_000; +/// The minimum time (in nanoseconds) per rekey (nanoseconds per update) +pub const REKEY_UPDATE_FREQUENCY_REINFORCED: u64 = 480 * 1_000_000_000; +/// The minimum time (in nanoseconds) per rekey (nanoseconds per update) +pub const REKEY_UPDATE_FREQUENCY_HIGH: u64 = 480 * 1_000_000_000; +/// The minimum time (in nanoseconds) per rekey (nanoseconds per update) +pub const REKEY_UPDATE_FREQUENCY_ULTRA: u64 = 480 * 1_000_000_000; +/// The minimum time (in nanoseconds) per rekey (nanoseconds per update) +pub const REKEY_UPDATE_FREQUENCY_EXTREME: u64 = 480 * 1_000_000_000; /// For ensuring that the hole-punching process begin at about the same time (required) /// this is applied to the ping. If the ping is 200ms, the a multiplier of 2.0 will mean that in 200*2.0 = 400ms, /// the hole-punching process will begin pub const HOLE_PUNCH_SYNC_TIME_MULTIPLIER: f64 = 2.0f64; -pub const TIMED_TICKET_LIFETIME: std::time::Duration = std::time::Duration::from_secs(30); /// the preconnect + connect stage will be limited by this duration pub const LOGIN_EXPIRATION_TIME: std::time::Duration = std::time::Duration::from_secs(20); -/// Every 30 minutes, resync the clocks. This was to fix bugs related to long-lasting connections and reconnections -pub const NTP_RESYNC_FREQUENCY: std::time::Duration = std::time::Duration::from_secs(60 * 30); pub const TCP_CONN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(4); pub const MAX_OUTGOING_UNPROCESSED_REQUESTS: usize = 512; -pub const MAX_INCOMING_UNPROCESSED_REQUESTS: usize = 512; diff --git a/citadel_proto/src/error.rs b/citadel_proto/src/error.rs index f303b03da..7fbd1f4bd 100644 --- a/citadel_proto/src/error.rs +++ b/citadel_proto/src/error.rs @@ -12,7 +12,7 @@ //! //! # Usage //! ```rust -//! use citadel_proto::NetworkError; +//! use citadel_proto::prelude::NetworkError; //! //! // Create a generic error with a message //! let error = NetworkError::msg("Connection failed"); @@ -60,6 +60,7 @@ pub enum NetworkError { reason: String, }, ProperShutdown, + IoError(std::io::Error), } impl Error for NetworkError {} @@ -87,6 +88,7 @@ impl NetworkError { NetworkError::InvalidPacket(err) => (*err).to_string(), NetworkError::ProperShutdown => "Proper shutdown called".to_string(), NetworkError::NodeRemoteSendError { reason, .. } => reason.clone(), + NetworkError::IoError(err) => err.to_string(), } } @@ -108,6 +110,7 @@ impl NetworkError { NetworkError::ProperShutdown => { format!("{:?}", NetworkError::ProperShutdown) } + NetworkError::IoError(err) => err.to_string(), } } @@ -148,6 +151,6 @@ impl From for NetworkError { impl From for NetworkError { fn from(err: std::io::Error) -> Self { - NetworkError::Generic(err.to_string()) + NetworkError::IoError(err) } } diff --git a/citadel_proto/src/functional.rs b/citadel_proto/src/functional.rs index 6ed9b81c1..bc8704608 100644 --- a/citadel_proto/src/functional.rs +++ b/citadel_proto/src/functional.rs @@ -12,22 +12,6 @@ //! - Lazy evaluation support //! - Type-safe conditional operations //! -//! # Usage Example -//! -//! ```rust -//! use citadel_proto::functional::Then; -//! use citadel_proto::functional::IfTrueConditional; -//! -//! // Method chaining -//! let result = 42.then(|x| x * 2) -//! .then(|x| x.to_string()); -//! -//! // Conditional operations -//! let value = true.if_true(1) -//! .if_false(0); -//! assert_eq!(value, 1); -//! ``` -//! //! # Important Notes //! //! - All operations are zero-cost abstractions diff --git a/citadel_proto/src/inner_arg.rs b/citadel_proto/src/inner_arg.rs index c176199c5..521f875d8 100644 --- a/citadel_proto/src/inner_arg.rs +++ b/citadel_proto/src/inner_arg.rs @@ -12,26 +12,6 @@ //! - Zero-cost abstractions //! - Generic over target types //! -//! # Usage Example -//! -//! ```rust -//! use citadel_proto::inner_arg::{InnerParameter, InnerParameterMut}; -//! use std::ops::{Deref, DerefMut}; -//! -//! struct Wrapper(T); -//! impl Deref for Wrapper { -//! type Target = T; -//! fn deref(&self) -> &T { &self.0 } -//! } -//! impl DerefMut for Wrapper { -//! fn deref_mut(&mut self) -> &mut T { &mut self.0 } -//! } -//! -//! let mut value = Wrapper(42); -//! let param = InnerParameterMut::from(&mut value); -//! assert_eq!(*param, 42); -//! ``` -//! //! # Important Notes //! //! - Zero runtime overhead diff --git a/citadel_proto/src/kernel/mod.rs b/citadel_proto/src/kernel/mod.rs index bb8948db0..c2cd722f1 100644 --- a/citadel_proto/src/kernel/mod.rs +++ b/citadel_proto/src/kernel/mod.rs @@ -14,27 +14,6 @@ //! - Protocol type abstraction //! - Session security management //! -//! # Usage Example -//! -//! ```rust -//! use citadel_proto::kernel::{KernelExecutor, KernelExecutorSettings}; -//! use citadel_proto::kernel::kernel_trait::NetKernel; -//! use citadel_wire::hypernode_type::NodeType; -//! -//! // Configure kernel settings -//! let settings = KernelExecutorSettings::default() -//! .with_max_concurrency(Some(10)); -//! -//! // Create kernel executor with settings -//! let executor = KernelExecutor::new( -//! runtime.handle(), -//! NodeType::default(), -//! account_manager, -//! kernel, -//! settings -//! ); -//! ``` -//! //! # Important Notes //! //! - Single-threaded lower level, multi-threaded upper level diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index 94182a3b9..fbdeaba5e 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -56,10 +56,7 @@ //! //! ## Feature Flags //! -//! - `multi-threaded`: Enables multi-threaded support (default) -//! - `compression`: Enables packet compression -//! - `file-transfer`: Enables secure file transfer support -//! - `group-chat`: Enables group communication features +//! - `multi-threaded`: Enables multi-threaded support //! //! ## Version Compatibility //! diff --git a/citadel_proto/src/proto/codec.rs b/citadel_proto/src/proto/codec.rs index 500a61eee..dccefd4fe 100644 --- a/citadel_proto/src/proto/codec.rs +++ b/citadel_proto/src/proto/codec.rs @@ -9,26 +9,6 @@ //! - Automatic buffer resizing when capacity is insufficient //! - Implementation of tokio_util's Encoder and Decoder traits //! -//! # Usage -//! ```rust -//! use citadel_proto::proto::codec::BytesCodec; -//! use bytes::{Bytes, BytesMut}; -//! use citadel_io::tokio_util::codec::{Decoder, Encoder}; -//! -//! // Create a new codec with specified buffer capacity -//! let mut codec = BytesCodec::new(8192); -//! let mut buf = BytesMut::new(); -//! -//! // Encode some bytes -//! let data = Bytes::from("Hello"); -//! codec.encode(data, &mut buf).unwrap(); -//! -//! // Decode the bytes -//! if let Ok(Some(decoded)) = codec.decode(&mut buf) { -//! assert_eq!(decoded, "Hello"); -//! } -//! ``` -//! //! # Important Notes //! - The codec maintains a minimum buffer size defined by `CODEC_MIN_BUFFER` //! - Buffer capacity is automatically increased if it falls below the minimum diff --git a/citadel_proto/src/proto/misc/session_security_settings.rs b/citadel_proto/src/proto/misc/session_security_settings.rs index 81acf3de3..6b8cf6e09 100644 --- a/citadel_proto/src/proto/misc/session_security_settings.rs +++ b/citadel_proto/src/proto/misc/session_security_settings.rs @@ -39,8 +39,7 @@ pub struct SessionSecuritySettingsBuilder { impl SessionSecuritySettingsBuilder { /// Sets the maximum security level for the session, allowing the use of multi-layered encryption on a per-message basis as well as increased difficulty in breaking the recursive chain key (default: low) /// ``` - /// use citadel_proto::prelude::SessionSecuritySettingsBuilder; - /// use citadel_crypt::entropy_bank::SecurityLevel; + /// use citadel_proto::prelude::*; /// SessionSecuritySettingsBuilder::default() /// .with_security_level(SecurityLevel::Standard) /// .build(); @@ -66,10 +65,10 @@ impl SessionSecuritySettingsBuilder { /// Default: Firesaber + AES_GCM_256_SIV /// ``` - /// use citadel_proto::prelude::SessionSecuritySettingsBuilder; - /// use citadel_pqcrypto::algorithm_dictionary::{EncryptionAlgorithm, KemAlgorithm}; + /// use citadel_proto::prelude::*; + /// use citadel_pqcrypto::prelude::*; /// SessionSecuritySettingsBuilder::default() - /// .with_crypto_params(EncryptionAlgorithm::AES_GCM_256_SIV + KemAlgorithm::Kyber) + /// .with_crypto_params(EncryptionAlgorithm::AES_GCM_256 + KemAlgorithm::Kyber) /// .build(); /// ``` pub fn with_crypto_params(mut self, params: impl Into) -> Self { diff --git a/citadel_proto/src/proto/outbound_sender.rs b/citadel_proto/src/proto/outbound_sender.rs index 526e0d9dc..4ed524870 100644 --- a/citadel_proto/src/proto/outbound_sender.rs +++ b/citadel_proto/src/proto/outbound_sender.rs @@ -32,21 +32,6 @@ //! - **BoundedSender**: Rate-limited channel with backpressure //! - **OutboundPrimaryStreamSender**: TCP stream management //! - **OutboundUdpSender**: UDP datagram handling with keep-alive -//! -//! ## Example Usage -//! -//! ```no_run -//! use citadel_proto::outbound_sender::{UnboundedSender, BoundedSender}; -//! -//! // Create an unbounded channel -//! let (tx, rx) = UnboundedSender::unbounded(); -//! -//! // Create a bounded channel with rate limiting -//! let (tx, rx) = BoundedSender::new(100); -//! -//! // Send messages -//! tx.send(message)?; -//! ``` use crate::error::NetworkError; use crate::proto::packet::packet_flags; diff --git a/citadel_proto/src/proto/packet_crafter.rs b/citadel_proto/src/proto/packet_crafter.rs index dc0269dc6..adbaafbfd 100644 --- a/citadel_proto/src/proto/packet_crafter.rs +++ b/citadel_proto/src/proto/packet_crafter.rs @@ -42,20 +42,6 @@ //! 2. Payload serialization and encryption //! 3. Security level enforcement //! 4. Timestamp and sequence number management -//! -//! ## Example Usage -//! -//! ```no_run -//! use citadel_proto::packet_crafter::{SecureProtocolPacket, GroupTransmitter}; -//! -//! // Create a secure packet -//! let packet = SecureProtocolPacket::new(); -//! packet.write_payload(data)?; -//! -//! // Create a group transmitter for file transfer -//! let transmitter = GroupTransmitter::new(stream, config)?; -//! transmitter.transmit_group_header(target)?; -//! ``` use bytes::BytesMut; @@ -86,14 +72,9 @@ use citadel_types::prelude::ObjectId; /// /// # Example /// -/// ```no_run -/// use citadel_proto::packet_crafter::SecureProtocolPacket; -/// -/// let mut packet = SecureProtocolPacket::new(); -/// packet.write_payload(data_len, |buf| { -/// buf.copy_from_slice(data); -/// Ok(()) -/// })?; +/// ```rust +/// use citadel_proto::prelude::SecureProtocolPacket; +/// let mut packet = SecureProtocolPacket::from("Hello, world!"); /// ``` #[derive(Debug)] /// A thin wrapper used for convenient creation of zero-copy outgoing buffers @@ -153,23 +134,6 @@ impl From for SecureMessagePacket { /// - Automatic packet chunking /// - Progress tracking /// - Error handling -/// -/// # Example -/// -/// ```no_run -/// use citadel_proto::packet_crafter::GroupTransmitter; -/// -/// let transmitter = GroupTransmitter::new_message( -/// stream, -/// object_id, -/// ratchet, -/// packet, -/// security_level, -/// group_id, -/// ticket, -/// time_tracker, -/// )?; -/// ``` pub struct GroupTransmitter { pub ratchet_container: RatchetPacketCrafterContainer, to_primary_stream: OutboundPrimaryStreamSender, diff --git a/citadel_proto/src/proto/packet_processor/deregister_packet.rs b/citadel_proto/src/proto/packet_processor/deregister_packet.rs index 4598c737e..8ab24982f 100644 --- a/citadel_proto/src/proto/packet_processor/deregister_packet.rs +++ b/citadel_proto/src/proto/packet_processor/deregister_packet.rs @@ -26,26 +26,6 @@ //! - `AccountManager`: Handles account removal //! - `SessionManager`: Manages session cleanup //! - `KernelInterface`: Reports deregistration results -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::deregister_packet; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! async fn handle_deregister(session: &CitadelSession, packet: HdpPacket) { -//! let header_entropy_bank_vers = 1; -//! match deregister_packet::process_deregister(session, packet, header_entropy_bank_vers).await { -//! Ok(result) => { -//! // Handle successful deregistration -//! } -//! Err(err) => { -//! // Handle deregistration error -//! } -//! } -//! } -//! ``` use super::includes::*; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs index e5ed93457..64d1c7bea 100644 --- a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs @@ -26,26 +26,6 @@ //! - `KernelInterface`: Handles disconnect signals //! - `SessionManager`: Tracks session lifecycle //! - `PrimaryStream`: Handles packet transmission -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::disconnect_packet; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! async fn handle_disconnect(session: &CitadelSession, packet: HdpPacket) { -//! let header_entropy_bank_vers = 1; -//! match disconnect_packet::process_disconnect(session, packet, header_entropy_bank_vers).await { -//! Ok(result) => { -//! // Handle successful disconnection -//! } -//! Err(err) => { -//! // Handle disconnection error -//! } -//! } -//! } -//! ``` use super::includes::*; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/file_packet.rs b/citadel_proto/src/proto/packet_processor/file_packet.rs index 62b8883e9..1c45a42ba 100644 --- a/citadel_proto/src/proto/packet_processor/file_packet.rs +++ b/citadel_proto/src/proto/packet_processor/file_packet.rs @@ -28,26 +28,6 @@ //! - `VirtualFileSystem`: Handles file operations //! - `ObjectTransferHandle`: Tracks transfer progress //! - `SecurityLevel`: Manages encryption levels -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::file_packet; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! fn handle_file_packet(session: &CitadelSession, packet: HdpPacket) { -//! let proxy_info = None; -//! match file_packet::process_file_packet(session, packet, proxy_info) { -//! Ok(result) => { -//! // Handle successful file operation -//! } -//! Err(err) => { -//! // Handle file operation error -//! } -//! } -//! } -//! ``` use super::includes::*; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/hole_punch.rs b/citadel_proto/src/proto/packet_processor/hole_punch.rs index 717d951b4..83f153115 100644 --- a/citadel_proto/src/proto/packet_processor/hole_punch.rs +++ b/citadel_proto/src/proto/packet_processor/hole_punch.rs @@ -26,27 +26,6 @@ //! - `HolePuncherPipe`: Handles connection establishment //! - `StackedRatchet`: Provides packet security //! - `ProxyManager`: Handles proxied connections -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::hole_punch; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! fn handle_hole_punch(session: &CitadelSession, packet: HdpPacket) { -//! let hr_version = 1; -//! let proxy_info = None; -//! match hole_punch::process_hole_punch(session, packet, hr_version, proxy_info) { -//! Ok(result) => { -//! // Handle successful hole punch -//! } -//! Err(err) => { -//! // Handle hole punch error -//! } -//! } -//! } -//! ``` use super::includes::*; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/mod.rs b/citadel_proto/src/proto/packet_processor/mod.rs index 1cee95df1..0a6f95b39 100644 --- a/citadel_proto/src/proto/packet_processor/mod.rs +++ b/citadel_proto/src/proto/packet_processor/mod.rs @@ -80,19 +80,6 @@ //! - All packets are encrypted using post-quantum cryptography //! - Headers are protected against tampering //! - Replay attacks are prevented through sequence numbers -//! -//! ## Example -//! -//! ```no_run -//! use citadel_proto::packet_processor::{PrimaryProcessorResult, HdpPacket}; -//! -//! // Process an incoming packet -//! match process_packet(packet) { -//! PrimaryProcessorResult::Void => { /* No response needed */ } -//! PrimaryProcessorResult::ReplyToSender(response) => { /* Send response */ } -//! PrimaryProcessorResult::EndSession(reason) => { /* Handle session end */ } -//! } -//! ``` use crate::proto::packet::HdpHeader; use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; diff --git a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs index ca9e94f84..5ee489e7c 100644 --- a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs +++ b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs @@ -29,19 +29,6 @@ //! - `GroupBroadcastPayload`: Message handling //! - `MessageGroupOptions`: Group configuration //! - `MemberState`: Member status tracking -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::peer::group_broadcast::GroupBroadcast; -//! use citadel_types::proto::MessageGroupOptions; -//! -//! // Create a new group broadcast message -//! let broadcast = GroupBroadcast::Create { -//! initial_invitees: vec![1, 2, 3], // CIDs of initial members -//! options: MessageGroupOptions::default(), -//! }; -//! ``` use super::super::includes::*; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index 73973932a..0c9aff8f8 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -30,37 +30,6 @@ //! - `HyperNodePeerLayerInner`: Peer layer //! - `StackedRatchet`: Cryptographic operations //! - `StateContainer`: State management -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::peer::peer_cmd_packet; -//! use citadel_proto::proto::peer::peer_layer::PeerSignal; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! async fn handle_peer_cmd( -//! session: &CitadelSession, -//! packet: HdpPacket, -//! header_version: u32, -//! ) { -//! let aux_cmd = 0; -//! let endpoint_info = None; -//! match peer_cmd_packet::process_peer_cmd( -//! session, -//! aux_cmd, -//! packet, -//! header_version, -//! endpoint_info, -//! ).await { -//! Ok(result) => { -//! // Handle successful peer command -//! } -//! Err(err) => { -//! // Handle peer command error -//! } -//! } -//! } -//! ``` use std::sync::atomic::Ordering; diff --git a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs index c445b5ae3..3825256c2 100644 --- a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs @@ -26,26 +26,6 @@ //! - `StackedRatchet`: Provides cryptographic primitives for secure channels //! - `UdpHolePuncher`: Handles NAT traversal operations //! - `SessionManager`: Tracks active protocol sessions -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::preconnect_packet; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! async fn handle_preconnect(session: &CitadelSession, packet: HdpPacket) { -//! let header_entropy_bank_vers = 1; -//! match preconnect_packet::process_preconnect(session, packet, header_entropy_bank_vers).await { -//! Ok(result) => { -//! // Handle successful preconnect processing -//! } -//! Err(err) => { -//! // Handle preconnect error -//! } -//! } -//! } -//! ``` use citadel_crypt::endpoint_crypto_container::AssociatedSecurityLevel; use citadel_crypt::ratchets::Ratchet; diff --git a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs index 1200b5e9b..ab22d4450 100644 --- a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs +++ b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs @@ -28,27 +28,6 @@ //! - `StackedRatchet`: Provides cryptographic primitives //! - `PeerSessionCrypto`: Handles peer-to-peer encryption //! - `VirtualConnection`: Manages group connections -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::primary_group_packet; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! fn handle_group_packet(session: &CitadelSession, packet: HdpPacket) { -//! let cmd_aux = 0; // Command auxiliary value -//! let proxy_info = None; // No proxy information -//! match primary_group_packet::process_primary_packet(session, cmd_aux, packet, proxy_info) { -//! Ok(result) => { -//! // Handle successful group packet processing -//! } -//! Err(err) => { -//! // Handle group packet error -//! } -//! } -//! } -//! ``` use super::includes::*; use crate::constants::GROUP_EXPIRE_TIME_MS; @@ -642,7 +621,7 @@ impl ToolsetUpdate<'_, R> { /// Unlocks the internal state, allowing future upgrades to the system. Returns the latest hyper ratchet pub(crate) fn unlock(&mut self, _requires_locked_by_alice: bool) -> Option { - self.crypt.maybe_unlock().map(|r| r.clone()) + self.crypt.maybe_unlock().cloned() } pub(crate) fn get_local_cid(&self) -> u64 { diff --git a/citadel_proto/src/proto/packet_processor/register_packet.rs b/citadel_proto/src/proto/packet_processor/register_packet.rs index e1d2a031b..a886ffe13 100644 --- a/citadel_proto/src/proto/packet_processor/register_packet.rs +++ b/citadel_proto/src/proto/packet_processor/register_packet.rs @@ -27,30 +27,6 @@ //! - `AccountManager`: Handles account creation //! - `StackedRatchet`: Provides cryptographic primitives //! - `SessionManager`: Tracks registration process -//! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::register_packet; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! use std::net::SocketAddr; -//! -//! async fn handle_register( -//! session: &CitadelSession, -//! packet: HdpPacket, -//! remote_addr: SocketAddr -//! ) { -//! match register_packet::process_register(session, packet, remote_addr).await { -//! Ok(result) => { -//! // Handle successful registration -//! } -//! Err(err) => { -//! // Handle registration error -//! } -//! } -//! } -//! ``` use super::includes::*; use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/packet_processor/rekey_packet.rs b/citadel_proto/src/proto/packet_processor/rekey_packet.rs index c10e07069..f96f2f6e2 100644 --- a/citadel_proto/src/proto/packet_processor/rekey_packet.rs +++ b/citadel_proto/src/proto/packet_processor/rekey_packet.rs @@ -28,25 +28,6 @@ //! - `RatchetUpdateState`: Tracks key updates //! - `SecurityLevel`: Manages encryption levels //! -//! # Example Usage -//! -//! ```no_run -//! use citadel_proto::proto::packet_processor::rekey_packet; -//! use citadel_proto::proto::CitadelSession; -//! use citadel_proto::proto::packet::HdpPacket; -//! -//! fn handle_rekey(session: &CitadelSession, packet: HdpPacket) { -//! let header_entropy_bank_vers = 1; -//! let proxy_info = None; -//! match rekey_packet::process_rekey(session, packet, header_entropy_bank_vers, proxy_info) { -//! Ok(result) => { -//! // Handle successful rekey -//! } -//! Err(err) => { -//! // Handle rekey error -//! } -//! } -//! } //! ``` use super::includes::*; diff --git a/citadel_proto/src/proto/peer/channel.rs b/citadel_proto/src/proto/peer/channel.rs index ff0db9752..25a9b2697 100644 --- a/citadel_proto/src/proto/peer/channel.rs +++ b/citadel_proto/src/proto/peer/channel.rs @@ -17,31 +17,24 @@ //! ## Example Usage //! //! ```no_run -//! use citadel_proto::peer::channel::PeerChannel; +//! use citadel_proto::prelude::*; +//! use futures::StreamExt; //! use citadel_types::crypto::SecurityLevel; +//! # async fn run() -> Result<(), Box> { //! -//! // Create a new peer channel -//! let channel = PeerChannel::new( -//! server_remote, -//! target_cid, -//! vconn_type, -//! channel_id, -//! SecurityLevel::Standard, -//! is_alive, -//! receiver, -//! outbound_stream -//! ); -//! +//! # let channel: PeerChannel = todo!(); //! // Split into send and receive halves //! let (send_half, recv_half) = channel.split(); //! //! // Send a message -//! send_half.send_message(secure_packet)?; +//! send_half.send_message("Hello, world!").await?; //! //! // Receive messages asynchronously //! while let Some(message) = recv_half.next().await { //! // Process received message //! } +//! # Ok(()) +//! # } //! ``` //! //! ## Important Notes @@ -173,8 +166,11 @@ impl PeerChannelSendHalf { } /// Sends a message through the channel - pub async fn send_message(&self, message: SecureProtocolPacket) -> Result<(), NetworkError> { - let (ticket, packet, target, security_level) = self.get_args(message); + pub async fn send_message>( + &self, + message: T, + ) -> Result<(), NetworkError> { + let (ticket, packet, target, security_level) = self.get_args(message.into()); let request = SessionRequest::SendMessage { ticket, packet, diff --git a/citadel_proto/src/proto/peer/group_channel.rs b/citadel_proto/src/proto/peer/group_channel.rs index 14f1e93f6..70a39464c 100644 --- a/citadel_proto/src/proto/peer/group_channel.rs +++ b/citadel_proto/src/proto/peer/group_channel.rs @@ -18,24 +18,19 @@ This module implements group communication channels in the Citadel Protocol, pro ## Example Usage ```rust -// Create a new group channel -let group_channel = GroupChannel::new( - node_remote, - tx, - key, - ticket, - session_cid, - recv -); - +use citadel_proto::prelude::*; +# async fn run() -> Result<(), Box> { +# let group_channel: GroupChannel = todo!(); // Send a message to the group -group_channel.send_message(message)?; +group_channel.send_message(SecBuffer::from(b"Hello, world!" as &[u8])).await?; +let peer_cid = 1234; // Invite new members -group_channel.invite(peer_cid)?; +group_channel.invite(peer_cid).await?; // Split the channel for separate send/receive handling let (send_half, recv_half) = group_channel.split(); +# } ``` ## Important Notes diff --git a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs index efadfb5e5..2455ff68a 100644 --- a/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs +++ b/citadel_proto/src/proto/peer/hole_punch_compat_sink_stream.rs @@ -15,24 +15,6 @@ This module implements a compatibility layer for UDP hole punching in the Citade - `ConnAddr`: Address management for connection endpoints - `StackedRatchet`: Encryption layer for secure communication -## Example Usage -```rust -// Create a new compatibility stream -let compat_stream = ReliableOrderedCompatStream::new( - primary_stream, - state_container, - target_cid, - stacked_ratchet, - security_level -); - -// Send data through the stream -compat_stream.send_to_peer(&data)?; - -// Receive data from the stream -let received = compat_stream.recv()?; -``` - ## Important Notes 1. Supports both client-server and peer-to-peer modes 2. Requires pre-loaded ratchets for P2P communication diff --git a/citadel_proto/src/proto/peer/message_group.rs b/citadel_proto/src/proto/peer/message_group.rs index 532375261..d15927ae2 100644 --- a/citadel_proto/src/proto/peer/message_group.rs +++ b/citadel_proto/src/proto/peer/message_group.rs @@ -15,25 +15,6 @@ This module implements the group messaging framework for the Citadel Protocol, p - `MessageGroupPeer`: Represents individual peer state and metadata - `MessageGroupOptions`: Configures group behavior and permissions -## Example Usage -```rust -// Create a new message group with options -let group = MessageGroup { - concurrent_peers: HashMap::new(), - pending_peers: HashMap::new(), - options: message_group_options, -}; - -// Add a peer to the pending list -let peer = MessageGroupPeer { peer_cid: peer_id }; -group.pending_peers.insert(peer_id, peer); - -// Upgrade a peer from pending to concurrent -if let Some(peer) = group.pending_peers.remove(&peer_id) { - group.concurrent_peers.insert(peer_id, peer); -} -``` - ## Important Notes 1. Groups are centered around an "axis of consent" (the initiator) 2. Consent is not transitive - all members must connect directly to the initiator diff --git a/citadel_proto/src/proto/peer/mod.rs b/citadel_proto/src/proto/peer/mod.rs index 3134a9638..55dcccbf2 100644 --- a/citadel_proto/src/proto/peer/mod.rs +++ b/citadel_proto/src/proto/peer/mod.rs @@ -19,20 +19,6 @@ This module is the root of the peer-to-peer networking implementation in the Cit - `p2p_conn_handler`: Direct P2P connection management - `hole_punch_compat_sink_stream`: NAT traversal compatibility layer -## Example Usage -```rust -use citadel_proto::proto::peer::{ - channel::PeerChannel, - group_channel::GroupChannel, - peer_layer::HyperNodePeerLayer, -}; - -// Create peer components as needed -let peer_layer = HyperNodePeerLayer::new(persistence_handler); -let channel = PeerChannel::new(/* ... */); -let group = GroupChannel::new(/* ... */); -``` - ## Important Notes 1. All peer communication is encrypted by default 2. NAT traversal is handled automatically diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index c31f24326..fdba6ebc2 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -14,36 +14,7 @@ This module implements direct peer-to-peer connection handling and NAT traversal - `DirectP2PRemote`: Manages direct P2P connection state and lifecycle - `P2PInboundHandle`: Handles incoming P2P connections and related state - `attempt_simultaneous_hole_punch`: Implements NAT traversal via UDP hole punching - -## Example Usage -```rust -// Setup a non-initiator listener -setup_listener_non_initiator( - local_bind_addr, - remote_addr, - session, - v_conn, - hole_punched_addr, - ticket, - udp_mode -)?; - -// Attempt NAT traversal -attempt_simultaneous_hole_punch( - peer_connection_type, - ticket, - session, - peer_nat_info, - session_cid, - kernel_tx, - channel_signal, - sync_time, - app, - encrypted_config_container, - client_config, - udp_mode -)?; -``` +- `PeerNatInfo`: Handles NAT detection and compatibility checking ## Important Notes 1. Supports both TCP and UDP connections with automatic fallback diff --git a/citadel_proto/src/proto/peer/peer_crypt.rs b/citadel_proto/src/proto/peer/peer_crypt.rs index a4fbe893c..6194c3b4e 100644 --- a/citadel_proto/src/proto/peer/peer_crypt.rs +++ b/citadel_proto/src/proto/peer/peer_crypt.rs @@ -15,28 +15,6 @@ This module implements the cryptographic key exchange and NAT traversal function - `PeerNatInfo`: Handles NAT-related information and compatibility - `TlsDomain`: Configures TLS settings for secure connections -## Example Usage -```rust -// Stage 0: Alice initiates key exchange -let stage0 = KeyExchangeProcess::Stage0( - public_key, - security_settings, - udp_mode -); - -// Stage 1: Bob responds with encrypted data -let stage1 = KeyExchangeProcess::Stage1( - ciphertext, - Some(peer_nat_info), - file_transfer_compatible -); - -// Check NAT compatibility -let (needs_turn, addr) = peer_nat_info.generate_proper_listener_connect_addr( - &local_nat_type -); -``` - ## Important Notes 1. Key exchange follows a three-stage protocol for security 2. NAT compatibility is checked before direct connections diff --git a/citadel_proto/src/proto/peer/peer_layer.rs b/citadel_proto/src/proto/peer/peer_layer.rs index 6d863b227..ea6f64e2c 100644 --- a/citadel_proto/src/proto/peer/peer_layer.rs +++ b/citadel_proto/src/proto/peer/peer_layer.rs @@ -16,24 +16,6 @@ This module implements the core peer-to-peer networking layer for the Citadel Pr - `MessageGroup`: Handles group messaging with support for concurrent and pending peers - `TrackedPosting`: Tracks peer signal states with timeout support -## Example Usage -```rust -let peer_layer = HyperNodePeerLayer::new(persistence_handler); - -// Create a new message group -let group_key = peer_layer.create_new_message_group( - session_cid, - &initial_peers, - message_group_options -)?; - -// Add peers to the group -peer_layer.add_pending_peers_to_group(group_key, new_peers); - -// Upgrade a peer from pending to concurrent -peer_layer.upgrade_peer_in_group(group_key, peer_cid); -``` - ## Important Notes 1. Peer signals are tracked using unique tickets per session to prevent unintended expiration 2. Message groups support both concurrent (active) and pending (invited) peers diff --git a/citadel_proto/src/proto/remote.rs b/citadel_proto/src/proto/remote.rs index 87ea22664..88872fcd8 100644 --- a/citadel_proto/src/proto/remote.rs +++ b/citadel_proto/src/proto/remote.rs @@ -28,17 +28,23 @@ //! ## Usage Example //! //! ```no_run -//! use citadel_proto::remote::NodeRemote; -//! use citadel_proto::prelude::NodeRequest; +//! use citadel_proto::prelude::*; +//! # async fn test() -> Result<(), NetworkError> { +//! # fn get_request() -> NodeRequest { todo!() } +//! # let remote: NodeRemote = todo!(); //! //! // Create a request -//! let request = NodeRequest::new(); +//! let request = get_request(); //! //! // Send request and get ticket -//! let ticket = remote.send(request)?; +//! let ticket = remote.send(request).await?; +//! +//! let request_expects_multiple_responses = get_request(); //! //! // Or use callback subscription -//! let subscription = remote.send_callback_subscription(request)?; +//! let subscription = remote.send_callback_subscription(request_expects_multiple_responses).await?; +//! # Ok(()) +//! # } //! ``` use crate::error::NetworkError; diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index 34a493bd8..dd2bcd84a 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -23,23 +23,6 @@ //! - **Session Key Rotation**: Automatic rotation based on configurable parameters //! - **Memory Security**: Sensitive data is securely zeroed when dropped //! -//! ## Example Usage -//! -//! ```no_run -//! use citadel_proto::session::{CitadelSession, SessionInitParams}; -//! -//! // Create session parameters -//! let params = SessionInitParams::new() -//! .with_node_type(NodeType::Client) -//! .with_protocol(ConnectProtocol::Tcp); -//! -//! // Initialize the session -//! let (shutdown_tx, session) = CitadelSession::new(params)?; -//! -//! // Execute the session with a network stream -//! session.execute(stream, peer_addr)?; -//! ``` -//! //! ## Security Considerations //! //! - All session data is encrypted using post-quantum cryptographic primitives @@ -70,9 +53,9 @@ use netbeam::time_tracker::TimeTracker; //use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender, channel, TrySendError}; use crate::auth::AuthenticationRequest; use crate::constants::{ - DRILL_UPDATE_FREQUENCY_LOW_BASE, FIREWALL_KEEP_ALIVE_UDP, GROUP_EXPIRE_TIME_MS, - HDP_HEADER_BYTE_LEN, INITIAL_RECONNECT_LOCKOUT_TIME_NS, KEEP_ALIVE_INTERVAL_MS, - KEEP_ALIVE_TIMEOUT_NS, LOGIN_EXPIRATION_TIME, + FIREWALL_KEEP_ALIVE_UDP, GROUP_EXPIRE_TIME_MS, HDP_HEADER_BYTE_LEN, + INITIAL_RECONNECT_LOCKOUT_TIME_NS, KEEP_ALIVE_INTERVAL_MS, KEEP_ALIVE_TIMEOUT_NS, + LOGIN_EXPIRATION_TIME, REKEY_UPDATE_FREQUENCY_STANDARD, }; use crate::error::NetworkError; use crate::kernel::RuntimeFuture; @@ -1167,7 +1150,7 @@ impl CitadelSession { ); if !is_server { - queue_worker.insert_reserved_fn(Some(QueueWorkerTicket::Periodic(DRILL_REKEY_WORKER, 0)), Duration::from_nanos(DRILL_UPDATE_FREQUENCY_LOW_BASE), move |state_container| { + queue_worker.insert_reserved_fn(Some(QueueWorkerTicket::Periodic(DRILL_REKEY_WORKER, 0)), Duration::from_nanos(REKEY_UPDATE_FREQUENCY_STANDARD), move |state_container| { let ticket = kernel_ticket; if state_container.state.is_connected() { diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index e23b30387..e0c23104e 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -36,38 +36,6 @@ //! - Connection states are protected against replay attacks //! - Group keys are securely managed //! - File transfers are encrypted end-to-end -//! -//! ## Example Usage -//! -//! ```no_run -//! use citadel_proto::state_container::{StateContainer, VirtualConnectionType}; -//! -//! // Create a new state container -//! let container = StateContainer::create( -//! kernel_tx, -//! remote, -//! timeout, -//! state, -//! account, -//! tracker, -//! settings, -//! is_server, -//! stats, -//! udp_mode, -//! ); -//! -//! // Handle a virtual connection -//! container.insert_new_peer_virtual_connection_as_endpoint( -//! peer_addr, -//! settings, -//! ticket, -//! target_cid, -//! conn_type, -//! crypto, -//! session, -//! true, -//! )?; -//! ``` use std::collections::{HashMap, VecDeque}; use std::fmt::{Debug, Display, Formatter}; diff --git a/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs index 66b04c9db..cccfddf6d 100644 --- a/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/connect_state_container.rs @@ -10,22 +10,6 @@ //! - Support for different connection modes //! - State recovery mechanisms for connection resilience //! -//! ## Example Usage -//! ```rust -//! use citadel_proto::proto::state_subcontainers::ConnectState; -//! -//! // Create new connection state -//! let mut state = ConnectState::default(); -//! -//! // Handle successful connection -//! state.on_success(); -//! state.on_connect_packet_received(); -//! -//! // Handle connection failure -//! state.on_fail(); -//! state.on_connect_packet_received(); -//! ``` -//! //! ## Important Notes //! - State transitions must update both local and global session state //! - Packet timing is tracked for timeout management diff --git a/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs b/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs index 062095b5c..0115884ce 100644 --- a/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/deregister_state_container.rs @@ -7,19 +7,6 @@ //! - Manages deregistration tickets for process identification //! - Tracks timing information for deregistration operations //! - Provides atomic state transitions for thread safety -//! -//! ## Example Usage -//! ```rust -//! use citadel_proto::proto::state_subcontainers::DeRegisterState; -//! use citadel_proto::proto::remote::Ticket; -//! -//! let mut state = DeRegisterState::default(); -//! let ticket = Ticket::default(); -//! -//! // Initialize deregistration process -//! state.on_init(timestamp, ticket); -//! ``` -//! //! ## Important Notes //! - State must be properly initialized before use //! - Timing information is critical for process tracking diff --git a/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs b/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs index dd843fe0f..0b9c601ad 100644 --- a/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/meta_expiry_container.rs @@ -10,21 +10,6 @@ //! - Handles file transfer expiry tracking //! - Provides adaptive expiry timing based on workload //! -//! ## Example Usage -//! ```rust -//! use citadel_proto::proto::state_subcontainers::MetaExpiryState; -//! -//! let mut state = MetaExpiryState::default(); -//! -//! // Update state on packet confirmation -//! state.on_event_confirmation(); -//! -//! // Check if expired -//! if state.expired() { -//! // Handle expiration -//! } -//! ``` -//! //! ## Important Notes //! - Critical for high-traffic workload scenarios //! - Prevents false expiration during async processing delays diff --git a/citadel_proto/src/proto/state_subcontainers/mod.rs b/citadel_proto/src/proto/state_subcontainers/mod.rs index 697e28b84..851a97f66 100644 --- a/citadel_proto/src/proto/state_subcontainers/mod.rs +++ b/citadel_proto/src/proto/state_subcontainers/mod.rs @@ -19,26 +19,6 @@ This module implements specialized state containers for managing different aspec - `rekey_container`: Handles key rotation states - `meta_expiry_container`: Manages state expiration -## Example Usage -```rust -use citadel_proto::proto::state_subcontainers::{ - ConnectState, - RegisterState, - PreConnectState, -}; - -// Initialize connection state -let mut connect_state = ConnectState::default(); -connect_state.on_connect_packet_received(); - -// Handle registration -let mut register_state = RegisterState::default(); -register_state.on_register_packet_received(); - -// Manage pre-connection -let pre_connect = PreConnectState::default(); -``` - ## Important Notes 1. Each state container manages a specific phase of connection 2. State transitions should be handled atomically diff --git a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs index 374748494..1491f45b0 100644 --- a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs @@ -16,26 +16,6 @@ This module manages the state of peer-to-peer key exchange processes in the Cita - `SessionSecuritySettings`: Configures security parameters - `UdpChannelSender`: Manages UDP channel communication -## Example Usage -```rust -use citadel_proto::proto::state_subcontainers::PeerKemStateContainer; - -// Create new key exchange state -let state = PeerKemStateContainer::new( - security_settings, - udp_enabled, - session_password -); - -// Access security settings -let settings = state.session_security_settings; - -// Check if local peer is initiator -if state.local_is_initiator { - // Handle initiator-specific logic -} -``` - ## Important Notes 1. Key exchange state is critical for secure communication 2. UDP channels are optional based on configuration diff --git a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs index 6f2a2c8c6..ba38650af 100644 --- a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs @@ -16,27 +16,6 @@ This module manages the state of pre-connection setup in the Citadel Protocol, h - `StackedRatchetConstructor`: Handles cryptographic setup - `NodeType`: Configures node behavior -## Example Usage -```rust -use citadel_proto::proto::state_subcontainers::PreConnectState; - -// Create new pre-connection state -let mut state = PreConnectState::default(); - -// Handle packet reception -state.on_packet_received(); - -// Check connection success -if state.success { - // Handle successful pre-connection -} - -// Access generated ratchet -if let Some(ratchet) = state.generated_ratchet { - // Use the ratchet for encryption -} -``` - ## Important Notes 1. Pre-connection state is critical for secure setup 2. UDP channels are managed through oneshot channels diff --git a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs index dbeb6e911..a20597563 100644 --- a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs @@ -16,23 +16,6 @@ This module manages the state of user registration processes in the Citadel Prot - `StackedRatchet`: Provides secure communication - `packet_flags`: Defines registration stages -## Example Usage -```rust -use citadel_proto::proto::state_subcontainers::RegisterState; - -// Create new registration state -let mut state = RegisterState::default(); - -// Handle registration packet -state.on_register_packet_received(); - -// Handle registration failure -state.on_fail(); - -// Create state from specific stage -let stage_state = RegisterState::from(stage_number); -``` - ## Important Notes 1. Registration stages must follow proper sequence 2. Cryptographic setup is essential for security diff --git a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs index 3f8f30f40..2be879daf 100644 --- a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs @@ -10,21 +10,8 @@ //! - Provides configurable security levels //! - Implements adaptive update frequency based on security needs //! -//! ## Example Usage -//! ```rust -//! use citadel_proto::proto::state_subcontainers::RatchetUpdateState; -//! use citadel_proto::proto::transfer_stats::TransferStats; -//! -//! // Create new ratchet update state -//! let mut state = RatchetUpdateState::default(); -//! -//! // Calculate update frequency based on security level -//! let stats = TransferStats::default(); -//! let frequency = calculate_update_frequency(2, &stats); -//! ``` -//! //! ## Important Notes -//! - Security levels range from 0 (low) to 4 (divine) +//! - Security levels range from 0 (low) to 4 (extreme) //! - Update frequencies are in nanoseconds //! - P2P updates are tracked per connection //! - Manual mode requires kernel notification @@ -39,9 +26,8 @@ use citadel_io::tokio::time::Duration; use crate::constants::{ - DRILL_UPDATE_FREQUENCY_DIVINE_BASE, DRILL_UPDATE_FREQUENCY_HIGH_BASE, - DRILL_UPDATE_FREQUENCY_LOW_BASE, DRILL_UPDATE_FREQUENCY_MEDIUM_BASE, - DRILL_UPDATE_FREQUENCY_ULTRA_BASE, + REKEY_UPDATE_FREQUENCY_EXTREME, REKEY_UPDATE_FREQUENCY_HIGH, REKEY_UPDATE_FREQUENCY_REINFORCED, + REKEY_UPDATE_FREQUENCY_STANDARD, REKEY_UPDATE_FREQUENCY_ULTRA, }; use crate::error::NetworkError; use crate::prelude::{NodeResult, ReKeyResult, ReKeyReturnType, Ticket, VirtualTargetType}; @@ -92,14 +78,10 @@ impl RatchetUpdateState { /// Calculates the frequency, in nanoseconds per update pub fn calculate_update_frequency(security_level: u8, _transfer_stats: &TransferStats) -> Duration { match security_level { - 0 => Duration::from_nanos(DRILL_UPDATE_FREQUENCY_LOW_BASE), - - 1 => Duration::from_nanos(DRILL_UPDATE_FREQUENCY_MEDIUM_BASE), - - 2 => Duration::from_nanos(DRILL_UPDATE_FREQUENCY_HIGH_BASE), - - 3 => Duration::from_nanos(DRILL_UPDATE_FREQUENCY_ULTRA_BASE), - - _ => Duration::from_nanos(DRILL_UPDATE_FREQUENCY_DIVINE_BASE), + 0 => Duration::from_nanos(REKEY_UPDATE_FREQUENCY_STANDARD), + 1 => Duration::from_nanos(REKEY_UPDATE_FREQUENCY_REINFORCED), + 2 => Duration::from_nanos(REKEY_UPDATE_FREQUENCY_HIGH), + 3 => Duration::from_nanos(REKEY_UPDATE_FREQUENCY_ULTRA), + _ => Duration::from_nanos(REKEY_UPDATE_FREQUENCY_EXTREME), } } diff --git a/citadel_proto/src/proto/transfer_stats.rs b/citadel_proto/src/proto/transfer_stats.rs index 3a9ce280d..95ea54fdc 100644 --- a/citadel_proto/src/proto/transfer_stats.rs +++ b/citadel_proto/src/proto/transfer_stats.rs @@ -9,22 +9,6 @@ //! - Maintains running totals of plaintext bytes sent //! - Thread-safe statistics accumulation //! -//! # Usage -//! ```rust -//! use citadel_proto::proto::transfer_stats::TransferStats; -//! -//! // Create initial stats -//! let mut stats = TransferStats::new(current_timestamp_ns, bytes_sent); -//! -//! // Update stats with new measurement -//! let new_stats = TransferStats::new(new_timestamp_ns, new_bytes_sent); -//! stats += new_stats; -//! -//! // Access calculated statistics -//! println!("Transfer rate: {} bytes/sec", stats.transfer_rate); -//! println!("Total bytes sent: {}", stats.total_plaintext_bytes_sent); -//! ``` -//! //! # Important Notes //! - Timestamps are in nanoseconds for high precision rate calculations //! - Transfer rates are calculated in bytes per second diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index 8d0cc6417..5f2b36d68 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -46,8 +46,8 @@ use crate::prelude::{ObjectSource, ProtocolRemoteTargetExt, TargetLockedRemote}; -use citadel_crypt::ratchets::Ratchet; use citadel_proto::prelude::NetworkError; +use citadel_proto::prelude::*; use citadel_types::crypto::SecurityLevel; use std::path::PathBuf; diff --git a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs index 2cd51df83..0970afbe2 100644 --- a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs +++ b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs @@ -17,7 +17,7 @@ //! use citadel_sdk::prefabs::server::accept_file_transfer_kernel::AcceptFileTransferKernel; //! //! # fn main() -> Result<(), NetworkError> { -//! let kernel = Box::new(AcceptFileTransferKernel::default()); +//! let kernel = Box::new(AcceptFileTransferKernel::::default()); //! # Ok(()) //! # } //! ``` diff --git a/citadel_sdk/src/prefabs/server/client_connect_listener.rs b/citadel_sdk/src/prefabs/server/client_connect_listener.rs index 834cf1993..5a7dc8475 100644 --- a/citadel_sdk/src/prefabs/server/client_connect_listener.rs +++ b/citadel_sdk/src/prefabs/server/client_connect_listener.rs @@ -18,7 +18,7 @@ //! use citadel_sdk::prefabs::server::client_connect_listener::ClientConnectListenerKernel; //! //! # fn main() -> Result<(), NetworkError> { -//! let kernel = Box::new(ClientConnectListenerKernel::new(|conn, remote| async move { +//! let kernel = Box::new(ClientConnectListenerKernel::<_, _, StackedRatchet>::new(|conn, remote| async move { //! println!("Client connected!"); //! Ok(()) //! })); diff --git a/citadel_sdk/src/prefabs/server/empty.rs b/citadel_sdk/src/prefabs/server/empty.rs index 460a7437a..78b9ac723 100644 --- a/citadel_sdk/src/prefabs/server/empty.rs +++ b/citadel_sdk/src/prefabs/server/empty.rs @@ -17,7 +17,7 @@ //! use citadel_sdk::prefabs::server::empty::EmptyKernel; //! //! # fn main() -> Result<(), NetworkError> { -//! let kernel = Box::new(EmptyKernel::default()); +//! let kernel = Box::new(EmptyKernel::::default()); //! # Ok(()) //! # } //! ``` diff --git a/citadel_sdk/src/prefabs/server/internal_service.rs b/citadel_sdk/src/prefabs/server/internal_service.rs index bbf28f4b1..78feeeda1 100644 --- a/citadel_sdk/src/prefabs/server/internal_service.rs +++ b/citadel_sdk/src/prefabs/server/internal_service.rs @@ -24,7 +24,7 @@ //! use citadel_sdk::prefabs::server::internal_service::InternalServiceKernel; //! //! // Create a kernel with an HTTP server -//! let kernel = InternalServiceKernel::new(|comm| async move { +//! let kernel = InternalServiceKernel::<_, _, StackedRatchet>::new(|comm| async move { //! //! let make_svc = make_service_fn(|socket: &AddrStream| { //! let remote_addr = socket.remote_addr(); diff --git a/citadel_sdk/src/prefabs/server/mod.rs b/citadel_sdk/src/prefabs/server/mod.rs index 9145b1984..6b3c60658 100644 --- a/citadel_sdk/src/prefabs/server/mod.rs +++ b/citadel_sdk/src/prefabs/server/mod.rs @@ -27,19 +27,19 @@ //! //! # fn main() -> Result<(), NetworkError> { //! // Create a basic server with file transfer support -//! let kernel = Box::new(AcceptFileTransferKernel::default()); +//! let kernel = Box::new(AcceptFileTransferKernel::::default()); //! //! // Create a server that listens for client connections -//! let kernel = Box::new(ClientConnectListenerKernel::new(|conn, remote| async move { +//! let kernel = Box::new(ClientConnectListenerKernel::<_, _, StackedRatchet>::new(|conn, remote| async move { //! println!("Client connected!"); //! Ok(()) //! })); //! //! // Create a minimal server with no additional processing -//! let kernel = Box::new(EmptyKernel::default()); +//! let kernel = Box::new(EmptyKernel::::default()); //! //! // Create a server with internal service support (e.g., HTTP server) -//! let kernel = Box::new(InternalServiceKernel::new(|_comm| async move { +//! let kernel = Box::new(InternalServiceKernel::<_, _, StackedRatchet>::new(|_comm| async move { //! let service = service_fn(|_req: Request| async move { //! Ok::<_, Infallible>(Response::new(Body::empty())) //! }); diff --git a/citadel_sdk/src/prefabs/shared/internal_service.rs b/citadel_sdk/src/prefabs/shared/internal_service.rs index c393f901c..9409f9d5d 100644 --- a/citadel_sdk/src/prefabs/shared/internal_service.rs +++ b/citadel_sdk/src/prefabs/shared/internal_service.rs @@ -40,9 +40,9 @@ use crate::prelude::{ConnectionSuccess, TargetLockedRemote}; use bytes::Bytes; -use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use citadel_proto::prelude::NetworkError; +use citadel_proto::prelude::*; use citadel_proto::re_imports::{StreamReader, UnboundedReceiverStream}; use citadel_types::crypto::SecBuffer; use futures::StreamExt; @@ -86,7 +86,7 @@ where // from_webserver forwards packets from the internal server to the proto let from_webserver = async move { while let Some(packet) = rx_from_service.recv().await { - sink.send_message(packet.into()).await?; + sink.send_message(packet).await?; } Ok(()) diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index 3308f39c3..e0f22549f 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -1211,9 +1211,8 @@ pub mod results { use crate::prefabs::client::peer_connection::FileTransferHandleRx; use crate::prelude::{PeerChannel, UdpChannel}; use crate::remote_ext::remote_specialization::PeerRemote; - use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::oneshot::Receiver; - use citadel_proto::prelude::NetworkError; + use citadel_proto::prelude::*; use std::fmt::Debug; pub struct PeerConnectSuccess { diff --git a/citadel_sdk/src/test_common.rs b/citadel_sdk/src/test_common.rs index 844f38e2a..5778d30ec 100644 --- a/citadel_sdk/src/test_common.rs +++ b/citadel_sdk/src/test_common.rs @@ -18,7 +18,7 @@ //! //! async fn test_server() { //! // Create a test server with default settings -//! let (server_future, addr) = server_info(); +//! let (server_future, addr) = server_info::(); //! //! // Run server and handle connections //! server_future.await.expect("Server failed to start"); diff --git a/citadel_types/src/lib.rs b/citadel_types/src/lib.rs index b7f20550d..f82d7b8f4 100644 --- a/citadel_types/src/lib.rs +++ b/citadel_types/src/lib.rs @@ -40,14 +40,8 @@ //! use citadel_types::prelude::*; //! //! // Use crypto types -//! let secure_buffer = SecBuffer::new(); +//! let secure_buffer = SecBuffer::empty(); //! let params = CryptoParameters::default(); -//! -//! // Use protocol types -//! let message = Message::new(); -//! -//! // Use user types -//! let user_id = UserId::new(); //! ``` //! //! ## Features diff --git a/example-library/examples/c2s/client_basic_transient_connection.rs b/example-library/examples/c2s/client_basic_transient_connection.rs index 8a2bfb8c7..ca565d940 100644 --- a/example-library/examples/c2s/client_basic_transient_connection.rs +++ b/example-library/examples/c2s/client_basic_transient_connection.rs @@ -51,11 +51,10 @@ async fn main() -> Result<(), Box> { .build()?; // Create server connection settings. If a custom transient ID is required, use `transient_with_id` over `transient`. - let server_connection_settings = - DefaultServerConnectionSettingsBuilder::transient(server_addr)? - .with_session_security_settings(session_security) - .disable_udp() - .build()?; + let server_connection_settings = DefaultServerConnectionSettingsBuilder::transient(server_addr) + .with_session_security_settings(session_security) + .disable_udp() + .build()?; // Create client kernel let kernel = SingleClientServerConnectionKernel::new( diff --git a/example-library/examples/c2s/client_basic_with_server_password.rs b/example-library/examples/c2s/client_basic_with_server_password.rs index 5d796aeaa..14138fa70 100644 --- a/example-library/examples/c2s/client_basic_with_server_password.rs +++ b/example-library/examples/c2s/client_basic_with_server_password.rs @@ -61,7 +61,7 @@ async fn main() -> Result<(), Box> { "my_username", "My Name", "notsecurepassword", - )? + ) .with_session_security_settings(session_security) .with_session_password(connect_password) .disable_udp() diff --git a/example-library/examples/c2s/client_echo.rs b/example-library/examples/c2s/client_echo.rs index 496c784bf..6cf8716cd 100644 --- a/example-library/examples/c2s/client_echo.rs +++ b/example-library/examples/c2s/client_echo.rs @@ -52,7 +52,7 @@ async fn main() -> Result<(), Box> { "my_username", "My Name", "notsecurepassword", - )? + ) .with_session_security_settings(session_security) .disable_udp() .build()?; @@ -67,7 +67,7 @@ async fn main() -> Result<(), Box> { let message = "Hello from client!"; // Send initial message let msg = SecBuffer::from(message); - if let Err(e) = tx.send_message(msg.into()).await { + if let Err(e) = tx.send_message(msg).await { println!("Error sending message: {}", e); return Err(e); } diff --git a/example-library/examples/c2s/server_basic.rs b/example-library/examples/c2s/server_basic.rs index 734500aa7..bd4379b84 100644 --- a/example-library/examples/c2s/server_basic.rs +++ b/example-library/examples/c2s/server_basic.rs @@ -35,7 +35,7 @@ async fn main() -> Result<(), Box> { // If post-connection client-server bidirectional communication is needed, use a // `ClientConnectListenerKernel` instead which runs a closure each time a new connection is // established with a client - let kernel = server::empty::EmptyKernel; + let kernel = server::empty::EmptyKernel::default(); // Build the server let node = DefaultNodeBuilder::default() diff --git a/example-library/examples/c2s/server_basic_with_password.rs b/example-library/examples/c2s/server_basic_with_password.rs index 0b028a8d2..b78a55e29 100644 --- a/example-library/examples/c2s/server_basic_with_password.rs +++ b/example-library/examples/c2s/server_basic_with_password.rs @@ -53,7 +53,7 @@ async fn main() -> Result<(), Box> { // If post-connection client-server bidirectional communication is needed, use a // `ClientConnectListenerKernel` instead which runs each time a new connection is // established with a client - let kernel = server::empty::EmptyKernel; + let kernel = server::empty::EmptyKernel::default(); // Build the server. It is password-protected, meaning that each time // a client attempts to register or connect, they must provide the password. diff --git a/example-library/examples/c2s/server_echo.rs b/example-library/examples/c2s/server_echo.rs index 4c2543f9d..f675a7ef4 100644 --- a/example-library/examples/c2s/server_echo.rs +++ b/example-library/examples/c2s/server_echo.rs @@ -61,7 +61,7 @@ async fn main() -> Result<(), Box> { ); // Echo the message back - if let Err(e) = tx.send_message(msg.into()).await { + if let Err(e) = tx.send_message(msg).await { println!("Error sending response: {}", e); } } diff --git a/example-library/examples/p2p/chat.rs b/example-library/examples/p2p/chat.rs index 8dd5a5955..819e096f7 100644 --- a/example-library/examples/p2p/chat.rs +++ b/example-library/examples/p2p/chat.rs @@ -59,7 +59,7 @@ async fn main() -> Result<(), Box> { my_user, "Name", "notsecurepassword", - )? + ) .with_session_security_settings(session_security) .disable_udp() .build()?; @@ -101,7 +101,7 @@ async fn main() -> Result<(), Box> { line = stdin.next_line() => { match line { Ok(Some(msg)) if !msg.is_empty() => { - tx.send_message(msg.into_bytes().into()).await?; + tx.send_message(msg.into_bytes()).await?; } Ok(None) => break, // EOF _ => continue, diff --git a/example-library/examples/p2p/file_transfer.rs b/example-library/examples/p2p/file_transfer.rs index 1fbf36846..86801a16c 100644 --- a/example-library/examples/p2p/file_transfer.rs +++ b/example-library/examples/p2p/file_transfer.rs @@ -59,7 +59,7 @@ async fn main() -> Result<(), Box> { my_user, "Name", "notsecurepassword", - )? + ) .with_session_security_settings(session_security) .disable_udp() .build()?; diff --git a/example-library/examples/p2p/revfs_delete.rs b/example-library/examples/p2p/revfs_delete.rs index 6784944ee..09814fbe9 100644 --- a/example-library/examples/p2p/revfs_delete.rs +++ b/example-library/examples/p2p/revfs_delete.rs @@ -69,7 +69,7 @@ async fn main() -> Result<(), Box> { my_user, "Name", "notsecurepassword", - )? + ) .with_session_security_settings(session_security) .disable_udp() .build()?; @@ -107,7 +107,7 @@ async fn main() -> Result<(), Box> { // Now, delete the contents of the file from the remote peer citadel_sdk::fs::delete(&peer_remote, virtual_file_path).await?; // Alert the other side that the file has been successfully processed - tx.send_message(SecBuffer::from("success").into()).await?; + tx.send_message(SecBuffer::from("success")).await?; tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } else { let incoming_file_requests = remote.get_incoming_file_transfer_handle().unwrap(); diff --git a/example-library/examples/p2p/revfs_read_write.rs b/example-library/examples/p2p/revfs_read_write.rs index 51ff19f36..293ff9853 100644 --- a/example-library/examples/p2p/revfs_read_write.rs +++ b/example-library/examples/p2p/revfs_read_write.rs @@ -67,7 +67,7 @@ async fn main() -> Result<(), Box> { my_user, "Name", "notsecurepassword", - )? + ) .with_session_security_settings(session_security) .disable_udp() .build()?; @@ -111,7 +111,7 @@ async fn main() -> Result<(), Box> { tokio::fs::read_to_string(&locally_downloaded_file).await?; assert_eq!(file_contents, downloaded_file_contents); // Alert the other side that the file has been successfully stored - tx.send_message(SecBuffer::from("success").into()).await?; + tx.send_message(SecBuffer::from("success")).await?; tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } else { let incoming_file_requests = remote.get_incoming_file_transfer_handle().unwrap(); diff --git a/example-library/examples/p2p/revfs_take.rs b/example-library/examples/p2p/revfs_take.rs index 6c85f9caa..bb50d6cb4 100644 --- a/example-library/examples/p2p/revfs_take.rs +++ b/example-library/examples/p2p/revfs_take.rs @@ -115,7 +115,7 @@ async fn main() -> Result<(), Box> { tokio::fs::read_to_string(&locally_downloaded_file).await?; assert_eq!(file_contents, downloaded_file_contents); // Alert the other side that the file has been successfully processed - tx.send_message(SecBuffer::from("success").into()).await?; + tx.send_message(SecBuffer::from("success")).await?; tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } else { let incoming_file_requests = remote.get_incoming_file_transfer_handle().unwrap(); From f09ff168a8b20d5c6e414ad76ac0b4602fb50d2f Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Tue, 31 Dec 2024 14:50:56 -0500 Subject: [PATCH 10/12] wip: pre-proto-messaging-overhaul --- .../src/endpoint_crypto_container.rs | 107 ++++-- citadel_crypt/src/lib.rs | 13 +- citadel_crypt/src/messsaging.rs | 193 ++++++++++ citadel_crypt/src/misc.rs | 8 +- citadel_crypt/src/packet_vector.rs | 2 +- .../src/{ => ratchets}/entropy_bank.rs | 12 +- citadel_crypt/src/ratchets/mod.rs | 6 +- citadel_crypt/src/ratchets/mono/ratchet.rs | 4 +- .../src/{ => ratchets}/ratchet_manager.rs | 360 +++++++++++------- citadel_crypt/src/ratchets/stacked/ratchet.rs | 4 +- citadel_crypt/src/scramble/crypt_splitter.rs | 4 +- citadel_crypt/src/scramble/mod.rs | 2 + .../streaming_crypt_scrambler.rs | 4 +- citadel_crypt/src/secure_buffer/mod.rs | 2 +- citadel_crypt/src/secure_buffer/sec_packet.rs | 2 +- citadel_crypt/src/sync_toggle.rs | 2 +- citadel_crypt/src/toolset.rs | 4 +- citadel_crypt/tests/primary.rs | 6 +- citadel_proto/src/lib.rs | 2 +- citadel_proto/src/proto/node_request.rs | 2 +- .../proto/packet_processor/register_packet.rs | 21 +- citadel_proto/src/proto/session.rs | 17 +- citadel_proto/src/proto/session_manager.rs | 2 +- citadel_proto/src/proto/state_container.rs | 5 +- citadel_proto/src/proto/validation.rs | 4 +- citadel_user/src/account_loader.rs | 11 +- citadel_user/src/account_manager.rs | 9 +- citadel_user/src/auth/mod.rs | 12 +- citadel_user/src/auth/proposed_credentials.rs | 2 +- .../src/backend/filesystem_backend.rs | 2 +- citadel_user/src/backend/mod.rs | 2 +- citadel_user/src/backend/redis_backend.rs | 8 +- citadel_user/src/backend/sql_backend.rs | 21 +- citadel_user/src/client_account.rs | 216 +++++------ citadel_user/src/lib.rs | 2 +- citadel_user/tests/primary.rs | 53 ++- 36 files changed, 715 insertions(+), 411 deletions(-) create mode 100644 citadel_crypt/src/messsaging.rs rename citadel_crypt/src/{ => ratchets}/entropy_bank.rs (97%) rename citadel_crypt/src/{ => ratchets}/ratchet_manager.rs (74%) rename citadel_crypt/src/{ => scramble}/streaming_crypt_scrambler.rs (99%) diff --git a/citadel_crypt/src/endpoint_crypto_container.rs b/citadel_crypt/src/endpoint_crypto_container.rs index 6b956f6ad..a20362901 100644 --- a/citadel_crypt/src/endpoint_crypto_container.rs +++ b/citadel_crypt/src/endpoint_crypto_container.rs @@ -58,68 +58,83 @@ use crate::misc::CryptError; use crate::ratchets::Ratchet; use crate::sync_toggle::{CurrentToggleState, SyncToggle}; use crate::toolset::{Toolset, ToolsetUpdateStatus}; +use citadel_io::RwLock; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_types::crypto::CryptoParameters; use citadel_types::prelude::{ObjectId, SecurityLevel}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::fmt::Debug; +use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; +use std::sync::Arc; use uuid::Uuid; /// A container that holds the toolset as well as some boolean flags to ensure validity /// in tight concurrency situations. It is up to the networking protocol to ensure /// that the inner functions are called when appropriate -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct PeerSessionCrypto { #[serde(bound = "")] - pub toolset: Toolset, + toolset: Arc>>, pub update_in_progress: SyncToggle, // if local is initiator, then in the case both nodes send a FastMessage at the same time (causing an update to the keys), the initiator takes preference, and the non-initiator's upgrade attempt gets dropped (if update_in_progress) - pub local_is_initiator: bool, - pub rolling_group_id: u64, + local_is_initiator: bool, + cid: u64, + pub incrementing_group_id: Arc, /// Alice sends to Bob, then bob updates internally the toolset. However. Bob can't send packets to Alice quite yet using that newest version. He must first wait from Alice to commit on her end and wait for an ACK. /// If alice sends a packet using the latest version, that's okay since we already have that entropy_bank version on Bob's side; it's just that Bob can't send packets using the latest version until AFTER receiving the ACK - pub latest_usable_version: u32, + pub latest_usable_version: Arc, } +const ORDERING: Ordering = Ordering::Relaxed; + impl PeerSessionCrypto { - /// Creates a new [PeerSessionCrypto] instance + /// Creates a new [`PeerSessionCrypto`] instance /// /// `local_is_initiator`: May also be "local_is_server", or any constant designation used to determine /// priority in case of concurrent conflicts + /// + /// This should only be called by the RatchetConstructor pub fn new(toolset: Toolset, local_is_initiator: bool) -> Self { Self { - toolset, + cid: toolset.cid, + toolset: Arc::new(RwLock::new(toolset)), update_in_progress: SyncToggle::new(), local_is_initiator, - rolling_group_id: 0, - latest_usable_version: 0, + incrementing_group_id: Arc::new(AtomicU64::new(0)), + latest_usable_version: Arc::new(AtomicU32::new(0)), } } /// Derives a new version of self safe to be used in the protocol /// Changes made to the returned version will not persist + // TODO: Remove pub fn new_session(&self) -> Self { Self { + cid: self.cid, toolset: self.toolset.clone(), update_in_progress: self.update_in_progress.clone(), local_is_initiator: self.local_is_initiator, - rolling_group_id: self.rolling_group_id, - latest_usable_version: self.latest_usable_version, + incrementing_group_id: self.incrementing_group_id.clone(), + latest_usable_version: self.latest_usable_version.clone(), } } /// Gets a specific entropy_bank version, or, gets the latest version committed - pub fn get_ratchet(&self, version: Option) -> Option<&R> { + pub fn get_ratchet(&self, version: Option) -> Option { self.toolset - .get_stacked_ratchet(version.unwrap_or(self.latest_usable_version)) + .read() + .get_stacked_ratchet( + version.unwrap_or_else(|| self.latest_usable_version.load(ORDERING)), + ) + .cloned() } /// This should only be called when Bob receives the new DOU during the ReKey phase (will receive transfer), or, when Alice receives confirmation /// that the endpoint updated the ratchet (no transfer received, since none needed) #[allow(clippy::type_complexity)] pub fn commit_next_stacked_ratchet_version( - &mut self, + &self, mut newest_version: R::Constructor, local_cid: u64, generate_next: bool, @@ -130,7 +145,8 @@ impl PeerSessionCrypto { ), CryptError, > { - let cur_vers = self.toolset.get_most_recent_stacked_ratchet_version(); + let mut toolset = self.toolset.write(); + let cur_vers = toolset.get_most_recent_stacked_ratchet_version(); let next_vers = cur_vers.wrapping_add(1); // Update version before any stage operations @@ -147,7 +163,7 @@ impl PeerSessionCrypto { .to_string(), ) })?; - let status = self.toolset.update_from(latest_ratchet).ok_or_else(|| { + let status = toolset.update_from(latest_ratchet).ok_or_else(|| { CryptError::RekeyUpdateError( "Unable to progress past update_from for bob-to-alice trigger".to_string(), ) @@ -168,12 +184,9 @@ impl PeerSessionCrypto { "Unable to progress past finish_with_custom_cid".to_string(), ) })?; - let status = self - .toolset - .update_from(next_stacked_ratchet) - .ok_or_else(|| { - CryptError::RekeyUpdateError("Unable to progress past update_from".to_string()) - })?; + let status = toolset.update_from(next_stacked_ratchet).ok_or_else(|| { + CryptError::RekeyUpdateError("Unable to progress past update_from".to_string()) + })?; log::trace!(target: "citadel", "[E2E] Client {local_cid} successfully updated Ratchet from v{cur_vers} to v{next_vers}"); Ok((Some(transfer), status)) @@ -181,19 +194,21 @@ impl PeerSessionCrypto { /// Deregisters the oldest StackedRatchet version. Requires the version input to ensure program/network consistency for debug purposes pub fn deregister_oldest_stacked_ratchet( - &mut self, + &self, version: u32, ) -> Result<(), CryptError> { - self.toolset.deregister_oldest_stacked_ratchet(version) + self.toolset + .write() + .deregister_oldest_stacked_ratchet(version) } /// Performs an update internally, only if sync conditions allow pub fn update_sync_safe( - &mut self, + &self, constructor: R::Constructor, triggered_by_bob_to_alice_transfer: bool, ) -> Result, CryptError> { - let local_cid = self.toolset.cid; + let local_cid = self.cid; let update_in_progress = self.update_in_progress.toggle_on_if_untoggled() == CurrentToggleState::AlreadyToggled; @@ -237,34 +252,33 @@ impl PeerSessionCrypto { } /// Unlocks the hold on future updates, then returns the latest stacked_ratchet - pub fn maybe_unlock(&mut self) -> Option<&R> { + pub fn maybe_unlock(&self) -> Option { if self.update_in_progress.reset_and_get_previous() != CurrentToggleState::AlreadyToggled { - log::error!(target: "citadel", "Client {} expected update_in_progress to be true", self.toolset.cid); + log::error!(target: "citadel", "Client {} expected update_in_progress to be true", self.cid); return None; } - log::trace!(target: "citadel", "Unlocking for {}", self.toolset.cid); + log::trace!(target: "citadel", "Unlocking for {}", self.cid); self.get_ratchet(None) } /// For alice: this should be called ONLY if the update occurred locally. This updates the latest usable version at the endpoint /// For bob: this should be called AFTER receiving the TRUNCATE_STATUS/ACK packet - pub fn post_alice_stage1_or_post_stage1_bob(&mut self) { - log::trace!(target: "citadel", "post_alice_stage1_or_post_stage1_bob for {}: Upgrading from {} to {}", self.toolset.cid, self.latest_usable_version, self.latest_usable_version.wrapping_add(1)); - self.latest_usable_version = self.latest_usable_version.wrapping_add(1); + pub fn post_alice_stage1_or_post_stage1_bob(&self) { + log::trace!(target: "citadel", "post_alice_stage1_or_post_stage1_bob for {}: Upgrading from {} to {}", self.cid, self.latest_usable_version(), self.latest_usable_version().wrapping_add(1)); + let _ = self.latest_usable_version.fetch_add(1, ORDERING); } pub fn get_and_increment_group_id(&mut self) -> u64 { - self.rolling_group_id = self.rolling_group_id.wrapping_add(1); - self.rolling_group_id.wrapping_sub(1) + self.incrementing_group_id.fetch_add(1, ORDERING) } - pub fn get_next_object_id(&mut self) -> ObjectId { + pub fn get_next_object_id(&self) -> ObjectId { Uuid::new_v4().as_u128().into() } - pub fn get_next_constructor(&mut self) -> Option { + pub fn get_next_constructor(&self) -> Option { if self.update_in_progress.toggle_on_if_untoggled() == CurrentToggleState::JustToggled { self.get_ratchet(None)?.next_alice_constructor() } else { @@ -273,20 +287,37 @@ impl PeerSessionCrypto { } /// Refreshed the internal state to init state - pub fn refresh_state(&mut self) { + pub fn refresh_state(&self) { self.update_in_progress.toggle_off(); - self.rolling_group_id = 0; + self.incrementing_group_id.store(0, ORDERING); } /// Gets the parameters used at registrations pub fn get_default_params(&self) -> CryptoParameters { self.toolset + .read() .get_static_auxiliary_ratchet() .get_message_pqc_and_entropy_bank_at_layer(None) .expect("Expected to get message pqc and entropy bank") .0 .params } + + pub fn local_is_initiator(&self) -> bool { + self.local_is_initiator + } + + pub fn latest_usable_version(&self) -> u32 { + self.latest_usable_version.load(ORDERING) + } + + pub fn cid(&self) -> u64 { + self.cid + } + + pub fn toolset(&self) -> &Arc>> { + &self.toolset + } } pub trait AssociatedSecurityLevel { diff --git a/citadel_crypt/src/lib.rs b/citadel_crypt/src/lib.rs index cfb3c5ad5..367bda6af 100644 --- a/citadel_crypt/src/lib.rs +++ b/citadel_crypt/src/lib.rs @@ -44,10 +44,10 @@ pub mod prelude { pub use citadel_pqcrypto::constructor_opts::ConstructorOpts; pub use citadel_pqcrypto::{bytes_in_place::EzBuffer, PostQuantumContainer}; - pub use crate::entropy_bank::EntropyBank; pub use crate::misc::CryptError; pub use crate::packet_vector::PacketVector; - pub use crate::streaming_crypt_scrambler::FixedSizedSource; + pub use crate::ratchets::entropy_bank::EntropyBank; + pub use crate::scramble::streaming_crypt_scrambler::FixedSizedSource; pub use crate::toolset::Toolset; pub use citadel_types::crypto::SecBuffer; pub use citadel_types::crypto::SecurityLevel; @@ -57,8 +57,6 @@ pub mod prelude { pub mod argon; /// An abstraction binding the entropy_bank and the PQC pub mod endpoint_crypto_container; -/// Organizes the different types of entropy_banks that can be used. Currently, there is only one: The Standard Drill -pub mod entropy_bank; /// Error type pub mod misc; /// For endowing packets with coordinates @@ -69,11 +67,10 @@ pub mod ratchets; pub mod scramble; /// For secure byte handling pub mod secure_buffer; -/// Allows thread-pooled asynchronous and parallel file processing -pub mod streaming_crypt_scrambler; -/// `RatchetManager` provides a robust, sync-safe method to rekey across networks between two nodes -pub mod ratchet_manager; pub mod sync_toggle; /// Provides entropy_bank management, update, and versioning. This is what's exposed to the citadel_user api. The entropy_banks themselves are abstracted beneath pub mod toolset; + +/// For secure messaging with concurrent ratcheting operations +pub mod messsaging; diff --git a/citadel_crypt/src/messsaging.rs b/citadel_crypt/src/messsaging.rs new file mode 100644 index 000000000..e47f3ef02 --- /dev/null +++ b/citadel_crypt/src/messsaging.rs @@ -0,0 +1,193 @@ +use crate::misc::CryptError; +use crate::ratchets::ratchet_manager::{AttachedPayload, DefaultRatchetManager, RatchetMessage}; +use crate::ratchets::Ratchet; +use citadel_io::tokio; +use citadel_types::prelude::SecrecyMode; +use futures::{SinkExt, Stream}; +use std::collections::VecDeque; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use tokio::sync::mpsc::UnboundedReceiver; + +/// A messenger intended for use by the citadel_proto package for two nodes to use +/// for messaging. It enforces the secrecy mode such that: +/// [*] True Perfect Forward Secrecy: C message can only be sent if a new key is +/// ready for use. This uses head-of-line blocking, placing any messages into the queue +/// until a rekey is complete. This is best for pure messaging applications. +/// [*] Best effort mode: Messages will attempt to use an unused key, but, will re-use the +/// same key in order to not block. This is best for high-throughput applications. +/// +/// This pattern bypasses the kernel executor for direct writing and reading from the +/// underlying stream at the application-layer +pub struct RatchetManagerMessengerLayer { + sink: RatchetManagerMessengerLayerTx, + stream: RatchetManagerMessengerLayerRx

, +} + +pub struct RatchetManagerMessengerLayerTx +{ + manager: DefaultRatchetManager, + enqueued_messages: Arc>>, + secrecy_mode: SecrecyMode, +} + +impl Clone + for RatchetManagerMessengerLayerTx +{ + fn clone(&self) -> Self { + Self { + manager: self.manager.clone(), + enqueued_messages: self.enqueued_messages.clone(), + secrecy_mode: self.secrecy_mode, + } + } +} + +pub struct RatchetManagerMessengerLayerRx { + rx: UnboundedReceiver

, +} + +impl RatchetManagerMessengerLayer +where + E: Send + Sync + 'static, + R: Ratchet, + P: AttachedPayload, +{ + pub fn new(manager: DefaultRatchetManager, secrecy_mode: SecrecyMode) -> Self { + // The rx below will receive all messages that get sent via the ratchet manager layer + // Some messages will "catch a ride" alongside a re-keying packet, others will be + // standalone. This is not Pandora. + let rx = manager + .take_payload_rx() + .expect("Cannot pass a RatchetManager that has no payload receiver!"); + let mut on_rekey_finish_listener = manager + .take_on_rekey_finished_event_listener() + .expect("Cannot pass a RatchetManager that has no on_rekey listener!"); + let enqueued_messages = Arc::new(tokio::sync::Mutex::new(VecDeque::new())); + let enqueued_messages_clone = enqueued_messages.clone(); + let manager_clone = manager.clone(); + + drop(tokio::task::spawn(async move { + // Each time a rekey finishes, we should check the local queue for any enqueued messages + // to poll and send + while let Some(_next_ratchet) = on_rekey_finish_listener.recv().await { + // TODO: Send notifications to kernel, if applicable, with the latest ratchet + // Check the latest item in the queue. Hold the lock to prevent race conditions locally + let mut lock = enqueued_messages_clone.lock().await; + if let Some(last_item) = lock.pop_front() { + match manager_clone + .trigger_rekey_with_payload(Some(last_item)) + .await + { + Ok(Some(message_not_sent)) => { + lock.insert(0, message_not_sent); + } + + Ok(None) => { + // Successfully sent + } + + Err(err) => { + log::error!(target: "citadel", "RatchetManager failed to trigger rekey: {:?}", err); + } + } + } + } + + log::warn!(target: "citadel", "on_rekey_finish_listener died") + })); + + Self { + sink: RatchetManagerMessengerLayerTx { + manager, + enqueued_messages, + secrecy_mode, + }, + stream: RatchetManagerMessengerLayerRx { rx }, + } + } + + pub fn split( + self, + ) -> ( + RatchetManagerMessengerLayerTx, + RatchetManagerMessengerLayerRx

, + ) { + (self.sink, self.stream) + } +} + +impl RatchetManagerMessengerLayerTx +where + E: Send + Sync + 'static, + R: Ratchet, + P: AttachedPayload, +{ + /// Triggers a re key on a separate task + pub fn trigger_rekey(&self) { + let this = self.manager.clone(); + drop(tokio::task::spawn(async move { + match this.trigger_rekey().await { + Ok(_new_vers) => (), + Err(err) => log::error!(target: "citadel", "{:?}", err), + } + })); + } + + pub async fn send(&mut self, message: impl Into

) -> Result<(), CryptError> { + match self.secrecy_mode { + SecrecyMode::BestEffort => { + if let Some(message_not_sent) = self + .manager + .trigger_rekey_with_payload(Some(message.into())) + .await? + { + // Just send through channel + self.manager + .sender + .lock() + .await + .send(RatchetMessage::JustMessage(message_not_sent)) + .await + .map_err(|_| { + CryptError::FatalError("Ratchet Manager's outbound stream died".into()) + })?; + } else { + // Success; this message will trigger a simultaneous rekey + } + + Ok(()) + } + + SecrecyMode::Perfect => { + if let Some(message_not_sent) = self + .manager + .trigger_rekey_with_payload(Some(message.into())) + .await? + { + // We need to enqueue + self.enqueued_messages + .lock() + .await + .push_back(message_not_sent); + } else { + // Success; this message will trigger a simultaneous rekey w/o enqueuing + } + + Ok(()) + } + } + } +} + +impl

Stream for RatchetManagerMessengerLayerRx

+where + P: AttachedPayload, +{ + type Item = P; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.get_mut().rx.poll_recv(cx) + } +} diff --git a/citadel_crypt/src/misc.rs b/citadel_crypt/src/misc.rs index c8dc45fa6..462c3365c 100644 --- a/citadel_crypt/src/misc.rs +++ b/citadel_crypt/src/misc.rs @@ -41,10 +41,10 @@ //! //! # Related Components //! -//! - [`crate::entropy_bank`] - Uses port mappings +//! - [`crate::ratchets::entropy_bank`] - Uses port mappings //! - [`citadel_types::crypto::SecurityLevel`] - Security settings -use crate::entropy_bank::DRILL_RANGE; +use crate::ratchets::entropy_bank::DRILL_RANGE; use rand::prelude::SliceRandom; use rand::thread_rng; use std::fmt::{Display, Formatter}; @@ -62,6 +62,8 @@ pub enum CryptError { OutOfBoundsError, /// This occurs if the byte-valued security level desired does not correspond to an actual [SecurityLevel] BadSecuritySetting, + /// Implies a component is no longer working + FatalError(String), } impl CryptError { @@ -76,6 +78,7 @@ impl CryptError { CryptError::RekeyUpdateError(s) => s.into(), CryptError::OutOfBoundsError => "[CryptError] Out of bounds exception".to_string(), CryptError::BadSecuritySetting => "[CryptError] Bad security setting".to_string(), + CryptError::FatalError(s) => s, } } @@ -89,6 +92,7 @@ impl CryptError { CryptError::RekeyUpdateError(s) => s.as_ref(), CryptError::OutOfBoundsError => "[CryptError] Out of bounds exception", CryptError::BadSecuritySetting => "[CryptError] Bad security setting", + CryptError::FatalError(s) => s.as_ref(), } } } diff --git a/citadel_crypt/src/packet_vector.rs b/citadel_crypt/src/packet_vector.rs index d0d50dba5..982bd4b99 100644 --- a/citadel_crypt/src/packet_vector.rs +++ b/citadel_crypt/src/packet_vector.rs @@ -50,7 +50,7 @@ //! - [`crate::secure_buffer::sec_packet`] - Packet buffer implementation //! -use crate::entropy_bank::EntropyBank; +use crate::ratchets::entropy_bank::EntropyBank; use num_integer::Integer; use zeroize::ZeroizeOnDrop; diff --git a/citadel_crypt/src/entropy_bank.rs b/citadel_crypt/src/ratchets/entropy_bank.rs similarity index 97% rename from citadel_crypt/src/entropy_bank.rs rename to citadel_crypt/src/ratchets/entropy_bank.rs index fc9c12acb..5750d6552 100644 --- a/citadel_crypt/src/entropy_bank.rs +++ b/citadel_crypt/src/ratchets/entropy_bank.rs @@ -298,12 +298,12 @@ use zeroize::Zeroizing; /// A entropy bank is a fundamental dataset that continually morphs into new future sets #[derive(Serialize, Deserialize)] pub struct EntropyBank { - pub(super) algorithm: EncryptionAlgorithm, - pub(super) version: u32, - pub(super) cid: u64, - pub(super) entropy: Zeroizing<[u8; BYTES_PER_STORE]>, - pub(super) scramble_mappings: Zeroizing>, - pub(super) transient_counter: AtomicU64, + pub(crate) algorithm: EncryptionAlgorithm, + pub(crate) version: u32, + pub(crate) cid: u64, + pub(crate) entropy: Zeroizing<[u8; BYTES_PER_STORE]>, + pub(crate) scramble_mappings: Zeroizing>, + pub(crate) transient_counter: AtomicU64, } /// Returns the approximate number of bytes needed to serialize a Drill diff --git a/citadel_crypt/src/ratchets/mod.rs b/citadel_crypt/src/ratchets/mod.rs index 2153649fc..73d572d6e 100644 --- a/citadel_crypt/src/ratchets/mod.rs +++ b/citadel_crypt/src/ratchets/mod.rs @@ -1,15 +1,19 @@ use crate::endpoint_crypto_container::EndpointRatchetConstructor; -use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; use bytes::BytesMut; use citadel_pqcrypto::bytes_in_place::EzBuffer; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_pqcrypto::PostQuantumContainer; use citadel_types::crypto::SecurityLevel; +use entropy_bank::EntropyBank; use serde::{Deserialize, Serialize}; use std::borrow::Cow; +/// Organizes the different types of entropy_banks that can be used. Currently, there is only one: The Standard Drill +pub mod entropy_bank; pub mod mono; +/// `RatchetManager` provides a robust, sync-safe method to rekey across networks between two nodes +pub mod ratchet_manager; pub mod stacked; /// For allowing registration inside the toolset diff --git a/citadel_crypt/src/ratchets/mono/ratchet.rs b/citadel_crypt/src/ratchets/mono/ratchet.rs index 34cb8d265..2b2a987d3 100644 --- a/citadel_crypt/src/ratchets/mono/ratchet.rs +++ b/citadel_crypt/src/ratchets/mono/ratchet.rs @@ -50,15 +50,15 @@ //! //! ## Related Components //! - [`FcmKeys`](super::keys::FcmKeys): FCM credential management -//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Entropy source +//! - [`EntropyBank`](crate::ratchets::entropy_bank::EntropyBank): Entropy source //! - [`PostQuantumContainer`](citadel_pqcrypto::PostQuantumContainer): PQ crypto operations //! - [`Ratchet`](crate::ratchets::Ratchet): Base ratchet trait use crate::endpoint_crypto_container::{ AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, }; -use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; +use crate::ratchets::entropy_bank::EntropyBank; use crate::ratchets::Ratchet; use arrayvec::ArrayVec; use citadel_pqcrypto::constructor_opts::ConstructorOpts; diff --git a/citadel_crypt/src/ratchet_manager.rs b/citadel_crypt/src/ratchets/ratchet_manager.rs similarity index 74% rename from citadel_crypt/src/ratchet_manager.rs rename to citadel_crypt/src/ratchets/ratchet_manager.rs index 29bc2b7bc..a8d46fc81 100644 --- a/citadel_crypt/src/ratchet_manager.rs +++ b/citadel_crypt/src/ratchets/ratchet_manager.rs @@ -55,44 +55,54 @@ use crate::endpoint_crypto_container::{ EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, }; use crate::misc::CryptError; +use crate::prelude::Toolset; use crate::ratchets::Ratchet; use atomic::Atomic; use bytemuck::NoUninit; use citadel_io::tokio::sync::Mutex as TokioMutex; use citadel_io::tokio_stream::Stream; -use citadel_io::{Mutex, RwLock}; +use citadel_io::{tokio, Mutex}; use futures::{Sink, SinkExt, StreamExt}; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::sync::atomic::Ordering; use std::sync::Arc; +use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; -pub struct RatchetManager +pub struct RatchetManager where R: Ratchet, { - sender: Arc>, + pub(crate) sender: Arc>, receiver: Arc>>, - container: Arc>>, + pub(crate) session_crypto_state: PeerSessionCrypto, + attached_payload_tx: UnboundedSender

, + attached_payload_rx: Arc>>>, + rekey_done_notifier: Arc>>>, cid: u64, psks: Arc>>, role: Arc>, constructor: Arc>>, is_initiator: bool, state: Arc>, - local_listener: - Arc>>>>, + local_listener: LocalListener, } -impl Clone for RatchetManager { +pub(crate) type LocalListener = Arc>>>; + +impl Clone for RatchetManager { fn clone(&self) -> Self { Self { sender: self.sender.clone(), receiver: self.receiver.clone(), - container: self.container.clone(), + session_crypto_state: self.session_crypto_state.clone(), cid: self.cid, psks: self.psks.clone(), role: self.role.clone(), + attached_payload_tx: self.attached_payload_tx.clone(), + attached_payload_rx: self.attached_payload_rx.clone(), + rekey_done_notifier: self.rekey_done_notifier.clone(), constructor: self.constructor.clone(), is_initiator: self.is_initiator, state: self.state.clone(), @@ -118,11 +128,14 @@ pub enum RekeyRole { } #[derive(Serialize, Deserialize)] -pub enum RatchetMessage { +pub enum RatchetMessage { AliceToBob { payload: Vec, earliest_ratchet_version: u32, latest_ratchet_version: u32, + // Note: it is up to the underlying stream to handle encryption, if necessary, of the attached payload + #[serde(bound = "")] + attached_payload: Option

, }, // Serialized transfer BobToAlice(Vec, RekeyRole), // Serialized transfer + sender's role Truncate(u32), // Version to truncate @@ -130,9 +143,11 @@ pub enum RatchetMessage { latest_version: u32, }, LoserCanFinish, + #[serde(bound = "")] + JustMessage(P), } -impl Debug for RatchetMessage { +impl Debug for RatchetMessage

{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { RatchetMessage::AliceToBob { .. } => write!(f, "AliceToBob"), @@ -140,16 +155,46 @@ impl Debug for RatchetMessage { RatchetMessage::Truncate(_) => write!(f, "Truncate"), RatchetMessage::LeaderCanFinish { .. } => write!(f, "LeaderCanFinish"), RatchetMessage::LoserCanFinish => write!(f, "LoserCanFinish"), + RatchetMessage::JustMessage(_) => write!(f, "JustMessage"), } } } -impl RatchetManager +pub trait RatchetManagerSink: + Sink> + Send + Unpin + 'static +{ +} + +impl RatchetManagerSink

for S where + S: Sink> + Send + Unpin + 'static +{ +} + +pub trait RatchetManagerStream: + Stream> + Send + Unpin + 'static +{ +} +impl RatchetManagerStream

for I where + I: Stream> + Send + Unpin + 'static +{ +} + +pub trait AttachedPayload: Send + Sync + 'static + Serialize + DeserializeOwned {} +impl AttachedPayload for T {} + +pub type DefaultRatchetManager = RatchetManager< + Box>, + Box>, + R, + P, +>; + +impl RatchetManager where - S: Sink + Send + Unpin + 'static, - I: Stream + Send + Unpin + 'static, + S: RatchetManagerSink

, + I: RatchetManagerStream

, R: Ratchet, - >::Error: std::fmt::Debug, + P: AttachedPayload, { pub fn new>( sender: S, @@ -157,28 +202,53 @@ where container: PeerSessionCrypto, psks: &[T], ) -> Self { - let cid = container.toolset.cid; - let is_initiator = container.local_is_initiator; - + let cid = container.cid(); + let is_initiator = container.local_is_initiator(); + let (attached_payload_tx, attached_payload_rx) = tokio::sync::mpsc::unbounded_channel(); + let (rekey_done_notifier_tx, rekey_done_notifier) = tokio::sync::mpsc::unbounded_channel(); + let rekey_done_notifier = Arc::new(Mutex::new(Some(rekey_done_notifier))); let this = Self { sender: Arc::new(TokioMutex::new(sender)), receiver: Arc::new(Mutex::new(Some(receiver))), - container: Arc::new(RwLock::new(container)), + session_crypto_state: container, cid, is_initiator, constructor: Arc::new(Mutex::new(None)), + attached_payload_tx, + attached_payload_rx: Arc::new(Mutex::new(Some(attached_payload_rx))), + rekey_done_notifier, psks: Arc::new(psks.iter().map(|psk| psk.as_ref().to_vec()).collect()), role: Arc::new(Atomic::new(RekeyRole::Idle)), state: Arc::new(Atomic::new(RekeyState::Idle)), local_listener: Arc::new(Mutex::new(None)), }; - this.clone().spawn_rekey_process(); + this.clone().spawn_rekey_process(rekey_done_notifier_tx); this } - /// Returns true if the re-key was a success, false if no re-key was needed - pub async fn trigger_rekey(&self) -> Result { + pub fn new_from_components>( + toolset: Toolset, + local_is_initiator: bool, + sender: S, + receiver: I, + psks: &[T], + ) -> Self { + let container = PeerSessionCrypto::new(toolset, local_is_initiator); + Self::new(sender, receiver, container, psks) + } + + /// Triggers a rekey without sending an attached payload + pub async fn trigger_rekey(&self) -> Result<(), CryptError> { + self.trigger_rekey_with_payload(None).await.map(|_| ()) + } + + /// Supposing the payload is Some: Returns Ok(None) if the payload was sent along with the rekey bundle, + /// otherwise, returns the attached payload for later use. + pub async fn trigger_rekey_with_payload( + &self, + attached_payload: Option

, + ) -> Result, CryptError> { log::info!(target: "citadel", "Client {} manually triggering rekey", self.cid); let state = self.state(); if state == RekeyState::Halted { @@ -189,14 +259,17 @@ where if self.role() != RekeyRole::Idle { // We are already in a rekey process - return Ok(false); + return Ok(attached_payload); } let (constructor, earliest_ratchet_version, latest_ratchet_version) = { - let mut container = self.container.write(); - let constructor = container.get_next_constructor(); - let earliest_ratchet_version = container.toolset.get_oldest_stacked_ratchet_version(); - let latest_ratchet_version = container.latest_usable_version; + let constructor = self.session_crypto_state.get_next_constructor(); + let earliest_ratchet_version = self + .session_crypto_state + .toolset() + .read() + .get_oldest_stacked_ratchet_version(); + let latest_ratchet_version = self.session_crypto_state.latest_usable_version(); ( constructor, earliest_ratchet_version, @@ -219,30 +292,27 @@ where payload, earliest_ratchet_version, latest_ratchet_version, + attached_payload, }) .await - .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + .map_err(|_err| CryptError::RekeyUpdateError("Sink send error".into()))?; log::debug!(target: "citadel", "Client {} sent initial AliceToBob transfer", self.cid); *self.constructor.lock() = Some(constructor); let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); *self.local_listener.lock() = Some(tx); - let err = rx.await.map_err(|_| { + let _res = rx.await.map_err(|_| { CryptError::RekeyUpdateError("Failed to wait for local listener".to_string()) })?; - if let Some(err) = err { - return Err(err); - } - - Ok(true) + Ok(None) } else { - Ok(false) + Ok(attached_payload) } } - fn spawn_rekey_process(self) { + fn spawn_rekey_process(self, rekey_done_notifier_tx: tokio::sync::mpsc::UnboundedSender) { struct DropWrapper { state: Arc>, } @@ -257,6 +327,7 @@ where let _drop_wrapper = DropWrapper { state: self.state.clone(), }; + let mut listener = { self.receiver.lock().take().unwrap() }; loop { self.set_state(RekeyState::Running); @@ -264,15 +335,24 @@ where self.set_state(RekeyState::Idle); self.set_role(RekeyRole::Idle); - let err = result.err(); + match result { + Ok(latest_ratchet) => { + // Alert any local callers waiting for rekeying to finish + if let Err(_err) = rekey_done_notifier_tx.send(latest_ratchet.clone()) { + log::warn!(target: "citadel", "Failed to send rekey done notification"); + } - if let Some(notifier) = self.local_listener.lock().take() { - let _ = notifier.send(err.clone()); - } + // Alert any passive background listeners wanting to keep track of each + // time a rekey finishes, independent of who initiated the rekey + if let Some(notifier) = self.local_listener.lock().take() { + let _ = notifier.send(latest_ratchet); + } + } - if let Some(err) = err { - log::error!("cid {} rekey error: {err:?}", self.cid); - continue; + Err(err) => { + log::error!("cid {} rekey error: {err:?}", self.cid); + continue; + } } } }; @@ -283,7 +363,7 @@ where /// Runs a single round of re-keying, listening to events and returning /// once a single re-key occurs. This function is intended to be used in a loop /// to continuously be ready for re-keying. - async fn rekey(&self, receiver: &mut I) -> Result<(), CryptError> { + async fn rekey(&self, receiver: &mut I) -> Result { log::trace!(target: "citadel", "Client {} starting rekey with initial role {:?}", self.cid, self.role()); let is_initiator = self.is_initiator; let mut completed_as_leader = false; @@ -297,19 +377,27 @@ where payload, earliest_ratchet_version, latest_ratchet_version, + attached_payload, }) => { + if let Some(attached_payload) = attached_payload { + let _ = self.attached_payload_tx.send(attached_payload); + } + let status = { log::debug!(target: "citadel", "Client {} received AliceToBob", self.cid); - let mut container = self.container.write(); // Process the AliceToBob message as Bob let transfer = bincode::deserialize(&payload) .map_err(|err| CryptError::RekeyUpdateError(err.to_string()))?; - let cid = container.toolset.cid; - let local_earliest_ratchet_version = - container.toolset.get_oldest_stacked_ratchet_version(); - let local_latest_ratchet_version = container.latest_usable_version; + let cid = self.session_crypto_state.cid(); + let local_earliest_ratchet_version = self + .session_crypto_state + .toolset() + .read() + .get_oldest_stacked_ratchet_version(); + let local_latest_ratchet_version = + self.session_crypto_state.latest_usable_version(); if earliest_ratchet_version != local_earliest_ratchet_version { log::warn!(target: "citadel", "Client {cid}: Earliest declared ratchet versions do not match. Local: {local_earliest_ratchet_version}, Peer: {earliest_ratchet_version}"); @@ -322,7 +410,8 @@ where } // Get next_opts from the container - let next_opts = container + let next_opts = self + .session_crypto_state .get_ratchet(None) .ok_or_else(|| { CryptError::RekeyUpdateError( @@ -342,7 +431,8 @@ where ) })?; - container.update_sync_safe(bob_constructor, false)? + self.session_crypto_state + .update_sync_safe(bob_constructor, false)? }; match status { @@ -353,10 +443,9 @@ where log::trace!(target: "citadel", "Client {} must send BobToAlice", self.cid); { - let container = self.container.write(); + let container = &self.session_crypto_state; let _ = container.update_in_progress.toggle_on_if_untoggled(); self.set_role(RekeyRole::Loser); - drop(container); } self.sender @@ -364,7 +453,9 @@ where .await .send(RatchetMessage::BobToAlice(serialized, RekeyRole::Loser)) .await - .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + .map_err(|_err| { + CryptError::RekeyUpdateError("Sink send error".into()) + })?; log::debug!( target: "citadel", "Client {} is {:?}. Sent BobToAlice", @@ -425,8 +516,7 @@ where alice_constructor.stage1_alice(transfer, &self.psks)?; let status = { - self.container - .write() + self.session_crypto_state .update_sync_safe(alice_constructor, true)? }; @@ -440,8 +530,7 @@ where if expected_status { if let Some(version_to_truncate) = truncation_required { { - self.container - .write() + self.session_crypto_state .deregister_oldest_stacked_ratchet(version_to_truncate)?; } @@ -450,8 +539,8 @@ where .await .send(RatchetMessage::Truncate(version_to_truncate)) .await - .map_err(|err| { - CryptError::RekeyUpdateError(format!("{err:?}")) + .map_err(|_err| { + CryptError::RekeyUpdateError("Sink send error".into()) })?; // We need to wait to be marked as complete } else { @@ -461,8 +550,8 @@ where .await .send(RatchetMessage::LoserCanFinish) .await - .map_err(|err| { - CryptError::RekeyUpdateError(format!("{err:?}")) + .map_err(|_err| { + CryptError::RekeyUpdateError("Sink send error".into()) })?; } } else { @@ -488,14 +577,14 @@ where } let latest_version = { - let mut container = self.container.write(); + let container = &self.session_crypto_state; container.deregister_oldest_stacked_ratchet(version_to_truncate)?; container.post_alice_stage1_or_post_stage1_bob(); let latest_actual_ratchet_version = container .maybe_unlock() .expect("Failed to fetch ratchet") .version(); - let latest_version = container.latest_usable_version; + let latest_version = container.latest_usable_version(); if latest_actual_ratchet_version != latest_version { log::warn!(target:"citadel", "Client {} received Truncate, but, update failed. Actual: {latest_actual_ratchet_version}, Expected: {latest_version} ", self.cid); } @@ -509,7 +598,7 @@ where .await .send(RatchetMessage::LeaderCanFinish { latest_version }) .await - .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + .map_err(|_err| CryptError::RekeyUpdateError("Sink send error".into()))?; break; } @@ -525,13 +614,13 @@ where log::debug!(target: "citadel", "Client {} received LoserCanFinish", self.cid); let latest_version = { - let mut container = self.container.write(); + let container = &self.session_crypto_state; container.post_alice_stage1_or_post_stage1_bob(); let latest_actual_ratchet_version = container .maybe_unlock() .expect("Failed to fetch ratchet") .version(); - let latest_version = container.latest_usable_version; + let latest_version = container.latest_usable_version(); if latest_actual_ratchet_version != latest_version { log::warn!(target:"citadel", "Client {} received LoserCanFinish but, update failed. Actual: {latest_actual_ratchet_version}, Expected: {latest_version} ", self.cid); } @@ -546,15 +635,12 @@ where .await .send(RatchetMessage::LeaderCanFinish { latest_version }) .await - .map_err(|err| CryptError::RekeyUpdateError(format!("{err:?}")))?; + .map_err(|_err| CryptError::RekeyUpdateError("Sink send error".into()))?; break; } Some(RatchetMessage::LeaderCanFinish { latest_version }) => { - let our_latest_version = { - let container = self.container.read(); - container.latest_usable_version - }; + let our_latest_version = self.session_crypto_state.latest_usable_version(); log::debug!("Client {} received LeaderCanFinish w/ latest_version = {latest_version} | our latest version: {our_latest_version}", self.cid); // Allow Leader if contention, or Idle if no contention @@ -567,15 +653,15 @@ where } { - let mut container = self.container.write(); + let container = &self.session_crypto_state; container.post_alice_stage1_or_post_stage1_bob(); let latest_actual_ratchet_version = container .maybe_unlock() .expect("Failed to fetch ratchet") .version(); - let latest_declared_version = container.latest_usable_version; + let latest_declared_version = container.latest_usable_version(); if latest_actual_ratchet_version != latest_declared_version { - log::warn!(target:"citadel", "Client {} received Truncate, desynced. Actual: {latest_actual_ratchet_version}, Expected: {latest_declared_version} ", self.cid); + log::warn!(target: "citadel", "Client {} received Truncate, desynced. Actual: {latest_actual_ratchet_version}, Expected: {latest_declared_version} ", self.cid); } } @@ -583,6 +669,13 @@ where break; } + Some(RatchetMessage::JustMessage(message)) => { + // If we receive a message, just forward it to the attached payload stream + if self.attached_payload_tx.send(message).is_err() { + log::warn!(target:"citadel", "Attached payload send error"); + } + } + None => { return Err(CryptError::RekeyUpdateError( "Unexpected end of stream".to_string(), @@ -591,24 +684,33 @@ where } } + let latest_ratchet = self.get_ratchet(None).unwrap(); log::debug!( target: "citadel", "Client {} completed re-key. Alice: {}, Bob: {}. Final version: {}. Final Declared Version: {}. Is initiator: {}", self.cid, completed_as_leader, completed_as_loser, - self.get_ratchet(None).unwrap().version(), - self.container.read().latest_usable_version, + latest_ratchet.version(), + self.session_crypto_state.latest_usable_version(), is_initiator ); - log::debug!(target: "citadel", "*** cid {} rekey completed", self.cid); + log::debug!(target: "citadel", "*** Client {} rekey completed ***", self.cid); - Ok(()) + Ok(latest_ratchet) + } + + pub fn take_payload_rx(&self) -> Option> { + self.attached_payload_rx.lock().take() + } + + pub fn take_on_rekey_finished_event_listener(&self) -> Option> { + self.rekey_done_notifier.lock().take() } pub fn get_ratchet(&self, version: Option) -> Option { - self.container.read().get_ratchet(version).cloned() + self.session_crypto_state.get_ratchet(version) } pub fn role(&self) -> RekeyRole { @@ -633,14 +735,14 @@ where mod tests { use crate::endpoint_crypto_container::{EndpointRatchetConstructor, PeerSessionCrypto}; use crate::prelude::Toolset; - use crate::ratchet_manager::{RatchetManager, RatchetMessage}; + use crate::ratchets::ratchet_manager::{ + RatchetManager, RatchetManagerSink, RatchetManagerStream, + }; use crate::ratchets::stacked::ratchet::StackedRatchet; use crate::ratchets::Ratchet; use citadel_io::tokio; use citadel_pqcrypto::constructor_opts::ConstructorOpts; use citadel_types::prelude::{EncryptionAlgorithm, KemAlgorithm, SecurityLevel}; - use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; - use futures::{Sink, Stream}; use rstest::rstest; use std::time::Duration; @@ -680,8 +782,11 @@ mod tests { (alice_container, bob_container) } - type TestRatchetManager = - RatchetManager, UnboundedReceiver, R>; + type TestRatchetManager = RatchetManager< + Box>, + Box>, + R, + >; fn create_ratchet_managers() -> (TestRatchetManager, TestRatchetManager) { let security_level = SecurityLevel::Standard; @@ -695,8 +800,20 @@ mod tests { let (tx_alice, rx_bob) = futures::channel::mpsc::unbounded(); let (tx_bob, rx_alice) = futures::channel::mpsc::unbounded(); - let alice_manager = RatchetManager::new(tx_alice, rx_alice, alice_container, TEST_PSKS); - let bob_manager = RatchetManager::new(tx_bob, rx_bob, bob_container, TEST_PSKS); + let alice_manager = RatchetManager::new( + Box::new(tx_alice) + as Box>, + Box::new(rx_alice) as Box>, + alice_container, + TEST_PSKS, + ); + let bob_manager = RatchetManager::new( + Box::new(tx_bob) + as Box>, + Box::new(rx_bob) as Box>, + bob_container, + TEST_PSKS, + ); (alice_manager, bob_manager) } @@ -713,35 +830,31 @@ mod tests { assert_eq!(bob_container.get_ratchet(None).unwrap().get_cid(), bob_cid); let start_version = alice_container - .toolset + .toolset() + .read() .get_most_recent_stacked_ratchet_version(); let new_version = start_version + 1; let new_version_bob = bob_container - .toolset + .toolset() + .read() .get_most_recent_stacked_ratchet_version() + 1; assert_eq!(new_version, new_version_bob); (start_version, new_version) } - async fn run_round_racy< - S: Sink + Unpin + Send + 'static, - I: Stream + Unpin + Send + 'static, - R: Ratchet, - >( + async fn run_round_racy, I: RatchetManagerStream<()>, R: Ratchet>( container_0: RatchetManager, container_1: RatchetManager, container_0_delay: Option, - ) where - >::Error: std::fmt::Debug, - { + ) { let cid_0 = container_0.cid; let cid_1 = container_1.cid; let (_start_version, _next_version) = pre_round_assertions( - &*container_0.container.read(), + &container_0.session_crypto_state, cid_0, - &*container_1.container.read(), + &container_1.session_crypto_state, cid_1, ); @@ -776,36 +889,34 @@ mod tests { let (alice_result, bob_result) = tokio::join!(alice_handle, bob_handle); // Update original containers with final state - let _rekey_0_res = alice_result.unwrap().unwrap(); - let _rekey_1_res = bob_result.unwrap().unwrap(); + alice_result.unwrap().unwrap(); + bob_result.unwrap().unwrap(); post_checks(&container_0, &container_1); log::info!(target: "citadel", "~~~~ Round ended! ~~~~"); } async fn run_round_one_node_only< - S: Sink + Unpin + Send + 'static, - I: Stream + Unpin + Send + 'static, + S: RatchetManagerSink<()>, + I: RatchetManagerStream<()>, R: Ratchet, >( container_0: RatchetManager, container_1: RatchetManager, - ) where - >::Error: std::fmt::Debug, - { + ) { let cid_0 = container_0.cid; let cid_1 = container_1.cid; let (_start_version, _next_version) = pre_round_assertions( - &*container_0.container.read(), + &container_0.session_crypto_state, cid_0, - &*container_1.container.read(), + &container_1.session_crypto_state, cid_1, ); let task = |container: RatchetManager, skip: bool| async move { if skip { - return Ok(false); + return Ok(()); } let res = container.trigger_rekey().await; log::debug!(target: "citadel", "*** [FINISHED] Client {} rekey result: {res:?}", container.cid); @@ -831,10 +942,8 @@ mod tests { let (alice_result, bob_result) = tokio::join!(alice_handle, bob_handle); // Update original containers with final state - let rekey_0_res = alice_result.unwrap().unwrap(); - let rekey_1_res = bob_result.unwrap().unwrap(); - assert_eq!(rekey_0_res, !alice_skips); - assert_eq!(rekey_1_res, !bob_skips); + alice_result.unwrap().unwrap(); + bob_result.unwrap().unwrap(); post_checks(&container_0, &container_1); } @@ -859,21 +968,16 @@ mod tests { assert_eq!(test_message.to_vec(), decrypted); } - fn post_checks< - S: Sink + Unpin + Send + 'static, - I: Stream + Unpin + Send + 'static, - R: Ratchet, - >( + fn post_checks, I: RatchetManagerStream<()>, R: Ratchet>( container_0: &RatchetManager, container_1: &RatchetManager, - ) where - >::Error: std::fmt::Debug, - { + ) { // Verify final state let cid_0 = container_0.cid; let cid_1 = container_1.cid; - let alice_declared_latest_version = container_0.container.read().latest_usable_version; - let bob_declared_latest_version = container_1.container.read().latest_usable_version; + let alice_declared_latest_version = + container_0.session_crypto_state.latest_usable_version(); + let bob_declared_latest_version = container_1.session_crypto_state.latest_usable_version(); assert_eq!(alice_declared_latest_version, bob_declared_latest_version); let alice_ratchet = container_0.get_ratchet(None).unwrap(); let bob_ratchet = container_1.get_ratchet(None).unwrap(); @@ -882,9 +986,9 @@ mod tests { let alice_ratchet_version = alice_ratchet.version(); ratchet_encrypt_decrypt_test( - &*container_0.container.read(), + &container_0.session_crypto_state, cid_0, - &*container_1.container.read(), + &container_1.session_crypto_state, cid_1, alice_ratchet_version, ); @@ -902,11 +1006,11 @@ mod tests { } assert_eq!( - alice_manager.container.read().latest_usable_version, + alice_manager.session_crypto_state.latest_usable_version(), ROUNDS as u32 ); assert_eq!( - bob_manager.container.read().latest_usable_version, + bob_manager.session_crypto_state.latest_usable_version(), ROUNDS as u32 ); } @@ -939,11 +1043,11 @@ mod tests { } assert_eq!( - alice_manager.container.read().latest_usable_version, + alice_manager.session_crypto_state.latest_usable_version(), ROUNDS as u32 ); assert_eq!( - bob_manager.container.read().latest_usable_version, + bob_manager.session_crypto_state.latest_usable_version(), ROUNDS as u32 ); } diff --git a/citadel_crypt/src/ratchets/stacked/ratchet.rs b/citadel_crypt/src/ratchets/stacked/ratchet.rs index 79009ac90..482f7e3ca 100644 --- a/citadel_crypt/src/ratchets/stacked/ratchet.rs +++ b/citadel_crypt/src/ratchets/stacked/ratchet.rs @@ -60,8 +60,8 @@ //! - [`crate::endpoint_crypto_container`] - Endpoint state management //! -use crate::entropy_bank::EntropyBank; use crate::misc::CryptError; +use crate::ratchets::entropy_bank::EntropyBank; use crate::ratchets::stacked::ratchet::constructor::StackedRatchetConstructor; use crate::ratchets::Ratchet; use citadel_pqcrypto::constructor_opts::{ConstructorOpts, RecursiveChain}; @@ -175,8 +175,8 @@ pub mod constructor { use crate::endpoint_crypto_container::{ AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, }; - use crate::entropy_bank::EntropyBank; use crate::prelude::CryptError; + use crate::ratchets::entropy_bank::EntropyBank; use crate::ratchets::stacked::ratchet::StackedRatchet; use arrayvec::ArrayVec; use citadel_pqcrypto::constructor_opts::{ConstructorOpts, ImpliedSecurityLevel}; diff --git a/citadel_crypt/src/scramble/crypt_splitter.rs b/citadel_crypt/src/scramble/crypt_splitter.rs index d53293dd2..f44e78e88 100644 --- a/citadel_crypt/src/scramble/crypt_splitter.rs +++ b/citadel_crypt/src/scramble/crypt_splitter.rs @@ -19,7 +19,7 @@ //! - Supports both encrypted and unencrypted packet transmission //! //! ## Related Components -//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Provides cryptographic entropy +//! - [`EntropyBank`](crate::ratchets::entropy_bank::EntropyBank): Provides cryptographic entropy //! - [`PacketVector`](crate::packet_vector::PacketVector): Handles packet orientation //! - [`Ratchet`](crate::ratchets::Ratchet): Manages encryption keys //! - [`PostQuantumContainer`](citadel_pqcrypto::PostQuantumContainer): Post-quantum cryptography @@ -35,9 +35,9 @@ use num_integer::Integer; use rand::prelude::{SliceRandom, ThreadRng}; use rand::Rng; -use crate::entropy_bank::EntropyBank; use crate::packet_vector::{generate_packet_vector, PacketVector}; use crate::prelude::CryptError; +use crate::ratchets::entropy_bank::EntropyBank; use crate::ratchets::Ratchet; pub use citadel_types::prelude::ObjectId; #[cfg(not(target_family = "wasm"))] diff --git a/citadel_crypt/src/scramble/mod.rs b/citadel_crypt/src/scramble/mod.rs index b89a95a51..42d9f7d66 100644 --- a/citadel_crypt/src/scramble/mod.rs +++ b/citadel_crypt/src/scramble/mod.rs @@ -1,2 +1,4 @@ /// For encrypting data and scrambling it pub mod crypt_splitter; +/// Allows thread-pooled asynchronous and parallel file processing +pub mod streaming_crypt_scrambler; diff --git a/citadel_crypt/src/streaming_crypt_scrambler.rs b/citadel_crypt/src/scramble/streaming_crypt_scrambler.rs similarity index 99% rename from citadel_crypt/src/streaming_crypt_scrambler.rs rename to citadel_crypt/src/scramble/streaming_crypt_scrambler.rs index a9c1816f6..ef583d36f 100644 --- a/citadel_crypt/src/streaming_crypt_scrambler.rs +++ b/citadel_crypt/src/scramble/streaming_crypt_scrambler.rs @@ -20,7 +20,7 @@ //! //! ## Related Components //! - [`crypt_splitter`](crate::scramble::crypt_splitter): Core encryption and packet splitting -//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Cryptographic entropy source +//! - [`EntropyBank`](crate::ratchets::entropy_bank::EntropyBank): Cryptographic entropy source //! - [`PacketVector`](crate::packet_vector::PacketVector): Packet orientation management //! - [`StackedRatchet`](crate::ratchets::stacked::ratchet::StackedRatchet): Key management @@ -33,8 +33,8 @@ use std::pin::Pin; use tokio::sync::mpsc::Sender as GroupChanneler; use tokio::sync::oneshot::Receiver; -use crate::entropy_bank::EntropyBank; use crate::packet_vector::PacketVector; +use crate::ratchets::entropy_bank::EntropyBank; use crate::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupSenderDevice}; use crate::misc::CryptError; diff --git a/citadel_crypt/src/secure_buffer/mod.rs b/citadel_crypt/src/secure_buffer/mod.rs index e336b7d8f..cae135d0d 100644 --- a/citadel_crypt/src/secure_buffer/mod.rs +++ b/citadel_crypt/src/secure_buffer/mod.rs @@ -26,7 +26,7 @@ //! //! # Related Components //! -//! - [`crate::streaming_crypt_scrambler`] - Uses secure buffers for streaming +//! - [`crate::scramble::streaming_crypt_scrambler`] - Uses secure buffers for streaming //! - [`crate::packet_vector`] - Packet handling utilities //! diff --git a/citadel_crypt/src/secure_buffer/sec_packet.rs b/citadel_crypt/src/secure_buffer/sec_packet.rs index 2a2c255e5..720498078 100644 --- a/citadel_crypt/src/secure_buffer/sec_packet.rs +++ b/citadel_crypt/src/secure_buffer/sec_packet.rs @@ -52,7 +52,7 @@ //! //! - [`PartitionedSecBuffer`] - Underlying buffer implementation //! - [`crate::packet_vector`] - Packet vector operations -//! - [`crate::streaming_crypt_scrambler`] - Streaming encryption +//! - [`crate::scramble::streaming_crypt_scrambler`] - Streaming encryption //! use crate::secure_buffer::partitioned_sec_buffer::{PartitionedSecBuffer, SliceHandle}; diff --git a/citadel_crypt/src/sync_toggle.rs b/citadel_crypt/src/sync_toggle.rs index 6efdaf4cb..edf34b09c 100644 --- a/citadel_crypt/src/sync_toggle.rs +++ b/citadel_crypt/src/sync_toggle.rs @@ -48,7 +48,7 @@ //! //! # Related Components //! -//! - [`crate::entropy_bank`] - Uses toggle for state transitions +//! - [`crate::ratchets::entropy_bank`] - Uses toggle for state transitions //! - [`crate::ratchets::stacked::ratchet`] - Ratchet state management //! diff --git a/citadel_crypt/src/toolset.rs b/citadel_crypt/src/toolset.rs index 5607d268a..c1903735b 100644 --- a/citadel_crypt/src/toolset.rs +++ b/citadel_crypt/src/toolset.rs @@ -20,7 +20,7 @@ //! //! ## Related Components //! - [`StackedRatchet`](crate::ratchets::stacked::ratchet::StackedRatchet): Core ratchet implementation -//! - [`EntropyBank`](crate::entropy_bank::EntropyBank): Entropy source for ratchets +//! - [`EntropyBank`](crate::ratchets::entropy_bank::EntropyBank): Entropy source for ratchets //! - [`CryptError`](crate::misc::CryptError): Error handling for cryptographic operations //! - [`ClientNetworkAccount`]: High-level account management @@ -39,7 +39,7 @@ use std::ops::RangeInclusive; #[cfg(debug_assertions)] pub const MAX_RATCHETS_IN_MEMORY: usize = 6; #[cfg(not(debug_assertions))] -pub const MAX_RATCHETS_IN_MEMORY: usize = 128; +pub const MAX_RATCHETS_IN_MEMORY: usize = 32; /// The reserved version for the static aux ratchet pub const STATIC_AUX_VERSION: u32 = 0; diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index b1aeaee22..e7430e343 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -7,9 +7,9 @@ mod tests { }; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; #[cfg(not(target_family = "wasm"))] - use citadel_crypt::entropy_bank::EntropyBank; - #[cfg(not(target_family = "wasm"))] use citadel_crypt::packet_vector::PacketVector; + #[cfg(not(target_family = "wasm"))] + use citadel_crypt::ratchets::entropy_bank::EntropyBank; use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::{par_scramble_encrypt_group, GroupReceiver}; @@ -907,7 +907,7 @@ mod tests { use std::time::Instant; use tokio::sync::mpsc::channel; - use citadel_crypt::streaming_crypt_scrambler::scramble_encrypt_source; + use citadel_crypt::scramble::streaming_crypt_scrambler::scramble_encrypt_source; let (alice, bob) = gen::( 0, diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index fbdeaba5e..2c8786501 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -551,7 +551,7 @@ pub mod prelude { pub use crate::auth::AuthenticationRequest; #[doc(hidden)] pub use crate::proto::misc::{read_one_packet_as_framed, write_one_packet}; - pub use citadel_crypt::streaming_crypt_scrambler::ObjectSource; + pub use citadel_crypt::scramble::streaming_crypt_scrambler::ObjectSource; pub use citadel_types::crypto::EncryptionAlgorithm; pub use citadel_types::crypto::KemAlgorithm; pub use citadel_types::crypto::SecurityLevel; diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs index e5da2d2b6..1d8937361 100644 --- a/citadel_proto/src/proto/node_request.rs +++ b/citadel_proto/src/proto/node_request.rs @@ -27,7 +27,7 @@ use crate::auth::AuthenticationRequest; use crate::prelude::{GroupBroadcast, PeerSignal, VirtualTargetType}; use crate::proto::state_container::VirtualConnectionType; -use citadel_crypt::streaming_crypt_scrambler::ObjectSource; +use citadel_crypt::scramble::streaming_crypt_scrambler::ObjectSource; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::TransferType; use citadel_types::proto::{ConnectMode, SessionSecuritySettings, UdpMode}; diff --git a/citadel_proto/src/proto/packet_processor/register_packet.rs b/citadel_proto/src/proto/packet_processor/register_packet.rs index a886ffe13..8fb5f7fdc 100644 --- a/citadel_proto/src/proto/packet_processor/register_packet.rs +++ b/citadel_proto/src/proto/packet_processor/register_packet.rs @@ -31,10 +31,8 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{RegisterFailure, RegisterOkay}; -use citadel_crypt::endpoint_crypto_container::{ - AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, -}; -use citadel_crypt::prelude::ConstructorOpts; +use citadel_crypt::endpoint_crypto_container::{AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, PeerSessionCrypto}; +use citadel_crypt::prelude::{ConstructorOpts, Toolset}; use citadel_crypt::ratchets::Ratchet; use citadel_user::serialization::SyncIO; @@ -252,14 +250,14 @@ pub async fn process_register( let timestamp = session.time_tracker.get_global_time_ns(); let account_manager = session.account_manager.clone(); std::mem::drop(state_container); - + let session_crypto_state = initialize_peer_session_crypto(stacked_ratchet.get_cid(), stacked_ratchet.clone(), true); // we must now create the CNAC async move { match account_manager .register_impersonal_hyperlan_client_network_account( conn_info, creds, - stacked_ratchet.clone(), + session_crypto_state, ) .await { @@ -355,10 +353,13 @@ pub async fn process_register( let account_manager = session.account_manager.clone(); let kernel_tx = session.kernel_tx.clone(); + let session_crypto_state = initialize_peer_session_crypto(stacked_ratchet.get_cid(), stacked_ratchet, false); + + async move { match account_manager .register_personal_hyperlan_server( - stacked_ratchet, + session_crypto_state, credentials, conn_info, ) @@ -432,7 +433,6 @@ pub async fn process_register( error_message: String::from_utf8(error_message) .unwrap_or_else(|_| "Non-UTF8 error message".to_string()), }))?; - //session.needs_close_message.set(false); session.shutdown(); } else { log::error!(target: "citadel", "Error validating FAILURE packet"); @@ -457,3 +457,8 @@ pub async fn process_register( to_concurrent_processor!(task) } + +// Only for registration; does not start the messenger/ratchet manager +fn initialize_peer_session_crypto(cid: u64, initial_ratchet: R, is_server: bool) -> PeerSessionCrypto { + PeerSessionCrypto::new(Toolset::new(cid, initial_ratchet), !is_server) +} \ No newline at end of file diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index dd2bcd84a..6c2e7d3f3 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -103,7 +103,7 @@ use crate::proto::transfer_stats::TransferStats; use bytemuck::NoUninit; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; use citadel_crypt::prelude::{ConstructorOpts, FixedSizedSource}; -use citadel_crypt::streaming_crypt_scrambler::{scramble_encrypt_source, ObjectSource}; +use citadel_crypt::scramble::streaming_crypt_scrambler::{scramble_encrypt_source, ObjectSource}; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ConnectMode; use citadel_types::proto::SessionSecuritySettings; @@ -1159,7 +1159,7 @@ impl CitadelSession { let security_level = state_container.session_security_settings.as_ref().map(|r| r.security_level).unwrap(); let p2p_sessions = state_container.active_virtual_connections.iter().filter_map(|vconn| { - if vconn.1.endpoint_container.as_ref()?.endpoint_crypto.local_is_initiator && vconn.1.is_active.load(Ordering::SeqCst) && vconn.1.last_delivered_message_timestamp.get().map(|r| r.elapsed() > Duration::from_millis(15000)).unwrap_or(true) { + if vconn.1.endpoint_container.as_ref()?.endpoint_crypto.local_is_initiator() && vconn.1.is_active.load(Ordering::SeqCst) && vconn.1.last_delivered_message_timestamp.get().map(|r| r.elapsed() > Duration::from_millis(15000)).unwrap_or(true) { Some(vconn.1.connection_type) } else { None @@ -1465,7 +1465,8 @@ impl CitadelSession { let group_id_start = crypt_container.get_and_increment_group_id(); let latest_hr = crypt_container.get_ratchet(None).cloned().unwrap(); let static_aux_ratchet = crypt_container - .toolset + .toolset() + .read() .get_static_auxiliary_ratchet() .clone(); @@ -1508,7 +1509,7 @@ impl CitadelSession { // if 1 group, we don't need to reserve any more group IDs. If 2, then we reserve just one. 3, then 2 let amt_to_reserve = groups_needed.saturating_sub(1); - crypt_container.rolling_group_id += amt_to_reserve as u64; + crypt_container.incrementing_group_id += amt_to_reserve as u64; let file_header = packet_crafter::file::craft_file_header_packet( &latest_hr, group_id_start, @@ -1543,7 +1544,8 @@ impl CitadelSession { .unwrap() .peer_session_crypto; let static_aux_ratchet = crypt_container_c2s - .toolset + .toolset() + .read() .get_static_auxiliary_ratchet() .clone(); @@ -1621,7 +1623,7 @@ impl CitadelSession { // if 1 group, we don't need to reserve any more group IDs. If 2, then we reserve just one. 3, then 2 let amt_to_reserve = groups_needed.saturating_sub(1); - endpoint_container.endpoint_crypto.rolling_group_id += amt_to_reserve as u64; + endpoint_container.endpoint_crypto.incrementing_group_id += amt_to_reserve as u64; ( preferred_primary_stream, @@ -1900,7 +1902,6 @@ impl CitadelSession { let mut state_container = inner_mut_state!(this.state_container); match request { - // (ticket, message, v_target, security_level) SessionRequest::SendMessage { ticket, packet, @@ -2063,7 +2064,7 @@ impl CitadelSession { .get_ratchet(None) .unwrap(); let packet = super::packet_crafter::peer_cmd::craft_peer_signal( - stacked_ratchet, + &stacked_ratchet, signal_processed, ticket, timestamp, diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index fc3653463..c2ccc034d 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -75,7 +75,7 @@ use crate::proto::session::{ CitadelSession, ClientOnlySessionInitSettings, HdpSessionInitMode, SessionInitParams, }; use crate::proto::state_container::{VirtualConnectionType, VirtualTargetType}; -use citadel_crypt::streaming_crypt_scrambler::ObjectSource; +use citadel_crypt::scramble::streaming_crypt_scrambler::ObjectSource; use citadel_io::tokio::sync::broadcast::Sender; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ConnectMode; diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index e0c23104e..62bc16715 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -857,8 +857,7 @@ impl StateContainerInner { self.updates_in_progress .insert(target_cid, endpoint_crypto.update_in_progress.clone()); - - //let (tx, rx) = futures::channel::mpsc::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); + let peer_channel = PeerChannel::new( self.node_remote.clone(), target_cid, @@ -927,7 +926,7 @@ impl StateContainerInner { is_active, to_primary_stream: session.to_primary_stream.clone().unwrap(), channel_signal: None, - peer_session_crypto: cnac.read().crypt_container.new_session(), + peer_session_crypto: cnac.get_session_crypto().new_session(), }; let updates_in_progress = c2s.peer_session_crypto.update_in_progress.clone(); diff --git a/citadel_proto/src/proto/validation.rs b/citadel_proto/src/proto/validation.rs index d55fe0e59..83bb68398 100644 --- a/citadel_proto/src/proto/validation.rs +++ b/citadel_proto/src/proto/validation.rs @@ -363,7 +363,7 @@ pub(crate) mod pre_connect { new_stacked_ratchet.clone(), )); - cnac.replace_toolset(toolset); + cnac.on_session_init(toolset); Ok(( static_auxiliary_ratchet, transfer, @@ -401,7 +401,7 @@ pub(crate) mod pre_connect { let new_stacked_ratchet = alice_constructor.finish()?; let _ = new_stacked_ratchet.verify_level(lvl.into()).ok()?; let toolset = Toolset::from((static_auxiliary_ratchet, new_stacked_ratchet.clone())); - cnac.replace_toolset(toolset); + cnac.on_session_init(toolset); Some((new_stacked_ratchet, packet.nat_type)) } diff --git a/citadel_user/src/account_loader.rs b/citadel_user/src/account_loader.rs index 03f2f6233..4098a529e 100644 --- a/citadel_user/src/account_loader.rs +++ b/citadel_user/src/account_loader.rs @@ -48,7 +48,6 @@ //! * [`AccountError`] - Error handling for account operations //! * [`Ratchet`] - Cryptographic operations for account security -use crate::client_account::ClientNetworkAccountInner; use crate::directory_store::*; use crate::hypernode_account::CNAC_SERIALIZED_EXTENSION; use crate::misc::AccountError; @@ -64,11 +63,11 @@ pub fn load_cnac_files( let hyxe_nac_dir_impersonal = ds.nac_dir_impersonal.as_str(); let hyxe_nac_dir_personal = ds.nac_dir_personal.as_str(); - let cnacs_impersonal = load_file_types_by_ext::, _>( + let cnacs_impersonal = load_file_types_by_ext::, _>( CNAC_SERIALIZED_EXTENSION, hyxe_nac_dir_impersonal, )?; - let cnacs_personal = load_file_types_by_ext::, _>( + let cnacs_personal = load_file_types_by_ext::, _>( CNAC_SERIALIZED_EXTENSION, hyxe_nac_dir_personal, )?; @@ -77,9 +76,9 @@ pub fn load_cnac_files( Ok(cnacs_impersonal .into_iter() .chain(cnacs_personal) - .map(|r| { - let cid = r.0.cid; - (cid, r.0.into()) + .map(|(cnac, _path)| { + let cid = cnac.get_cid(); + (cid, cnac) }) .collect()) } diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs index 5c17fb60a..64ebc9da1 100644 --- a/citadel_user/src/account_manager.rs +++ b/citadel_user/src/account_manager.rs @@ -98,6 +98,7 @@ use crate::misc::{AccountError, CNACMetadata}; use crate::prelude::ConnectionInfo; use crate::server_misc_settings::ServerMiscSettings; use citadel_crypt::argon::argon_container::{ArgonDefaultServerSettings, ArgonSettings}; +use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; use citadel_crypt::ratchets::mono::MonoRatchet; use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_crypt::ratchets::Ratchet; @@ -202,7 +203,7 @@ impl AccountManager { &self, conn_info: ConnectionInfo, creds: ProposedCredentials, - init_stacked_ratchet: R, + session_crypto_state: PeerSessionCrypto, ) -> Result, AccountError> { let reserved_cid = self .persistence_handler @@ -235,7 +236,7 @@ impl AccountManager { false, conn_info, auth_store, - init_stacked_ratchet, + session_crypto_state, ) .await?; log::trace!(target: "citadel", "Created impersonal CNAC ..."); @@ -248,7 +249,7 @@ impl AccountManager { /// HyperLAN Client (Alice) runs this function below pub async fn register_personal_hyperlan_server( &self, - stacked_ratchet: R, + session_crypto_state: PeerSessionCrypto, creds: ProposedCredentials, conn_info: ConnectionInfo, ) -> Result, AccountError> { @@ -258,7 +259,7 @@ impl AccountManager { let client_auth_store = creds.into_auth_store(); let cnac = ClientNetworkAccount::::new_from_network_personal( valid_cid, - stacked_ratchet, + session_crypto_state, client_auth_store, conn_info, ) diff --git a/citadel_user/src/auth/mod.rs b/citadel_user/src/auth/mod.rs index b1ff2ac55..41ae3f754 100644 --- a/citadel_user/src/auth/mod.rs +++ b/citadel_user/src/auth/mod.rs @@ -46,7 +46,7 @@ pub enum DeclaredAuthenticationMode { full_name: String, argon: ArgonContainerType, }, - Passwordless { + Transient { username: String, full_name: String, }, @@ -56,28 +56,28 @@ impl DeclaredAuthenticationMode { pub fn username(&self) -> &str { match self { Self::Argon { username, .. } => username.as_str(), - Self::Passwordless { username, .. } => username.as_str(), + Self::Transient { username, .. } => username.as_str(), } } pub fn full_name(&self) -> &str { match self { Self::Argon { full_name, .. } => full_name.as_str(), - Self::Passwordless { full_name, .. } => full_name.as_str(), + Self::Transient { full_name, .. } => full_name.as_str(), } } pub fn argon_container(&self) -> Option<&ArgonContainerType> { match self { Self::Argon { argon, .. } => Some(argon), - Self::Passwordless { .. } => None, + Self::Transient { .. } => None, } } - pub fn is_passwordless(&self) -> bool { + pub fn is_transient(&self) -> bool { match self { Self::Argon { .. } => false, - Self::Passwordless { .. } => true, + Self::Transient { .. } => true, } } } diff --git a/citadel_user/src/auth/proposed_credentials.rs b/citadel_user/src/auth/proposed_credentials.rs index f799e11dd..485d64c40 100644 --- a/citadel_user/src/auth/proposed_credentials.rs +++ b/citadel_user/src/auth/proposed_credentials.rs @@ -195,7 +195,7 @@ impl ProposedCredentials { pub(crate) fn into_auth_store(self) -> DeclaredAuthenticationMode { match self { - Self::Disabled { username } => DeclaredAuthenticationMode::Passwordless { + Self::Disabled { username } => DeclaredAuthenticationMode::Transient { username, full_name: "authless.client".to_string(), }, diff --git a/citadel_user/src/backend/filesystem_backend.rs b/citadel_user/src/backend/filesystem_backend.rs index 3550bdf3f..5d4245173 100644 --- a/citadel_user/src/backend/filesystem_backend.rs +++ b/citadel_user/src/backend/filesystem_backend.rs @@ -38,7 +38,7 @@ use crate::serialization::SyncIO; use async_trait::async_trait; use citadel_crypt::ratchets::Ratchet; use citadel_crypt::scramble::crypt_splitter::MAX_BYTES_PER_GROUP; -use citadel_crypt::streaming_crypt_scrambler::ObjectSource; +use citadel_crypt::scramble::streaming_crypt_scrambler::ObjectSource; use citadel_io::tokio; use citadel_io::tokio_stream::StreamExt; use citadel_types::proto::{ObjectTransferStatus, TransferType, VirtualObjectMetadata}; diff --git a/citadel_user/src/backend/mod.rs b/citadel_user/src/backend/mod.rs index 0945b2e8b..8e82802f5 100644 --- a/citadel_user/src/backend/mod.rs +++ b/citadel_user/src/backend/mod.rs @@ -54,7 +54,7 @@ use crate::backend::redis_backend::RedisConnectionOptions; use crate::backend::sql_backend::SqlConnectionOptions; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; -use citadel_crypt::streaming_crypt_scrambler::ObjectSource; +use citadel_crypt::scramble::streaming_crypt_scrambler::ObjectSource; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user; diff --git a/citadel_user/src/backend/redis_backend.rs b/citadel_user/src/backend/redis_backend.rs index a0b376cca..27a130b8c 100644 --- a/citadel_user/src/backend/redis_backend.rs +++ b/citadel_user/src/backend/redis_backend.rs @@ -34,12 +34,13 @@ use crate::backend::memory::no_backend_streaming; use crate::backend::BackendConnection; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; -use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX}; +use crate::prelude::{AccountState, HYPERLAN_IDX}; use crate::serialization::SyncIO; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; +use futures::TryFutureExt; use mobc::async_trait; use mobc::Manager; use mobc::Pool; @@ -757,6 +758,7 @@ impl RedisBackend { .get_conn() .await? .hget::<_, _, Option>>(get_cid_to_cnac_key(), cid) + .map_err(|err| AccountError::msg(err.to_string())) .await? { self.cnac_bytes_to_cnac(value).map(Some) @@ -769,9 +771,7 @@ impl RedisBackend { &self, bytes: Vec, ) -> Result, AccountError> { - let deserialized = - ClientNetworkAccountInner::::deserialize_from_vector(bytes.as_ref())?; - Ok(deserialized.into()) + ClientNetworkAccount::::deserialize_from_vector(bytes.as_ref()) } async fn get_conn(&self) -> Result { diff --git a/citadel_user/src/backend/sql_backend.rs b/citadel_user/src/backend/sql_backend.rs index 9bd2be9bc..9e0b623b8 100644 --- a/citadel_user/src/backend/sql_backend.rs +++ b/citadel_user/src/backend/sql_backend.rs @@ -34,11 +34,11 @@ use crate::backend::memory::no_backend_streaming; use crate::backend::{BackendConnection, BackendType}; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; -use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX}; +use crate::prelude::{AccountState, HYPERLAN_IDX}; use crate::serialization::SyncIO; use async_trait::async_trait; -use citadel_crypt::ratchets::mono::ratchet::MonoRatchet; -use citadel_crypt::ratchets::stacked::ratchet::StackedRatchet; +use citadel_crypt::ratchets::mono::MonoRatchet; +use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; @@ -111,6 +111,12 @@ pub struct SqlConnectionOptions { pub car_mode: Option, } +impl From for AccountError { + fn from(e: sqlx::Error) -> Self { + AccountError::IoError(e.to_string()) + } +} + impl From<&'_ SqlConnectionOptions> for AnyPoolOptions { fn from(this: &'_ SqlConnectionOptions) -> AnyPoolOptions { let mut ret = AnyPoolOptions::default(); @@ -238,7 +244,7 @@ impl BackendConnection for SqlBackend async fn save_cnac(&self, cnac: &ClientNetworkAccount) -> Result<(), AccountError> { let conn = &(self.get_conn().await?); // The issue: at endpoints, mutuals are being saved inside CNAC, but not the database. We see here that mutuals are not synced to database - let serded = cnac.generate_proper_bytes()?; + let bytes = cnac.generate_proper_bytes()?; let metadata = cnac.get_metadata(); @@ -269,7 +275,7 @@ impl BackendConnection for SqlBackend args.add(metadata.username); args.add(metadata.full_name); args.add(metadata.creation_date); - args.add(serded); + args.add(bytes); let _query = sqlx::query_with(query.as_str(), args) .execute(conn) @@ -874,8 +880,7 @@ impl SqlBackend { ) -> Result>, AccountError> { if let Some(row) = query { let bin = row.try_get::, _>("bin")?; - let cnac_inner = - ClientNetworkAccountInner::::deserialize_from_owned_vector(bin)?; + let cnac_inner = ClientNetworkAccount::::deserialize_from_owned_vector(bin)?; Ok(Some(cnac_inner.into())) } else { Ok(None) @@ -992,7 +997,7 @@ pub fn try_get_blob_as_utf8(key: &str, row: &AnyRow) -> Result { let blob = row.try_get::, _>(key)?; - let blob = String::from_utf8(blob)?; + let blob = String::from_utf8(blob).map_err(|err| AccountError::msg(err.to_string()))?; Ok(blob) } res => Err(AccountError::Generic(format!( diff --git a/citadel_user/src/client_account.rs b/citadel_user/src/client_account.rs index 93e88c598..e84776061 100644 --- a/citadel_user/src/client_account.rs +++ b/citadel_user/src/client_account.rs @@ -110,29 +110,18 @@ use citadel_types::user::MutualPeer; use std::collections::HashMap; use std::marker::PhantomData; -/// The password file needs to have a hard-to-guess password enclosing in the case it is accidentally exposed over the network -pub const HYXEFILE_PASSWORD_LENGTH: usize = 222; -/// The maximum size a password can be. This upper limit was made inconsideration of the idea that passwords can bloat to the size of MAX_PACKET_SIZE, and force a split of data -/// which we want to prevent -pub const MAX_PASSWORD_SIZE: usize = 33; -/// The minimum size was selected quasi-randomly -pub const MIN_PASSWORD_SIZE: usize = 7; /// The default index for denoting a HyperLAN connection (relative to THIS cnac) pub const HYPERLAN_IDX: u64 = 0; ///use futures::{TryFutureExt, TryStreamExt}; #[derive(Serialize, Deserialize)] /// Inner device -pub struct ClientNetworkAccountInner { - /// The client identification number - pub cid: u64, - /// While this NAC should be session-oriented, it may be replaced if [PINNED_IP_MODE] is disabled, meaning, a new IP - /// address can enact as the CNAC, otherwise the IP address must stay constant - pub adjacent_nac: ConnectionInfo, - /// If this CNAC is for a personal connection, this is true - pub is_local_personal: bool, - /// The creation date - pub creation_date: String, +pub struct AccountState { + /// RTDB config for client-side communications + #[cfg(feature = "google-services")] + pub client_rtdb_config: Option, + #[cfg(not(feature = "google-services"))] + pub client_rtdb_config: Option<()>, /// For impersonal mode: /// input: iCID (the central server of the CID), output: MutualPeer(iCID, CID) where iCID == iCID. /// It maps the iCID to the CID. The iCID is zero if the peer client is within the HyperLAN (impersonal mode ONLY). Each CNAC @@ -144,35 +133,36 @@ pub struct ClientNetworkAccountInner, - /// Toolset which contains all the entropy_banks - #[serde(bound = "")] - pub crypt_container: PeerSessionCrypto, - /// RTDB config for client-side communications - #[cfg(feature = "google-services")] - pub client_rtdb_config: Option, - #[cfg(not(feature = "google-services"))] - pub client_rtdb_config: Option<()>, - /// For storing critical ID information for this CNAC - pub auth_store: DeclaredAuthenticationMode, /// peer id -> key -> sub_key -> bytes pub byte_map: HashMap>>>, - _pd: PhantomData, } /// A thread-safe handle for sharing data across threads and applications /// /// SAFETY: The `cid`, `adjacent_nid`, and `is_personal` is private. These values /// should NEVER be edited within this source file +#[derive(Serialize, Deserialize)] pub struct ClientNetworkAccount { /// The inner thread-safe device - inner: Arc>, + #[serde(bound = "")] + inner: Arc>, } -struct MetaInner { +#[derive(Serialize, Deserialize)] +struct ClientNetworkAccountInner { + /// The client identification number cid: u64, is_personal: bool, - passwordless: bool, - inner: RwLock>, + is_transient: bool, + pub creation_date: String, + pub adjacent_nac: ConnectionInfo, + #[serde(bound = "")] + pub crypto_session_state: PeerSessionCrypto, + /// For storing critical ID information for this CNAC + pub auth_store: DeclaredAuthenticationMode, + peer_state: RwLock, + // For future use cases + _phantom: PhantomData, } impl ClientNetworkAccount { @@ -183,45 +173,47 @@ impl ClientNetworkAccount { is_personal: bool, adjacent_nac: ConnectionInfo, auth_store: DeclaredAuthenticationMode, - base_stacked_ratchet: R, + session_crypto_state: PeerSessionCrypto, ) -> Result { log::trace!(target: "citadel", "Creating CNAC w/valid cid: {:?}", valid_cid); let creation_date = get_present_formatted_timestamp(); - let crypt_container = PeerSessionCrypto::::new( - Toolset::::new(valid_cid, base_stacked_ratchet), - is_personal, - ); - let mutuals = MultiMap::new(); - let byte_map = HashMap::default(); - let client_rtdb_config = None; - let inner = ClientNetworkAccountInner:: { - client_rtdb_config, - creation_date, - cid: valid_cid, - auth_store, - adjacent_nac, - is_local_personal: is_personal, - mutuals, - crypt_container, - byte_map, - _pd: Default::default(), + + let peer_state = AccountState { + mutuals: MultiMap::new(), + byte_map: HashMap::default(), + client_rtdb_config: None, }; - let this = Self::from(inner); - Ok(this) + + let is_transient = auth_store.is_transient(); + + Ok(Self { + inner: Arc::new(ClientNetworkAccountInner { + creation_date, + cid: valid_cid, + auth_store, + adjacent_nac, + is_personal, + is_transient, + crypto_session_state: session_crypto_state, + peer_state: RwLock::new(peer_state), + _phantom: PhantomData, + }), + }) + } + + pub fn get_session_crypto(&self) -> &PeerSessionCrypto { + &self.inner.crypto_session_state } /// Resets the toolset, if necessary. If the CNAC was freshly serialized, the hyper ratchet /// is not updated. In either case, returns the static aux hyper ratchet #[allow(unused_results)] pub fn refresh_static_ratchet(&self) -> R { - let mut write = self.write(); - write.crypt_container.toolset.verify_init_state(); - write.crypt_container.refresh_state(); - write - .crypt_container - .toolset - .get_static_auxiliary_ratchet() - .clone() + self.get_session_crypto().refresh_state(); + // Use write to enforce one accessor + let write = self.get_session_crypto().toolset().write(); + write.verify_init_state(); + write.get_static_auxiliary_ratchet().clone() } /// Stores the rtdb config @@ -229,16 +221,20 @@ impl ClientNetworkAccount { pub fn store_rtdb_config(&self, cfg: crate::external_services::rtdb::RtdbClientConfig) { self.write().client_rtdb_config = Some(cfg); } + + pub fn auth_store(&self) -> &DeclaredAuthenticationMode { + &self.inner.auth_store + } /// Returns true if the NAC is a personal type pub fn is_personal(&self) -> bool { self.inner.is_personal } - /// Towards the end of the registration phase, the [`ClientNetworkAccountInner`] gets transmitted to Alice. + /// Towards the end of the registration phase, the [`AccountState`] gets transmitted to Alice. pub async fn new_from_network_personal( valid_cid: u64, - stacked_ratchet: R, + session_crypto_state: PeerSessionCrypto, auth_store: DeclaredAuthenticationMode, conn_info: ConnectionInfo, ) -> Result { @@ -249,14 +245,14 @@ impl ClientNetworkAccount { IS_PERSONAL, conn_info, auth_store, - stacked_ratchet, + session_crypto_state, ) .await } /// Returns the username of this client pub fn get_username(&self) -> String { - self.read().auth_store.username().to_string() + self.inner.auth_store.username().to_string() } /// Checks the credentials for validity. Used for the login process. @@ -265,16 +261,15 @@ impl ClientNetworkAccount { creds: ProposedCredentials, ) -> Result<(), AccountError> { let argon_container = { - let read = self.read(); - let username = read.auth_store.username(); + let username = self.inner.auth_store.username(); if !creds.compare_username(username.as_bytes()) { return Err(AccountError::InvalidUsername); } - match &read.auth_store { + match &self.inner.auth_store { DeclaredAuthenticationMode::Argon { argon, .. } => argon.clone(), - DeclaredAuthenticationMode::Passwordless { .. } => return Ok(()), + DeclaredAuthenticationMode::Transient { .. } => return Ok(()), } }; @@ -287,8 +282,7 @@ impl ClientNetworkAccount { password_raw: SecBuffer, ) -> Result { let (settings, full_name, username) = { - let read = self.read(); - match &read.auth_store { + match &self.inner.auth_store { DeclaredAuthenticationMode::Argon { argon, full_name, @@ -298,7 +292,7 @@ impl ClientNetworkAccount { full_name.clone(), username.clone(), ), - DeclaredAuthenticationMode::Passwordless { username, .. } => { + DeclaredAuthenticationMode::Transient { username, .. } => { return Ok(ProposedCredentials::transient(username.clone())) } } @@ -309,27 +303,27 @@ impl ClientNetworkAccount { /// Replaces the internal toolset. This should ONLY be called (if absolutely necessary) during the PRE_CONNECT stage /// if synchronization is required - pub fn replace_toolset(&self, toolset: Toolset) { - self.write().crypt_container.toolset = toolset; + pub fn on_session_init(&self, toolset: Toolset) { + *self.get_session_crypto().toolset().write() = toolset; } /// This should ONLY be used for recovery mode pub fn get_static_auxiliary_stacked_ratchet(&self) -> R { - let this = self.read(); - this.crypt_container - .toolset + self.get_session_crypto() + .toolset() + .read() .get_static_auxiliary_ratchet() .clone() } /// Allows shared interior access - pub fn read(&self) -> RwLockReadGuard> { - self.inner.inner.read() + pub fn read(&self) -> RwLockReadGuard { + self.inner.peer_state.read() } /// Allows exclusive interior access - pub fn write(&self) -> RwLockWriteGuard> { - self.inner.inner.write() + pub fn write(&self) -> RwLockWriteGuard { + self.inner.peer_state.write() } /* @@ -401,12 +395,12 @@ impl ClientNetworkAccount { let this_cid = self.inner.cid; let other_cid = other_orig.inner.cid; + let this_username = self.inner.auth_store.username().to_string(); + let other_username = other_orig.inner.auth_store.username().to_string(); + let mut this = self.write(); let mut other = other_orig.write(); - let this_username = this.auth_store.username().to_string(); - let other_username = other.auth_store.username().to_string(); - this.mutuals.insert( HYPERLAN_IDX, MutualPeer { @@ -468,7 +462,7 @@ impl ClientNetworkAccount { /// Returns true if the data was mutated pub(crate) fn synchronize_hyperlan_peer_list(&self, peers: Vec) { let mut this = self.write(); - let ClientNetworkAccountInner:: { mutuals, .. } = &mut *this; + let AccountState { mutuals, .. } = &mut *this; let _ = mutuals.remove(&HYPERLAN_IDX); mutuals.insert_many(HYPERLAN_IDX, peers); @@ -499,7 +493,7 @@ impl ClientNetworkAccount { let removed_peer = hyperlan_peers.remove(idx); return Some(removed_peer); } else { - log::warn!(target: "citadel", "Peer {} not found within cnac {}", cid, write.cid); + log::warn!(target: "citadel", "Peer {} not found within cnac {}", cid, self.inner.cid); } } @@ -513,23 +507,18 @@ impl ClientNetworkAccount { /// Generates the serialized bytes pub fn generate_proper_bytes(&self) -> Result, AccountError> where - ClientNetworkAccountInner: SyncIO, + Self: SyncIO, { - // get write lock to ensure no further writes - let ptr = self.write(); - // now that the nac is encrypted internally, we can serialize - let serialized = (&ptr as &ClientNetworkAccountInner).serialize_to_vector()?; - - Ok(serialized) + self.serialize_to_vector() } /// Returns the metadata for this CNAC pub(crate) fn get_metadata(&self) -> CNACMetadata { - let read = self.read(); + let read = &self.inner; let cid = read.cid; let username = read.auth_store.username().to_string(); let full_name = read.auth_store.full_name().to_string(); - let is_personal = read.is_local_personal; + let is_personal = read.is_personal; let creation_date = read.creation_date.clone(); CNACMetadata { cid, @@ -542,7 +531,7 @@ impl ClientNetworkAccount { /// Returns the information related to the network endpoints (e.g., socket addrs) pub fn get_connect_info(&self) -> ConnectionInfo { - self.inner.inner.read().adjacent_nac.clone() + self.inner.adjacent_nac.clone() } /// Returns the CID @@ -552,7 +541,7 @@ impl ClientNetworkAccount { /// Returns true if passwordless pub fn passwordless(&self) -> bool { - self.inner.passwordless + self.inner.is_transient } } @@ -564,46 +553,17 @@ impl std::fmt::Debug for ClientNetworkAccount impl std::fmt::Display for ClientNetworkAccount { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let inner = self.read(); writeln!( f, "{}\t\t{}\t\t{}\t\t{}", self.inner.cid, - inner.auth_store.username(), - inner.auth_store.full_name(), + self.inner.auth_store.username(), + self.inner.auth_store.full_name(), self.inner.is_personal ) } } -impl From> for MetaInner { - fn from(inner: ClientNetworkAccountInner) -> Self { - let authless = inner.auth_store.is_passwordless(); - Self { - cid: inner.cid, - is_personal: inner.is_local_personal, - passwordless: authless, - inner: RwLock::new(inner), - } - } -} - -impl From> for ClientNetworkAccount { - fn from(inner: MetaInner) -> Self { - Self { - inner: Arc::new(inner), - } - } -} - -impl From> - for ClientNetworkAccount -{ - fn from(inner: ClientNetworkAccountInner) -> Self { - ClientNetworkAccount::from(MetaInner::from(inner)) - } -} - impl Clone for ClientNetworkAccount { fn clone(&self) -> Self { Self { diff --git a/citadel_user/src/lib.rs b/citadel_user/src/lib.rs index ddbee1a7a..a47154872 100644 --- a/citadel_user/src/lib.rs +++ b/citadel_user/src/lib.rs @@ -89,7 +89,7 @@ pub mod prelude { pub use crate::client_account::*; pub use crate::connection_metadata::*; pub use crate::hypernode_account::*; - pub use citadel_crypt::streaming_crypt_scrambler::MAX_BYTES_PER_GROUP; + pub use citadel_crypt::scramble::streaming_crypt_scrambler::MAX_BYTES_PER_GROUP; } /// Serde and others diff --git a/citadel_user/tests/primary.rs b/citadel_user/tests/primary.rs index b9c53a86d..c7b80460b 100644 --- a/citadel_user/tests/primary.rs +++ b/citadel_user/tests/primary.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use citadel_crypt::prelude::ConstructorOpts; + use citadel_crypt::prelude::{ConstructorOpts, Toolset}; use citadel_crypt::ratchets::stacked::constructor::StackedRatchetConstructor; use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_types::crypto::KemAlgorithm; @@ -10,7 +10,7 @@ mod tests { use citadel_user::client_account::ClientNetworkAccount; use futures::Future; - use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; + use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, PeerSessionCrypto}; use citadel_io::tokio; use citadel_types::crypto::EncryptionAlgorithm; use citadel_types::crypto::SecBuffer; @@ -49,6 +49,9 @@ mod tests { .get_persistence_handler() .get_cid_by_username(username); let (client_hr, server_hr) = gen(cid, 0, None); + let server_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, server_hr), false); + let client_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, client_hr), true); + let server_vers = self .server_acc_mgr .register_impersonal_hyperlan_client_network_account( @@ -60,14 +63,14 @@ mod tests { ) .await .unwrap(), - server_hr, + server_session_crypto_state, ) .await .unwrap(); let client_vers = self .client_acc_mgr .register_personal_hyperlan_server( - client_hr, + client_session_crypto_state, ProposedCredentials::new_register( full_name, username, @@ -100,7 +103,9 @@ mod tests { .get_persistence_handler() .get_cid_by_username(username); let (client_hr, server_hr) = gen(cid, 0, None); - + let server_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, server_hr), false); + let client_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, client_hr), true); + let _server_vers = self .server_acc_mgr .register_impersonal_hyperlan_client_network_account( @@ -112,13 +117,13 @@ mod tests { ) .await .unwrap(), - server_hr, + server_session_crypto_state, ) .await .unwrap(); let client_vers = client_acc_mgr .register_personal_hyperlan_server( - client_hr, + client_session_crypto_state, ProposedCredentials::new_register( full_name, username, @@ -457,16 +462,13 @@ mod tests { }] ); - let lock_server = server.write(); - let lock_client = client.write(); - - assert!(!lock_server.is_local_personal); - assert!(lock_client.is_local_personal); - assert_eq!(lock_client.auth_store.username(), USERNAME); - assert_eq!(lock_server.auth_store.username(), USERNAME); - assert_eq!(lock_client.auth_store.full_name(), FULL_NAME); - assert_eq!(lock_server.auth_store.full_name(), FULL_NAME); - assert_eq!(lock_server.cid, lock_server.cid); + assert!(!server.is_personal()); + assert!(client.is_personal()); + assert_eq!(client.auth_store().username(), USERNAME); + assert_eq!(server.auth_store().username(), USERNAME); + assert_eq!(client.auth_store().full_name(), FULL_NAME); + assert_eq!(server.auth_store().full_name(), FULL_NAME); + assert_eq!(server.get_cid(), client.get_cid()); Ok(()) }) @@ -676,16 +678,13 @@ mod tests { assert_eq!(pers_se.get_cid_by_username(USERNAME), client.get_cid()); assert_eq!(pers_cl.get_cid_by_username(USERNAME), client.get_cid()); - let lock_server = se2.write(); - let lock_client = cl2.write(); - - assert!(!lock_server.is_local_personal); - assert!(lock_client.is_local_personal); - assert_eq!(lock_client.auth_store.username(), USERNAME); - assert_eq!(lock_server.auth_store.username(), USERNAME); - assert_eq!(lock_client.auth_store.full_name(), FULL_NAME); - assert_eq!(lock_server.auth_store.full_name(), FULL_NAME); - assert_eq!(lock_server.cid, lock_server.cid); + assert!(!se2.is_personal()); + assert!(cl2.is_personal()); + assert_eq!(cl2.auth_store().username(), USERNAME); + assert_eq!(se2.auth_store().username(), USERNAME); + assert_eq!(cl2.auth_store().full_name(), FULL_NAME); + assert_eq!(se2.auth_store().full_name(), FULL_NAME); + assert_eq!(se2.get_cid(), cl2.get_cid()); Ok(()) }) .await From 73401e1a2cbc748d3ccafa3d07ec689b46c4b931 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sat, 11 Jan 2025 15:46:40 -0500 Subject: [PATCH 11/12] feat: Header Obfuscation --- Makefile.toml | 2 +- README.md | 6 +- citadel_crypt/Cargo.toml | 1 + .../src/endpoint_crypto_container.rs | 41 +- citadel_crypt/src/lib.rs | 2 +- .../src/{messsaging.rs => messaging.rs} | 114 +- citadel_crypt/src/packet_vector.rs | 2 +- citadel_crypt/src/ratchets/mod.rs | 5 +- citadel_crypt/src/ratchets/mono/ratchet.rs | 9 + citadel_crypt/src/ratchets/ratchet_manager.rs | 93 +- citadel_crypt/src/ratchets/stacked/ratchet.rs | 12 +- citadel_crypt/src/scramble/crypt_splitter.rs | 15 +- .../src/scramble/streaming_crypt_scrambler.rs | 12 +- .../secure_buffer/partitioned_sec_buffer.rs | 5 +- citadel_crypt/src/secure_buffer/sec_packet.rs | 24 +- citadel_crypt/src/toolset.rs | 118 +- citadel_crypt/tests/primary.rs | 74 +- citadel_proto/Cargo.toml | 3 +- citadel_proto/src/constants.rs | 2 +- .../src/kernel/kernel_communicator.rs | 49 +- citadel_proto/src/kernel/kernel_executor.rs | 18 +- citadel_proto/src/kernel/kernel_trait.rs | 2 +- citadel_proto/src/kernel/mod.rs | 5 +- citadel_proto/src/lib.rs | 24 +- .../src/proto/endpoint_crypto_accessor.rs | 37 +- citadel_proto/src/proto/misc/net.rs | 21 +- .../src/proto/misc/ordered_channel.rs | 26 +- .../proto/misc/session_security_settings.rs | 28 +- citadel_proto/src/proto/node.rs | 103 +- citadel_proto/src/proto/node_request.rs | 6 +- citadel_proto/src/proto/node_result.rs | 24 +- citadel_proto/src/proto/packet.rs | 637 ++++++++-- citadel_proto/src/proto/packet_crafter.rs | 671 +++------- .../proto/packet_processor/connect_packet.rs | 27 +- .../packet_processor/deregister_packet.rs | 4 +- .../packet_processor/disconnect_packet.rs | 4 +- .../src/proto/packet_processor/file_packet.rs | 14 +- .../packet_processor/keep_alive_packet.rs | 3 +- .../src/proto/packet_processor/mod.rs | 6 +- .../packet_processor/peer/group_broadcast.rs | 32 +- .../packet_processor/peer/peer_cmd_packet.rs | 395 +++--- .../peer/server/post_connect.rs | 4 +- .../peer/server/post_register.rs | 8 +- .../packet_processor/preconnect_packet.rs | 88 +- .../packet_processor/primary_group_packet.rs | 565 ++------- .../packet_processor/raw_primary_packet.rs | 12 +- .../proto/packet_processor/register_packet.rs | 51 +- .../proto/packet_processor/rekey_packet.rs | 336 ----- citadel_proto/src/proto/peer/channel.rs | 195 ++- citadel_proto/src/proto/peer/group_channel.rs | 10 +- .../src/proto/peer/p2p_conn_handler.rs | 67 +- citadel_proto/src/proto/remote.rs | 17 +- citadel_proto/src/proto/session.rs | 453 ++++--- citadel_proto/src/proto/session_manager.rs | 95 +- citadel_proto/src/proto/state_container.rs | 1082 +++++------------ .../peer_kem_state_container.rs | 2 +- .../preconnect_state_container.rs | 12 +- .../register_state_container.rs | 4 +- .../state_subcontainers/rekey_container.rs | 48 +- citadel_proto/src/proto/validation.rs | 121 +- citadel_proto/tests/connections.rs | 55 +- citadel_sdk/examples/client.rs | 4 +- citadel_sdk/examples/server.rs | 2 +- citadel_sdk/src/builder/node_builder.rs | 33 +- citadel_sdk/src/fs.rs | 34 +- citadel_sdk/src/macros.rs | 2 +- citadel_sdk/src/prefabs/client/broadcast.rs | 41 +- citadel_sdk/src/prefabs/client/mod.rs | 19 +- .../src/prefabs/client/peer_connection.rs | 58 +- .../src/prefabs/client/single_connection.rs | 172 +-- citadel_sdk/src/prefabs/mod.rs | 26 +- .../server/accept_file_transfer_kernel.rs | 2 +- .../prefabs/server/client_connect_listener.rs | 28 +- citadel_sdk/src/prefabs/server/empty.rs | 2 +- .../src/prefabs/server/internal_service.rs | 15 +- .../src/prefabs/shared/internal_service.rs | 13 +- citadel_sdk/src/remote_ext.rs | 88 +- citadel_sdk/src/responses.rs | 2 +- citadel_sdk/src/test_common.rs | 8 +- citadel_sdk/tests/stress_tests.rs | 56 +- citadel_types/src/crypto/mod.rs | 45 +- citadel_types/src/proto/mod.rs | 2 + citadel_user/src/account_manager.rs | 7 +- citadel_user/src/client_account.rs | 6 +- citadel_user/tests/primary.rs | 16 +- context.ai.json | 2 +- suggestions.ai.json | 2 +- 87 files changed, 2940 insertions(+), 3646 deletions(-) rename citadel_crypt/src/{messsaging.rs => messaging.rs} (67%) delete mode 100644 citadel_proto/src/proto/packet_processor/rekey_packet.rs diff --git a/Makefile.toml b/Makefile.toml index 1bac4cf10..899bb25fe 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -51,7 +51,7 @@ dependencies = ["install-binstall"] command = "cargo" description = "Tests all available unit/integration tests locally without using SQL/redis backends and appropriate localhost network settings" env = { "SKIP_EXT_BACKENDS" = "true" } -args = ["nextest", "run", "--features", "localhost-testing"] +args = ["nextest", "run", "--features", "localhost-testing,multi-threaded"] dependencies = ["install-nextest"] [tasks.test] diff --git a/README.md b/README.md index 951524883..845646841 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Multiple Key Encapsulation Mechanism (KEM) families: - [NTRU](https://ntru.org/) (Sntrup761) Novel Multi-layered Security Architecture: -- [Patent-pending](#-patent-and-open-source-commitment) multi-layered ratcheting algorithm +- [Patent-pending (allowed)](#-patent-and-open-source-commitment) 3D matrix ratcheting algorithm - Per-message re-keying mechanism - Multi-layered key exchange protocol - Multi-layered encryption with customizable algorithms @@ -159,7 +159,7 @@ async fn main() -> Result<(), Box> { The Citadel Protocol implements a novel multi-layered security approach that goes beyond traditional encryption methods: ### 🔄 Multi-layered Ratcheting -- Implements an advanced patent-pending ratcheting algorithm that operates across multiple security layers +- Implements an advanced patent-pending (allowed as of Dec 2024) ratcheting algorithm that operates across multiple security layers - Each layer maintains its own independent key schedule - Provides enhanced forward secrecy by frequently rotating keys at different intervals - Resistant against quantum attacks through post-quantum primitives @@ -301,6 +301,8 @@ The Citadel Protocol offers flexible data persistence options to suit various de ## 📜 Patent and Open Source Commitment +Status: Allowed as of December 2024 + The Citadel Protocol's core technology is a [patent-pending innovative security architecture](https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/20230403261) that combines multiple novel features into a unique, highly secure communication system. Despite the patent protection, we remain committed to keeping this technology free and open source for the benefit of the entire community. This approach ensures that: - The protocol remains freely available for everyone to use diff --git a/citadel_crypt/Cargo.toml b/citadel_crypt/Cargo.toml index 4f91f6e7d..83858d865 100644 --- a/citadel_crypt/Cargo.toml +++ b/citadel_crypt/Cargo.toml @@ -36,6 +36,7 @@ fcm = [] async-trait = { workspace = true } bincode = { workspace = true } serde = { workspace = true, features=["rc", "derive"] } +serde-big-array = { workspace = true } futures = { workspace = true } log = { workspace = true } byteorder = { workspace = true } diff --git a/citadel_crypt/src/endpoint_crypto_container.rs b/citadel_crypt/src/endpoint_crypto_container.rs index a20362901..d826e2fd1 100644 --- a/citadel_crypt/src/endpoint_crypto_container.rs +++ b/citadel_crypt/src/endpoint_crypto_container.rs @@ -106,34 +106,18 @@ impl PeerSessionCrypto { } } - /// Derives a new version of self safe to be used in the protocol - /// Changes made to the returned version will not persist - // TODO: Remove - pub fn new_session(&self) -> Self { - Self { - cid: self.cid, - toolset: self.toolset.clone(), - update_in_progress: self.update_in_progress.clone(), - local_is_initiator: self.local_is_initiator, - incrementing_group_id: self.incrementing_group_id.clone(), - latest_usable_version: self.latest_usable_version.clone(), - } - } - /// Gets a specific entropy_bank version, or, gets the latest version committed pub fn get_ratchet(&self, version: Option) -> Option { self.toolset .read() - .get_stacked_ratchet( - version.unwrap_or_else(|| self.latest_usable_version.load(ORDERING)), - ) + .get_ratchet(version.unwrap_or_else(|| self.latest_usable_version.load(ORDERING))) .cloned() } /// This should only be called when Bob receives the new DOU during the ReKey phase (will receive transfer), or, when Alice receives confirmation /// that the endpoint updated the ratchet (no transfer received, since none needed) #[allow(clippy::type_complexity)] - pub fn commit_next_stacked_ratchet_version( + pub fn commit_next_ratchet_version( &self, mut newest_version: R::Constructor, local_cid: u64, @@ -146,7 +130,7 @@ impl PeerSessionCrypto { CryptError, > { let mut toolset = self.toolset.write(); - let cur_vers = toolset.get_most_recent_stacked_ratchet_version(); + let cur_vers = toolset.get_most_recent_ratchet_version(); let next_vers = cur_vers.wrapping_add(1); // Update version before any stage operations @@ -177,14 +161,14 @@ impl PeerSessionCrypto { CryptError::RekeyUpdateError("Unable to progress past stage0_bob".to_string()) })?; - let next_stacked_ratchet = newest_version + let next_ratchet = newest_version .finish_with_custom_cid(local_cid) .ok_or_else(|| { CryptError::RekeyUpdateError( "Unable to progress past finish_with_custom_cid".to_string(), ) })?; - let status = toolset.update_from(next_stacked_ratchet).ok_or_else(|| { + let status = toolset.update_from(next_ratchet).ok_or_else(|| { CryptError::RekeyUpdateError("Unable to progress past update_from".to_string()) })?; log::trace!(target: "citadel", "[E2E] Client {local_cid} successfully updated Ratchet from v{cur_vers} to v{next_vers}"); @@ -193,13 +177,8 @@ impl PeerSessionCrypto { } /// Deregisters the oldest StackedRatchet version. Requires the version input to ensure program/network consistency for debug purposes - pub fn deregister_oldest_stacked_ratchet( - &self, - version: u32, - ) -> Result<(), CryptError> { - self.toolset - .write() - .deregister_oldest_stacked_ratchet(version) + pub fn deregister_oldest_ratchet(&self, version: u32) -> Result<(), CryptError> { + self.toolset.write().deregister_oldest_ratchet(version) } /// Performs an update internally, only if sync conditions allow @@ -224,7 +203,7 @@ impl PeerSessionCrypto { // There is one last special possibility. Let's say the initiator spam sends a bunch of FastMessage packets. Since the initiator's local won't have the appropriate proposed version ID // we need to ensure that it gets the right version, The crypt container will take care of that for us - let result = self.commit_next_stacked_ratchet_version( + let result = self.commit_next_ratchet_version( constructor, local_cid, !triggered_by_bob_to_alice_transfer, @@ -251,7 +230,7 @@ impl PeerSessionCrypto { } } - /// Unlocks the hold on future updates, then returns the latest stacked_ratchet + /// Unlocks the hold on future updates, then returns the latest ratchet pub fn maybe_unlock(&self) -> Option { if self.update_in_progress.reset_and_get_previous() != CurrentToggleState::AlreadyToggled { log::error!(target: "citadel", "Client {} expected update_in_progress to be true", self.cid); @@ -270,7 +249,7 @@ impl PeerSessionCrypto { let _ = self.latest_usable_version.fetch_add(1, ORDERING); } - pub fn get_and_increment_group_id(&mut self) -> u64 { + pub fn get_and_increment_group_id(&self) -> u64 { self.incrementing_group_id.fetch_add(1, ORDERING) } diff --git a/citadel_crypt/src/lib.rs b/citadel_crypt/src/lib.rs index 367bda6af..1c9e9d604 100644 --- a/citadel_crypt/src/lib.rs +++ b/citadel_crypt/src/lib.rs @@ -73,4 +73,4 @@ pub mod sync_toggle; pub mod toolset; /// For secure messaging with concurrent ratcheting operations -pub mod messsaging; +pub mod messaging; diff --git a/citadel_crypt/src/messsaging.rs b/citadel_crypt/src/messaging.rs similarity index 67% rename from citadel_crypt/src/messsaging.rs rename to citadel_crypt/src/messaging.rs index e47f3ef02..d5c4bc71f 100644 --- a/citadel_crypt/src/messsaging.rs +++ b/citadel_crypt/src/messaging.rs @@ -6,6 +6,7 @@ use citadel_types::prelude::SecrecyMode; use futures::{SinkExt, Stream}; use std::collections::VecDeque; use std::pin::Pin; +use std::sync::atomic::AtomicBool; use std::sync::Arc; use std::task::{Context, Poll}; use tokio::sync::mpsc::UnboundedReceiver; @@ -22,39 +23,38 @@ use tokio::sync::mpsc::UnboundedReceiver; /// underlying stream at the application-layer pub struct RatchetManagerMessengerLayer { sink: RatchetManagerMessengerLayerTx, - stream: RatchetManagerMessengerLayerRx

, + stream: RatchetManagerMessengerLayerRx, } pub struct RatchetManagerMessengerLayerTx { manager: DefaultRatchetManager, enqueued_messages: Arc>>, + is_active: Arc, secrecy_mode: SecrecyMode, } -impl Clone - for RatchetManagerMessengerLayerTx +pub struct RatchetManagerMessengerLayerRx { - fn clone(&self) -> Self { - Self { - manager: self.manager.clone(), - enqueued_messages: self.enqueued_messages.clone(), - secrecy_mode: self.secrecy_mode, - } - } -} - -pub struct RatchetManagerMessengerLayerRx { + manager: DefaultRatchetManager, rx: UnboundedReceiver

, + is_active: Arc, } +const ORDERING: std::sync::atomic::Ordering = std::sync::atomic::Ordering::Relaxed; + impl RatchetManagerMessengerLayer where E: Send + Sync + 'static, R: Ratchet, P: AttachedPayload, { - pub fn new(manager: DefaultRatchetManager, secrecy_mode: SecrecyMode) -> Self { + pub fn new( + manager: DefaultRatchetManager, + secrecy_mode: SecrecyMode, + rekey_finished_tx: Option>, + is_active: Arc, + ) -> Self { // The rx below will receive all messages that get sent via the ratchet manager layer // Some messages will "catch a ride" alongside a re-keying packet, others will be // standalone. This is not Pandora. @@ -67,12 +67,17 @@ where let enqueued_messages = Arc::new(tokio::sync::Mutex::new(VecDeque::new())); let enqueued_messages_clone = enqueued_messages.clone(); let manager_clone = manager.clone(); + let is_active_bg = is_active.clone(); - drop(tokio::task::spawn(async move { + let background_task = async move { // Each time a rekey finishes, we should check the local queue for any enqueued messages // to poll and send - while let Some(_next_ratchet) = on_rekey_finish_listener.recv().await { - // TODO: Send notifications to kernel, if applicable, with the latest ratchet + while let Some(next_ratchet) = on_rekey_finish_listener.recv().await { + if let Some(notify_on_finish_tx) = rekey_finished_tx.as_ref() { + if let Err(err) = notify_on_finish_tx.send(next_ratchet) { + log::warn!(target: "citadel", "Failed to notify on rekey finish: {err}"); + } + } // Check the latest item in the queue. Hold the lock to prevent race conditions locally let mut lock = enqueued_messages_clone.lock().await; if let Some(last_item) = lock.pop_front() { @@ -93,18 +98,31 @@ where } } } + + if !is_active_bg.load(ORDERING) { + break; + } } - log::warn!(target: "citadel", "on_rekey_finish_listener died") - })); + is_active_bg.store(false, ORDERING); + + log::warn!(target: "citadel", "RatchetManagerMessengerLayer: background task ending") + }; + + drop(tokio::task::spawn(background_task)); Self { sink: RatchetManagerMessengerLayerTx { - manager, + manager: manager.clone(), enqueued_messages, secrecy_mode, + is_active: is_active.clone(), + }, + stream: RatchetManagerMessengerLayerRx { + rx, + is_active, + manager, }, - stream: RatchetManagerMessengerLayerRx { rx }, } } @@ -112,7 +130,7 @@ where self, ) -> ( RatchetManagerMessengerLayerTx, - RatchetManagerMessengerLayerRx

, + RatchetManagerMessengerLayerRx, ) { (self.sink, self.stream) } @@ -124,18 +142,13 @@ where R: Ratchet, P: AttachedPayload, { - /// Triggers a re key on a separate task - pub fn trigger_rekey(&self) { - let this = self.manager.clone(); - drop(tokio::task::spawn(async move { - match this.trigger_rekey().await { - Ok(_new_vers) => (), - Err(err) => log::error!(target: "citadel", "{:?}", err), - } - })); - } + pub async fn send(&self, message: impl Into

) -> Result<(), CryptError> { + if !self.is_active.load(ORDERING) { + return Err(CryptError::Encrypt( + "Cannot send encrypted messages (stream died)".to_string(), + )); + } - pub async fn send(&mut self, message: impl Into

) -> Result<(), CryptError> { match self.secrecy_mode { SecrecyMode::BestEffort => { if let Some(message_not_sent) = self @@ -181,13 +194,44 @@ where } } -impl

Stream for RatchetManagerMessengerLayerRx

+impl Stream for RatchetManagerMessengerLayerRx where + E: Send + Sync + 'static, + R: Ratchet, P: AttachedPayload, { type Item = P; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.get_mut().rx.poll_recv(cx) + let this = self.get_mut(); + if !this.is_active.load(ORDERING) { + return Poll::Ready(None); + } + + this.rx.poll_recv(cx) + } +} + +impl Drop for RatchetManagerMessengerLayerTx +where + E: Send + Sync + 'static, + R: Ratchet, + P: AttachedPayload, +{ + fn drop(&mut self) { + self.is_active.store(false, ORDERING); + let _ = self.manager.shutdown(); + } +} + +impl Drop for RatchetManagerMessengerLayerRx +where + E: Send + Sync + 'static, + R: Ratchet, + P: AttachedPayload, +{ + fn drop(&mut self) { + self.is_active.store(false, ORDERING); + let _ = self.manager.shutdown(); } } diff --git a/citadel_crypt/src/packet_vector.rs b/citadel_crypt/src/packet_vector.rs index 982bd4b99..84813175c 100644 --- a/citadel_crypt/src/packet_vector.rs +++ b/citadel_crypt/src/packet_vector.rs @@ -17,7 +17,7 @@ //! //! ```rust //! use citadel_crypt::packet_vector::{PacketVector, generate_packet_vector}; -//! use citadel_crypt::entropy_bank::EntropyBank; +//! use citadel_crypt::ratchets::entropy_bank::EntropyBank; //! use citadel_crypt::misc::CryptError; //! //! fn coordinate_packets() -> Result<(), CryptError> { diff --git a/citadel_crypt/src/ratchets/mod.rs b/citadel_crypt/src/ratchets/mod.rs index 73d572d6e..42194058d 100644 --- a/citadel_crypt/src/ratchets/mod.rs +++ b/citadel_crypt/src/ratchets/mod.rs @@ -8,6 +8,7 @@ use citadel_types::crypto::SecurityLevel; use entropy_bank::EntropyBank; use serde::{Deserialize, Serialize}; use std::borrow::Cow; +use std::fmt::Debug; /// Organizes the different types of entropy_banks that can be used. Currently, there is only one: The Standard Drill pub mod entropy_bank; @@ -17,7 +18,9 @@ pub mod ratchet_manager; pub mod stacked; /// For allowing registration inside the toolset -pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + 'static { +pub trait Ratchet: + Debug + Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + 'static +{ type Constructor: EndpointRatchetConstructor + Serialize + for<'a> Deserialize<'a>; /// Returns the client ID diff --git a/citadel_crypt/src/ratchets/mono/ratchet.rs b/citadel_crypt/src/ratchets/mono/ratchet.rs index 2b2a987d3..8012e34f6 100644 --- a/citadel_crypt/src/ratchets/mono/ratchet.rs +++ b/citadel_crypt/src/ratchets/mono/ratchet.rs @@ -77,6 +77,15 @@ pub struct MonoRatchet { inner: Arc, } +impl Debug for MonoRatchet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MonoRatchet") + .field("cid", &self.get_cid()) + .field("version", &self.version()) + .finish() + } +} + #[derive(Serialize, Deserialize)] pub struct MonoRatchetInner { entropy_bank: EntropyBank, diff --git a/citadel_crypt/src/ratchets/ratchet_manager.rs b/citadel_crypt/src/ratchets/ratchet_manager.rs index a8d46fc81..f72d4d99b 100644 --- a/citadel_crypt/src/ratchets/ratchet_manager.rs +++ b/citadel_crypt/src/ratchets/ratchet_manager.rs @@ -17,8 +17,7 @@ //! //! ```rust,no_run //! use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; -//! use citadel_crypt::ratchet_manager::{RatchetManager, RatchetMessage}; -//! use futures::{Sink, Stream}; +//! use citadel_crypt::ratchets::ratchet_manager::{RatchetManager, RatchetMessage, RatchetManagerSink, RatchetManagerStream}; //! use citadel_crypt::ratchets::Ratchet; //! //! async fn example( @@ -28,9 +27,8 @@ //! psks: &[Vec] //! ) -> Result<(), Box> //! where -//! S: Sink + Unpin + Send + 'static, -//! >::Error: std::fmt::Debug, -//! I: Stream + Unpin + Send + 'static, +//! S: RatchetManagerSink>, +//! I: RatchetManagerStream>, //! R: Ratchet, //! { //! let mut manager = RatchetManager::new(sender, receiver, container, psks); @@ -87,6 +85,7 @@ where is_initiator: bool, state: Arc>, local_listener: LocalListener, + shutdown_tx: Arc>>>, } pub(crate) type LocalListener = Arc>>>; @@ -107,6 +106,7 @@ impl Clone for RatchetManager is_initiator: self.is_initiator, state: self.state.clone(), local_listener: self.local_listener.clone(), + shutdown_tx: self.shutdown_tx.clone(), } } } @@ -161,21 +161,21 @@ impl Debug for RatchetMessage

{ } pub trait RatchetManagerSink: - Sink> + Send + Unpin + 'static + Sink> + Send + Sync + Unpin + 'static { } impl RatchetManagerSink

for S where - S: Sink> + Send + Unpin + 'static + S: Sink> + Send + Sync + Unpin + 'static { } pub trait RatchetManagerStream: - Stream> + Send + Unpin + 'static + Stream> + Send + Sync + Unpin + 'static { } impl RatchetManagerStream

for I where - I: Stream> + Send + Unpin + 'static + I: Stream> + Send + Sync + Unpin + 'static { } @@ -207,6 +207,7 @@ where let (attached_payload_tx, attached_payload_rx) = tokio::sync::mpsc::unbounded_channel(); let (rekey_done_notifier_tx, rekey_done_notifier) = tokio::sync::mpsc::unbounded_channel(); let rekey_done_notifier = Arc::new(Mutex::new(Some(rekey_done_notifier))); + let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); let this = Self { sender: Arc::new(TokioMutex::new(sender)), receiver: Arc::new(Mutex::new(Some(receiver))), @@ -221,9 +222,11 @@ where role: Arc::new(Atomic::new(RekeyRole::Idle)), state: Arc::new(Atomic::new(RekeyState::Idle)), local_listener: Arc::new(Mutex::new(None)), + shutdown_tx: Arc::new(Mutex::new(Some(shutdown_tx))), }; - this.clone().spawn_rekey_process(rekey_done_notifier_tx); + this.clone() + .spawn_rekey_process(rekey_done_notifier_tx, shutdown_rx); this } @@ -238,6 +241,10 @@ where Self::new(sender, receiver, container, psks) } + pub fn is_rekeying(&self) -> bool { + self.role() != RekeyRole::Idle + } + /// Triggers a rekey without sending an attached payload pub async fn trigger_rekey(&self) -> Result<(), CryptError> { self.trigger_rekey_with_payload(None).await.map(|_| ()) @@ -257,7 +264,7 @@ where )); } - if self.role() != RekeyRole::Idle { + if self.is_rekeying() { // We are already in a rekey process return Ok(attached_payload); } @@ -268,7 +275,7 @@ where .session_crypto_state .toolset() .read() - .get_oldest_stacked_ratchet_version(); + .get_oldest_ratchet_version(); let latest_ratchet_version = self.session_crypto_state.latest_usable_version(); ( constructor, @@ -312,20 +319,29 @@ where } } - fn spawn_rekey_process(self, rekey_done_notifier_tx: tokio::sync::mpsc::UnboundedSender) { + fn spawn_rekey_process( + self, + rekey_done_notifier_tx: tokio::sync::mpsc::UnboundedSender, + shutdown_rx: tokio::sync::oneshot::Receiver<()>, + ) { struct DropWrapper { state: Arc>, + role: Arc>, } impl Drop for DropWrapper { fn drop(&mut self) { self.state.store(RekeyState::Halted, Ordering::Relaxed); + self.role.store(RekeyRole::Idle, Ordering::Relaxed); } } + let cid = self.cid; + let task = async move { let _drop_wrapper = DropWrapper { state: self.state.clone(), + role: self.role.clone(), }; let mut listener = { self.receiver.lock().take().unwrap() }; @@ -350,14 +366,31 @@ where } Err(err) => { - log::error!("cid {} rekey error: {err:?}", self.cid); - continue; + if matches!(err, CryptError::FatalError(..)) { + if self.shutdown_tx.lock().is_some() { + log::error!(target: "citadel", "Client {} fatal rekey error: {err:?}", self.cid); + } + break; + } else { + log::warn!(target: "citadel", "Client {} rekey error: {err:?}", self.cid); + } } } } }; - drop(citadel_io::tokio::task::spawn(task)); + let combined = async move { + tokio::select! { + _ = shutdown_rx => { + log::warn!(target: "citadel", "Client {cid} rekey process shutting down due to shutdown signal"); + }, + _ = task => { + log::warn!(target: "citadel", "Client {cid} rekey process shutting down"); + } + } + }; + + drop(citadel_io::tokio::task::spawn(combined)); } /// Runs a single round of re-keying, listening to events and returning @@ -371,7 +404,6 @@ where loop { let msg = receiver.next().await; - // log::debug!(target: "citadel", "Client {} received message {msg:?}", self.cid); match msg { Some(RatchetMessage::AliceToBob { payload, @@ -395,7 +427,7 @@ where .session_crypto_state .toolset() .read() - .get_oldest_stacked_ratchet_version(); + .get_oldest_ratchet_version(); let local_latest_ratchet_version = self.session_crypto_state.latest_usable_version(); @@ -531,7 +563,7 @@ where if let Some(version_to_truncate) = truncation_required { { self.session_crypto_state - .deregister_oldest_stacked_ratchet(version_to_truncate)?; + .deregister_oldest_ratchet(version_to_truncate)?; } self.sender @@ -578,7 +610,7 @@ where let latest_version = { let container = &self.session_crypto_state; - container.deregister_oldest_stacked_ratchet(version_to_truncate)?; + container.deregister_oldest_ratchet(version_to_truncate)?; container.post_alice_stage1_or_post_stage1_bob(); let latest_actual_ratchet_version = container .maybe_unlock() @@ -618,7 +650,7 @@ where container.post_alice_stage1_or_post_stage1_bob(); let latest_actual_ratchet_version = container .maybe_unlock() - .expect("Failed to fetch ratchet") + .expect("Failed to unlock container") .version(); let latest_version = container.latest_usable_version(); if latest_actual_ratchet_version != latest_version { @@ -677,7 +709,7 @@ where } None => { - return Err(CryptError::RekeyUpdateError( + return Err(CryptError::FatalError( "Unexpected end of stream".to_string(), )); } @@ -709,10 +741,18 @@ where self.rekey_done_notifier.lock().take() } + pub fn session_crypto_state(&self) -> &PeerSessionCrypto { + &self.session_crypto_state + } + pub fn get_ratchet(&self, version: Option) -> Option { self.session_crypto_state.get_ratchet(version) } + pub fn local_is_initiator(&self) -> bool { + self.session_crypto_state.local_is_initiator() + } + pub fn role(&self) -> RekeyRole { self.role.load(Ordering::Relaxed) } @@ -722,6 +762,11 @@ where self.role.store(role, Ordering::SeqCst); } + /// Shuts down the rekey + pub fn shutdown(&self) -> Option<()> { + self.shutdown_tx.lock().take()?.send(()).ok() + } + pub fn state(&self) -> RekeyState { self.state.load(Ordering::Relaxed) } @@ -832,12 +877,12 @@ mod tests { let start_version = alice_container .toolset() .read() - .get_most_recent_stacked_ratchet_version(); + .get_most_recent_ratchet_version(); let new_version = start_version + 1; let new_version_bob = bob_container .toolset() .read() - .get_most_recent_stacked_ratchet_version() + .get_most_recent_ratchet_version() + 1; assert_eq!(new_version, new_version_bob); (start_version, new_version) diff --git a/citadel_crypt/src/ratchets/stacked/ratchet.rs b/citadel_crypt/src/ratchets/stacked/ratchet.rs index 482f7e3ca..f7d5b3353 100644 --- a/citadel_crypt/src/ratchets/stacked/ratchet.rs +++ b/citadel_crypt/src/ratchets/stacked/ratchet.rs @@ -69,16 +69,26 @@ use citadel_pqcrypto::PostQuantumContainer; use citadel_types::crypto::SecurityLevel; use serde::{Deserialize, Serialize}; use sha3::Digest; +use std::fmt::Debug; use std::sync::Arc; /// A container meant to establish perfect forward secrecy AND scrambling w/ an independent key /// This is meant for messages, not file transfer. File transfers should use a single key throughout /// the entire file -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize)] pub struct StackedRatchet { pub(crate) inner: Arc, } +impl Debug for StackedRatchet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("StackedRatchet") + .field("cid", &self.get_cid()) + .field("version", &self.version()) + .finish() + } +} + impl Ratchet for StackedRatchet { type Constructor = StackedRatchetConstructor; diff --git a/citadel_crypt/src/scramble/crypt_splitter.rs b/citadel_crypt/src/scramble/crypt_splitter.rs index f44e78e88..d0bf9d74e 100644 --- a/citadel_crypt/src/scramble/crypt_splitter.rs +++ b/citadel_crypt/src/scramble/crypt_splitter.rs @@ -163,7 +163,7 @@ pub fn generate_scrambler_metadata>( #[allow(clippy::too_many_arguments)] fn get_scramble_encrypt_config<'a, R: Ratchet>( - stacked_ratchet: &'a R, + ratchet: &'a R, plain_text: &'a [u8], header_size_bytes: usize, security_level: SecurityLevel, @@ -180,9 +180,8 @@ fn get_scramble_encrypt_config<'a, R: Ratchet>( ), CryptError, > { - let (msg_pqc, msg_entropy_bank) = - stacked_ratchet.get_message_pqc_and_entropy_bank_at_layer(None)?; - let scramble_entropy_bank = stacked_ratchet.get_scramble_pqc_and_entropy_bank(); + let (msg_pqc, msg_entropy_bank) = ratchet.get_message_pqc_and_entropy_bank_at_layer(None)?; + let scramble_entropy_bank = ratchet.get_scramble_pqc_and_entropy_bank(); let cfg = generate_scrambler_metadata( msg_entropy_bank, plain_text, @@ -214,7 +213,7 @@ pub struct PacketCoordinate { pub fn par_scramble_encrypt_group, R: Ratchet, F, const N: usize>( plain_text: T, security_level: SecurityLevel, - stacked_ratchet: &R, + ratchet: &R, static_aux_ratchet: &R, header_size_bytes: usize, target_cid: u64, @@ -246,7 +245,7 @@ where } let (mut cfg, msg_entropy_bank, msg_pqc, scramble_entropy_bank) = get_scramble_encrypt_config( - stacked_ratchet, + ratchet, &plain_text, header_size_bytes, security_level, @@ -642,7 +641,7 @@ impl GroupReceiver { _group_id: u64, true_sequence: usize, wave_id: u32, - stacked_ratchet: &R, + ratchet: &R, packet: T, ) -> GroupReceiverStatus { let packet = packet.as_ref(); @@ -704,7 +703,7 @@ impl GroupReceiver { if wave_store.packets_received == wave_store.packets_in_wave { let ciphertext_bytes_for_this_wave = &wave_store.ciphertext_buffer[..wave_store.bytes_written]; - let (msg_pqc, msg_entropy_bank) = match stacked_ratchet + let (msg_pqc, msg_entropy_bank) = match ratchet .get_message_pqc_and_entropy_bank_at_layer(None) { Ok((msg_pqc, msg_entropy_bank)) => (msg_pqc, msg_entropy_bank), diff --git a/citadel_crypt/src/scramble/streaming_crypt_scrambler.rs b/citadel_crypt/src/scramble/streaming_crypt_scrambler.rs index ef583d36f..098f2e14f 100644 --- a/citadel_crypt/src/scramble/streaming_crypt_scrambler.rs +++ b/citadel_crypt/src/scramble/streaming_crypt_scrambler.rs @@ -202,7 +202,7 @@ pub fn scramble_encrypt_source< group_sender: GroupChanneler, CryptError>>, stop: Receiver<()>, security_level: SecurityLevel, - stacked_ratchet: R, + ratchet: R, static_aux_ratchet: R, header_size_bytes: usize, target_cid: u64, @@ -245,7 +245,7 @@ pub fn scramble_encrypt_source< target_cid, group_id, security_level, - stacked_ratchet, + ratchet, static_aux_ratchet, reader, transfer_type, @@ -296,7 +296,7 @@ async fn file_streamer { reader: BufReader, - stacked_ratchet: Ra, + ratchet: Ra, static_aux_ratchet: Ra, security_level: SecurityLevel, transfer_type: TransferType, @@ -348,7 +348,7 @@ impl AsyncCryptScram cx: &mut Context<'_>, ) -> Poll>> { let Self { - stacked_ratchet, + ratchet, static_aux_ratchet, file_len, read_cursor, @@ -383,7 +383,7 @@ impl AsyncCryptScram let header_inscriber = header_inscriber.clone(); let buffer = buffer.clone(); let security_level = *security_level; - let stacked_ratchet = stacked_ratchet.clone(); + let ratchet = ratchet.clone(); let static_aux_ratchet = static_aux_ratchet.clone(); let header_size_bytes = *header_size_bytes; let target_cid = *target_cid; @@ -394,7 +394,7 @@ impl AsyncCryptScram par_scramble_encrypt_group( &buffer.lock()[..poll_len], security_level, - &stacked_ratchet, + &ratchet, &static_aux_ratchet, header_size_bytes, target_cid, diff --git a/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs b/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs index efb635524..8bcbd7544 100644 --- a/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs +++ b/citadel_crypt/src/secure_buffer/partitioned_sec_buffer.rs @@ -44,6 +44,8 @@ use bytes::{BufMut, BytesMut}; use citadel_types::crypto::SecBuffer; +use serde::{Deserialize, Serialize}; +use serde_big_array::BigArray; use std::ops::{Deref, DerefMut, Range}; /// A secure buffer implementation with N fixed-size partitions @@ -60,8 +62,9 @@ use std::ops::{Deref, DerefMut, Range}; /// /// * `layout` - Array storing the size of each partition /// * `buffer` - Underlying secure buffer storing all partition data -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct PartitionedSecBuffer { + #[serde(with = "BigArray")] layout: [u32; N], buffer: SecBuffer, } diff --git a/citadel_crypt/src/secure_buffer/sec_packet.rs b/citadel_crypt/src/secure_buffer/sec_packet.rs index 720498078..ae05f9221 100644 --- a/citadel_crypt/src/secure_buffer/sec_packet.rs +++ b/citadel_crypt/src/secure_buffer/sec_packet.rs @@ -58,10 +58,11 @@ use crate::secure_buffer::partitioned_sec_buffer::{PartitionedSecBuffer, SliceHandle}; use byteorder::{BigEndian, ByteOrder}; use bytes::BytesMut; +use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Formatter}; /// An optimized unit designed for one-time only allocation between creating the packet and sending outbound -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct SecurePacket { /// There are three parts to the packet: /// [0]: The header @@ -113,7 +114,7 @@ impl SecurePacket { } /// Consumes the packet and returns the underlying buffer. - pub fn into_packet(self) -> BytesMut { + pub fn into_raw_packet(self) -> BytesMut { self.inner.into_buffer() } } @@ -125,6 +126,7 @@ impl Default for SecurePacket { } /// Used for handling the flow of writing a packet that must first receive its payload from the user, then its header from the protocol, and finally the extended payload appended to the end from the protocol +#[derive(Serialize, Deserialize)] pub enum SecureMessagePacket { PayloadNext(SecurePacket), HeaderNext(SecurePacket), @@ -210,6 +212,10 @@ impl SecureMessagePacket { } } + pub fn finish(self) -> std::io::Result { + self.write_payload_extension(0, |_| Ok(())) + } + /// The final write to the buffer should be the payload extension. This consumes self and returns bytes. pub fn write_payload_extension( self, @@ -218,9 +224,13 @@ impl SecureMessagePacket { ) -> std::io::Result { match self { Self::FinalPayloadExt(mut packet) => { + if len == 0 { + return Ok(packet.into_raw_packet()); + } + packet.prepare_extended_payload(len)?; (fx)(&mut packet.extended_payload()?)?; - Ok(packet.into_packet()) + Ok(packet.into_raw_packet()) } _ => Err(std::io::Error::new( @@ -242,13 +252,7 @@ impl SecureMessagePacket { impl Debug for SecureMessagePacket { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{:?}", - match self { - Self::PayloadNext(p) | Self::HeaderNext(p) | Self::FinalPayloadExt(p) => p, - } - ) + write!(f, "Secure message packet of length {}", self.message_len(),) } } diff --git a/citadel_crypt/src/toolset.rs b/citadel_crypt/src/toolset.rs index c1903735b..4f0735f32 100644 --- a/citadel_crypt/src/toolset.rs +++ b/citadel_crypt/src/toolset.rs @@ -50,8 +50,8 @@ pub const STATIC_AUX_VERSION: u32 = 0; pub struct Toolset { /// the CID of the owner pub cid: u64, - most_recent_stacked_ratchet_version: u32, - oldest_stacked_ratchet_version: u32, + most_recent_ratchet_version: u32, + oldest_ratchet_version: u32, #[serde(bound = "")] map: VecDeque, /// The static auxiliary entropy_bank was made to cover a unique situation that is consequence of dropping-off the back of the VecDeque upon upgrade: @@ -62,7 +62,7 @@ pub struct Toolset { /// Local filesystems should be encrypted anyways (otherwise voids warranty), but, having the HyxeFile layer is really just a "weak" layer of protection /// designed to derail any currently existing or historical viruses that may look for conventional means of breaking-through data #[serde(bound = "")] - static_auxiliary_stacked_ratchet: R, + static_auxiliary_ratchet: R, } // This clone should only be called in the middle of a session @@ -70,10 +70,10 @@ impl Clone for Toolset { fn clone(&self) -> Self { Self { cid: self.cid, - most_recent_stacked_ratchet_version: self.most_recent_stacked_ratchet_version, - oldest_stacked_ratchet_version: self.oldest_stacked_ratchet_version, + most_recent_ratchet_version: self.most_recent_ratchet_version, + oldest_ratchet_version: self.oldest_ratchet_version, map: self.map.clone(), - static_auxiliary_stacked_ratchet: self.static_auxiliary_stacked_ratchet.clone(), + static_auxiliary_ratchet: self.static_auxiliary_ratchet.clone(), } } } @@ -93,52 +93,52 @@ pub enum ToolsetUpdateStatus { } impl Toolset { - /// Creates a new [Toolset]. Designates the `stacked_ratchet` as the static auxiliary ratchet - /// stacked_ratchet should be version 0 - pub fn new(cid: u64, stacked_ratchet: R) -> Self { + /// Creates a new [Toolset]. Designates the `ratchet` as the static auxiliary + /// ratchet should be version 0 + pub fn new(cid: u64, ratchet: R) -> Self { let mut map = VecDeque::with_capacity(MAX_RATCHETS_IN_MEMORY); - map.push_front(stacked_ratchet.clone()); + map.push_front(ratchet.clone()); Toolset { cid, - most_recent_stacked_ratchet_version: 0, - oldest_stacked_ratchet_version: 0, + most_recent_ratchet_version: 0, + oldest_ratchet_version: 0, map, - static_auxiliary_stacked_ratchet: stacked_ratchet, + static_auxiliary_ratchet: ratchet, } } pub fn new_debug( cid: u64, - stacked_ratchet: R, - most_recent_stacked_ratchet_version: u32, - oldest_stacked_ratchet_version: u32, + ratchet: R, + most_recent_ratchet_version: u32, + oldest_ratchet_version: u32, ) -> Self { let mut map = VecDeque::with_capacity(MAX_RATCHETS_IN_MEMORY); - map.push_front(stacked_ratchet.clone()); + map.push_front(ratchet.clone()); Toolset { cid, - most_recent_stacked_ratchet_version, - oldest_stacked_ratchet_version, + most_recent_ratchet_version, + oldest_ratchet_version, map, - static_auxiliary_stacked_ratchet: stacked_ratchet, + static_auxiliary_ratchet: ratchet, } } /// Updates from an inbound DrillUpdateObject. Returns the new Drill - pub fn update_from(&mut self, new_stacked_ratchet: R) -> Option { - let latest_hr_version = self.get_most_recent_stacked_ratchet_version(); + pub fn update_from(&mut self, new_ratchet: R) -> Option { + let latest_hr_version = self.get_most_recent_ratchet_version(); - if new_stacked_ratchet.get_cid() != self.cid { - log::error!(target: "citadel", "The supplied hyper ratchet does not belong to the expected CID (expected: {}, obtained: {})", self.cid, new_stacked_ratchet.get_cid()); + if new_ratchet.get_cid() != self.cid { + log::error!(target: "citadel", "The supplied hyper ratchet does not belong to the expected CID (expected: {}, obtained: {})", self.cid, new_ratchet.get_cid()); return None; } - if latest_hr_version != new_stacked_ratchet.version().wrapping_sub(1) { - log::error!(target: "citadel", "The supplied hyper ratchet is not precedent to the entropy_bank update object (expected: {}, obtained: {})", latest_hr_version + 1, new_stacked_ratchet.version()); + if latest_hr_version != new_ratchet.version().wrapping_sub(1) { + log::error!(target: "citadel", "The supplied hyper ratchet is not precedent to the entropy_bank update object (expected: {}, obtained: {})", latest_hr_version + 1, new_ratchet.version()); return None; } - let update_status = self.append_stacked_ratchet(new_stacked_ratchet); + let update_status = self.append_ratchet(new_ratchet); let cur_version = match &update_status { ToolsetUpdateStatus::Committed { new_version } | ToolsetUpdateStatus::CommittedNeedsSynchronization { new_version, .. } => { @@ -146,10 +146,10 @@ impl Toolset { } }; - self.most_recent_stacked_ratchet_version = cur_version; + self.most_recent_ratchet_version = cur_version; - let prev_version = self.most_recent_stacked_ratchet_version.wrapping_sub(1); - log::trace!(target: "citadel", "[{}] Upgraded {} to {} for cid={}. Adjusted index of current: {}. Adjusted index of (current - 1): {} || OLDEST: {} || LEN: {}", MAX_RATCHETS_IN_MEMORY, prev_version, cur_version, self.cid, self.get_adjusted_index(cur_version), self.get_adjusted_index(prev_version), self.get_oldest_stacked_ratchet_version(), self.map.len()); + let prev_version = self.most_recent_ratchet_version.wrapping_sub(1); + log::trace!(target: "citadel", "[{}] Upgraded {} to {} for cid={}. Adjusted index of current: {}. Adjusted index of (current - 1): {} || OLDEST: {} || LEN: {}", MAX_RATCHETS_IN_MEMORY, prev_version, cur_version, self.cid, self.get_adjusted_index(cur_version), self.get_adjusted_index(prev_version), self.get_oldest_ratchet_version(), self.map.len()); Some(update_status) } @@ -157,13 +157,13 @@ impl Toolset { ///Replacing entropy_banks is not allowed, and is why this subroutine returns an error when a collision is detected /// /// Returns the new hyper ratchet version - fn append_stacked_ratchet(&mut self, stacked_ratchet: R) -> ToolsetUpdateStatus { + fn append_ratchet(&mut self, ratchet: R) -> ToolsetUpdateStatus { //debug_assert!(self.map.len() <= MAX_HYPER_RATCHETS_IN_MEMORY); - let new_version = stacked_ratchet.version(); - //println!("max hypers: {} @ {} bytes ea", MAX_HYPER_RATCHETS_IN_MEMORY, get_approx_bytes_per_stacked_ratchet()); - self.map.push_front(stacked_ratchet); + let new_version = ratchet.version(); + //println!("max hypers: {} @ {} bytes ea", MAX_HYPER_RATCHETS_IN_MEMORY, get_approx_bytes_per_ratchet()); + self.map.push_front(ratchet); if self.map.len() >= MAX_RATCHETS_IN_MEMORY { - let oldest_version = self.get_oldest_stacked_ratchet_version(); + let oldest_version = self.get_oldest_ratchet_version(); log::trace!(target: "citadel", "[Toolset Update] Needs Truncation. Oldest version: {}", oldest_version); ToolsetUpdateStatus::CommittedNeedsSynchronization { new_version, @@ -174,29 +174,28 @@ impl Toolset { } } - /// When append_stacked_ratchet returns CommittedNeedsSynchronization on Bob's side, Bob should first + /// When append_ratchet returns CommittedNeedsSynchronization on Bob's side, Bob should first /// send a packet to Alice telling her that capacity has been reached and that version V should be dropped. /// Alice will then prevent herself from sending any more packets using version V, and will locally run this /// function. Next, Alice should alert Bob telling him that it's now safe to remove version V. Bob then runs /// this function last. By doing this, Alice no longer sends packets that may be no longer be valid #[allow(unused_results)] - pub fn deregister_oldest_stacked_ratchet(&mut self, version: u32) -> Result<(), CryptError> { + pub fn deregister_oldest_ratchet(&mut self, version: u32) -> Result<(), CryptError> { if self.map.len() < MAX_RATCHETS_IN_MEMORY { return Err(CryptError::RekeyUpdateError( "Cannot call for deregistration unless the map len is maxed out".to_string(), )); } - let oldest = self.get_oldest_stacked_ratchet_version(); + let oldest = self.get_oldest_ratchet_version(); if oldest != version { Err(CryptError::RekeyUpdateError(format!( "Unable to deregister. Provided version: {version}, expected version: {oldest}", ))) } else { self.map.pop_back().ok_or(CryptError::OutOfBoundsError)?; - self.oldest_stacked_ratchet_version = - self.oldest_stacked_ratchet_version.wrapping_add(1); - log::trace!(target: "citadel", "[Toolset] Deregistered version {} for cid={}. New oldest: {} | LEN: {}", version, self.cid, self.oldest_stacked_ratchet_version, self.len()); + self.oldest_ratchet_version = self.oldest_ratchet_version.wrapping_add(1); + log::trace!(target: "citadel", "[Toolset] Deregistered version {} for cid={}. New oldest: {} | LEN: {}", version, self.cid, self.oldest_ratchet_version, self.len()); Ok(()) } } @@ -208,23 +207,23 @@ impl Toolset { } /// Returns the latest entropy_bank version - pub fn get_most_recent_stacked_ratchet(&self) -> Option<&R> { + pub fn get_most_recent_ratchet(&self) -> Option<&R> { self.map.front() } /// Returns the oldest entropy_bank in the VecDeque - pub fn get_oldest_stacked_ratchet(&self) -> Option<&R> { + pub fn get_oldest_ratchet(&self) -> Option<&R> { self.map.back() } /// Gets the oldest entropy_bank version - pub fn get_oldest_stacked_ratchet_version(&self) -> u32 { - self.oldest_stacked_ratchet_version + pub fn get_oldest_ratchet_version(&self) -> u32 { + self.oldest_ratchet_version } /// Returns the most recent entropy_bank - pub fn get_most_recent_stacked_ratchet_version(&self) -> u32 { - self.most_recent_stacked_ratchet_version + pub fn get_most_recent_ratchet_version(&self) -> u32 { + self.most_recent_ratchet_version } /// Returns the static auxiliary entropy_bank. There is no "set" function, because this really @@ -237,34 +236,33 @@ impl Toolset { /// of sync, then the static auxiliary entropy_bank is used to obtain the nonce for the AES GCM /// mode of encryption pub fn get_static_auxiliary_ratchet(&self) -> &R { - &self.static_auxiliary_stacked_ratchet + &self.static_auxiliary_ratchet } /// The index within the vec deque does not necessarily track the entropy_bank versions. /// This function adjusts for that #[inline] fn get_adjusted_index(&self, version: u32) -> usize { - self.most_recent_stacked_ratchet_version - .wrapping_sub(version) as usize + self.most_recent_ratchet_version.wrapping_sub(version) as usize } /// Returns a specific entropy_bank version - pub fn get_stacked_ratchet(&self, version: u32) -> Option<&R> { + pub fn get_ratchet(&self, version: u32) -> Option<&R> { let idx = self.get_adjusted_index(version); let res = self.map.get(idx); if res.is_none() { - log::error!(target: "citadel", "Attempted to get ratchet v{} for cid={}, but does not exist! len: {}. Oldest: {}. Newest: {}", version, self.cid, self.map.len(), self.oldest_stacked_ratchet_version, self.most_recent_stacked_ratchet_version); + log::error!(target: "citadel", "Attempted to get ratchet v{} for cid={}, but does not exist! len: {}. Oldest: {}. Newest: {}", version, self.cid, self.map.len(), self.oldest_ratchet_version, self.most_recent_ratchet_version); } res } /// Returns a range of entropy_banks. Returns None if any entropy_bank in the range is missing - pub fn get_stacked_ratchets(&self, versions: RangeInclusive) -> Option> { + pub fn get_ratchets(&self, versions: RangeInclusive) -> Option> { let mut ret = Vec::with_capacity((*versions.end() - *versions.start() + 1) as usize); for version in versions { - if let Some(entropy_bank) = self.get_stacked_ratchet(version) { + if let Some(entropy_bank) = self.get_ratchet(version) { ret.push(entropy_bank); } else { return None; @@ -287,7 +285,7 @@ impl Toolset { /// Resets the internal state to the default, if necessary. At the beginning of each session, this should be called pub fn verify_init_state(&self) -> Option<()> { - self.static_auxiliary_stacked_ratchet.reset_ara(); + self.static_auxiliary_ratchet.reset_ara(); Some(()) } } @@ -297,16 +295,16 @@ impl Toolset { pub type StaticAuxRatchet = StackedRatchet; impl From<(R, R)> for Toolset { fn from(entropy_bank: (R, R)) -> Self { - let most_recent_stacked_ratchet_version = entropy_bank.1.version(); - let oldest_stacked_ratchet_version = most_recent_stacked_ratchet_version; // for init, just like in the normal constructor + let most_recent_ratchet_version = entropy_bank.1.version(); + let oldest_ratchet_version = most_recent_ratchet_version; // for init, just like in the normal constructor let mut map = VecDeque::with_capacity(MAX_RATCHETS_IN_MEMORY); map.insert(0, entropy_bank.1); Self { cid: entropy_bank.0.get_cid(), - oldest_stacked_ratchet_version, - most_recent_stacked_ratchet_version, + oldest_ratchet_version, + most_recent_ratchet_version, map, - static_auxiliary_stacked_ratchet: entropy_bank.0, + static_auxiliary_ratchet: entropy_bank.0, } } } diff --git a/citadel_crypt/tests/primary.rs b/citadel_crypt/tests/primary.rs index e7430e343..350780c17 100644 --- a/citadel_crypt/tests/primary.rs +++ b/citadel_crypt/tests/primary.rs @@ -190,7 +190,7 @@ mod tests { println!("{:?}\n", &cids_order_decrypt); let output = chain.links.iter().rfold(onion_packet, |mut acc, (cid, container)| { println!("At {} (onion packet len: {})", cid, acc.len()); - let (pqc, entropy_bank) = container.get_stacked_ratchet(None).unwrap().message_pqc_entropy_bank(None); + let (pqc, entropy_bank) = container.get_ratchet(None).unwrap().message_pqc_entropy_bank(None); let payload = acc.split_off(HEADER_LEN); entropy_bank.aes_gcm_decrypt(0, pqc, payload) .map(|vec| bytes::BytesMut::from(&vec[..])).unwrap() @@ -220,7 +220,7 @@ mod tests { } #[test] - fn stacked_ratchets() { + fn ratchets() { citadel_logging::setup_log(); for x in 0u8..KEM_ALGORITHM_COUNT { for sec in 0..SecurityLevel::Extreme.value() { @@ -312,29 +312,27 @@ mod tests { log::trace!(target: "citadel", "Using {:?} with {:?} @ {:?} security level", algorithm.kem_algorithm, algorithm.encryption_algorithm, security_level); let algorithm = Some(algorithm); let security_level = security_level.unwrap_or_default(); - let mut alice_stacked_ratchet = R::Constructor::new_alice( + let mut alice_ratchet = R::Constructor::new_alice( ConstructorOpts::new_vec_init(algorithm, security_level), 99, 0, ) .unwrap(); - let transfer = alice_stacked_ratchet.stage0_alice().unwrap(); + let transfer = alice_ratchet.stage0_alice().unwrap(); - let mut bob_stacked_ratchet = R::Constructor::new_bob( + let mut bob_ratchet = R::Constructor::new_bob( 99, ConstructorOpts::new_vec_init(algorithm, security_level), transfer, bob_psks, ) .unwrap(); - let transfer = bob_stacked_ratchet.stage0_bob().unwrap(); + let transfer = bob_ratchet.stage0_bob().unwrap(); - alice_stacked_ratchet - .stage1_alice(transfer, alice_psks) - .unwrap(); + alice_ratchet.stage1_alice(transfer, alice_psks).unwrap(); - let alice_stacked_ratchet = alice_stacked_ratchet.finish().unwrap(); - let bob_stacked_ratchet = bob_stacked_ratchet.finish().unwrap(); + let alice_ratchet = alice_ratchet.finish().unwrap(); + let bob_ratchet = bob_ratchet.finish().unwrap(); const MESSAGE: &[u8] = b"Hello, world!" as &[u8]; const HEADER_LEN: usize = 50; @@ -349,20 +347,20 @@ mod tests { let plaintext_packet = packet.clone(); - alice_stacked_ratchet + alice_ratchet .protect_message_packet(Some(security_level), HEADER_LEN, &mut packet) .unwrap(); assert_ne!(packet, plaintext_packet); let mut header = packet.split_to(HEADER_LEN); - bob_stacked_ratchet + bob_ratchet .validate_message_packet(Some(security_level), &header[..], &mut packet) .unwrap(); header.unsplit(packet); assert_eq!(header, plaintext_packet); - alice_stacked_ratchet + alice_ratchet } #[rstest] @@ -393,7 +391,7 @@ mod tests { ) { toolset::(enx, kem, sig); #[cfg(feature = "fcm")] - toolset::(enx, kem, sig); + toolset::(enx, kem, sig); } fn toolset(enx: EncryptionAlgorithm, kem: KemAlgorithm, sig: SigAlgorithm) { @@ -429,8 +427,8 @@ mod tests { match res { ToolsetUpdateStatus::Committed { .. } => { assert!(x < MAX_RATCHETS_IN_MEMORY as u32); - assert_eq!(0, toolset.get_oldest_stacked_ratchet_version()); - assert_eq!(x, toolset.get_most_recent_stacked_ratchet_version()); + assert_eq!(0, toolset.get_oldest_ratchet_version()); + assert_eq!(x, toolset.get_most_recent_ratchet_version()); } ToolsetUpdateStatus::CommittedNeedsSynchronization { @@ -439,21 +437,19 @@ mod tests { } => { assert_eq!(old_version, 0); // we're not truncating it yet, so it should be 0 assert!(x + 1 >= MAX_RATCHETS_IN_MEMORY as u32); - assert_eq!(0, toolset.get_oldest_stacked_ratchet_version()); // this shouldn't change because the oldest needs to be manually removed - assert_eq!(x, toolset.get_most_recent_stacked_ratchet_version()); + assert_eq!(0, toolset.get_oldest_ratchet_version()); // this shouldn't change because the oldest needs to be manually removed + assert_eq!(x, toolset.get_most_recent_ratchet_version()); } } } for x in 0..COUNT { - if toolset.deregister_oldest_stacked_ratchet(x).is_ok() { - assert_eq!(x + 1, toolset.get_oldest_stacked_ratchet_version()); + if toolset.deregister_oldest_ratchet(x).is_ok() { + assert_eq!(x + 1, toolset.get_oldest_ratchet_version()); } else { assert_eq!(toolset.len(), MAX_RATCHETS_IN_MEMORY - 1); assert_eq!( - toolset - .get_oldest_stacked_ratchet_version() - .saturating_sub(1), + toolset.get_oldest_ratchet_version().saturating_sub(1), COUNT - MAX_RATCHETS_IN_MEMORY as u32 ); } @@ -474,16 +470,13 @@ mod tests { .unwrap(); assert_eq!(toolset.len(), MAX_RATCHETS_IN_MEMORY); assert_eq!( - toolset - .get_oldest_stacked_ratchet_version() - .saturating_sub(1), - toolset.get_most_recent_stacked_ratchet_version() - MAX_RATCHETS_IN_MEMORY as u32 + toolset.get_oldest_ratchet_version().saturating_sub(1), + toolset.get_most_recent_ratchet_version() - MAX_RATCHETS_IN_MEMORY as u32 ); toolset - .deregister_oldest_stacked_ratchet( - toolset.get_most_recent_stacked_ratchet_version() - - (MAX_RATCHETS_IN_MEMORY - 1) as u32, // Add one since the max count is only for when it's full and temporarily fills the full buffer + .deregister_oldest_ratchet( + toolset.get_most_recent_ratchet_version() - (MAX_RATCHETS_IN_MEMORY - 1) as u32, // Add one since the max count is only for when it's full and temporarily fills the full buffer ) .unwrap(); assert_eq!(toolset.len(), MAX_RATCHETS_IN_MEMORY - 1); @@ -543,7 +536,7 @@ mod tests { ) { toolset_wrapping_vers::(enx, kem, sig); #[cfg(feature = "fcm")] - toolset_wrapping_vers::(enx, kem, sig); + toolset_wrapping_vers::(enx, kem, sig); } fn toolset_wrapping_vers( @@ -563,7 +556,7 @@ mod tests { &PRE_SHARED_KEYS, ); let mut toolset = Toolset::new_debug(cid, hr.0, vers, vers); - let r = toolset.get_stacked_ratchet(vers).unwrap(); + let r = toolset.get_ratchet(vers).unwrap(); assert_eq!(r.version(), vers); const COUNT: usize = 100; @@ -587,27 +580,24 @@ mod tests { .0, ) .unwrap(); - let ratchet = toolset.get_stacked_ratchet(cur_vers).unwrap(); + let ratchet = toolset.get_ratchet(cur_vers).unwrap(); assert_eq!(ratchet.version(), cur_vers); cur_vers = cur_vers.wrapping_add(1); insofar += 1; } - assert_eq!( - toolset.get_oldest_stacked_ratchet().unwrap().version(), - vers - ); + assert_eq!(toolset.get_oldest_ratchet().unwrap().version(), vers); let mut amt_culled = 0; for _ in 0..COUNT { if toolset.len() == MAX_RATCHETS_IN_MEMORY { continue; } toolset - .deregister_oldest_stacked_ratchet(vers.wrapping_add(amt_culled)) + .deregister_oldest_ratchet(vers.wrapping_add(amt_culled)) .unwrap(); amt_culled += 1; assert_eq!( - toolset.get_oldest_stacked_ratchet().unwrap().version(), + toolset.get_oldest_ratchet().unwrap().version(), vers.wrapping_add(amt_culled) ); } @@ -647,7 +637,7 @@ mod tests { |decrypted, plaintext, _, _| debug_assert_eq!(decrypted, plaintext), ); #[cfg(feature = "fcm")] - scrambler_transmission_spectrum::( + scrambler_transmission_spectrum::( enx, kem, sig, @@ -705,7 +695,7 @@ mod tests { scrambler_transmission_spectrum::(enx, kem, sig, tx_type, verifier); #[cfg(feature = "fcm")] - scrambler_transmission_spectrum::( + scrambler_transmission_spectrum::( enx, kem, sig, tx_type, verifier, ); } diff --git a/citadel_proto/Cargo.toml b/citadel_proto/Cargo.toml index baaf75c93..abcb1f39d 100644 --- a/citadel_proto/Cargo.toml +++ b/citadel_proto/Cargo.toml @@ -73,7 +73,8 @@ itertools = { workspace = true } tracing = { workspace = true, optional = true } bytemuck = { workspace = true, features = ["derive"] } chrono = { workspace = true } -sha256 = { workspace = true } +sha3 = { workspace = true } +citadel_logging = { workspace = true } [dev-dependencies] citadel_logging = { workspace = true } diff --git a/citadel_proto/src/constants.rs b/citadel_proto/src/constants.rs index f67b5e5ec..16ec0765c 100644 --- a/citadel_proto/src/constants.rs +++ b/citadel_proto/src/constants.rs @@ -33,7 +33,7 @@ use lazy_static::lazy_static; // Note: these values can each be up to 1024 in size, but, to be safe, we fix the upper // bound to 255 (u8::MAX) to ensure that the values fit inside the u32 bit packer pub const MAJOR_VERSION: u8 = 0; -pub const MINOR_VERSION: u8 = 6; +pub const MINOR_VERSION: u8 = 7; pub const PATCH_VERSION: u8 = 0; lazy_static! { diff --git a/citadel_proto/src/kernel/kernel_communicator.rs b/citadel_proto/src/kernel/kernel_communicator.rs index 276f1ebf6..e365e00b7 100644 --- a/citadel_proto/src/kernel/kernel_communicator.rs +++ b/citadel_proto/src/kernel/kernel_communicator.rs @@ -29,6 +29,7 @@ use crate::error::NetworkError; use crate::proto::node_result::NodeResult; use crate::proto::remote::Ticket; +use citadel_crypt::ratchets::Ratchet; use citadel_io::Mutex; use futures::{Future, Stream}; use std::collections::HashMap; @@ -36,18 +37,18 @@ use std::pin::Pin; use std::sync::Arc; #[derive(Default)] -pub struct KernelAsyncCallbackHandler { - pub inner: Arc>, +pub struct KernelAsyncCallbackHandler { + pub inner: Arc>>, } #[derive(Default)] -pub struct KernelAsyncCallbackHandlerInner { - map: HashMap, +pub struct KernelAsyncCallbackHandlerInner { + pub(crate) map: HashMap>, } #[allow(dead_code)] -pub(crate) struct CallbackNotifier { - tx: citadel_io::tokio::sync::mpsc::UnboundedSender, +pub(crate) struct CallbackNotifier { + tx: citadel_io::tokio::sync::mpsc::UnboundedSender>, key: CallbackKey, } @@ -57,10 +58,10 @@ pub struct CallbackKey { pub session_cid: Option, } -fn search_for_value<'a>( - map: &'a mut HashMap, +fn search_for_value<'a, R: Ratchet>( + map: &'a mut HashMap>, callback_key_received: &'a CallbackKey, -) -> Option<(&'a mut CallbackNotifier, CallbackKey)> { +) -> Option<(&'a mut CallbackNotifier, CallbackKey)> { let expected_ticket = callback_key_received.ticket; for (key, notifier) in map.iter_mut() { let ticket = key.ticket; @@ -122,11 +123,11 @@ impl CallbackKey { } } -impl KernelAsyncCallbackHandler { +impl KernelAsyncCallbackHandler { pub fn register_stream( &self, callback_key: CallbackKey, - ) -> Result { + ) -> Result, NetworkError> { let mut this = self.inner.lock(); let (tx, rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); this.insert( @@ -151,7 +152,7 @@ impl KernelAsyncCallbackHandler { } // If a notification occurred, returns None. Else, returns the result - fn maybe_notify(&self, result: NodeResult) -> Option { + fn maybe_notify(&self, result: NodeResult) -> Option> { match result.callback_key() { Some(ref received_callback_key) => { let mut this = self.inner.lock(); @@ -173,8 +174,8 @@ impl KernelAsyncCallbackHandler { pub async fn on_message_received>>( &self, - result: NodeResult, - default: impl FnOnce(NodeResult) -> F, + result: NodeResult, + default: impl FnOnce(NodeResult) -> F, ) -> Result<(), NetworkError> { match self.maybe_notify(result) { None => Ok(()), @@ -183,11 +184,11 @@ impl KernelAsyncCallbackHandler { } } -impl KernelAsyncCallbackHandlerInner { +impl KernelAsyncCallbackHandlerInner { fn insert( &mut self, callback_key: CallbackKey, - notifier: CallbackNotifier, + notifier: CallbackNotifier, ) -> Result<(), NetworkError> { if self.map.insert(callback_key, notifier).is_some() { Err(NetworkError::InternalError("Overwrote previous notifier")) @@ -197,7 +198,7 @@ impl KernelAsyncCallbackHandlerInner { } } -impl Clone for KernelAsyncCallbackHandler { +impl Clone for KernelAsyncCallbackHandler { fn clone(&self) -> Self { Self { inner: self.inner.clone(), @@ -206,26 +207,26 @@ impl Clone for KernelAsyncCallbackHandler { } #[allow(dead_code)] -pub struct KernelStreamSubscription { - inner: citadel_io::tokio::sync::mpsc::UnboundedReceiver, - ptr: KernelAsyncCallbackHandler, +pub struct KernelStreamSubscription { + inner: citadel_io::tokio::sync::mpsc::UnboundedReceiver>, + ptr: KernelAsyncCallbackHandler, callback_key: CallbackKey, } -impl KernelStreamSubscription { +impl KernelStreamSubscription { pub fn callback_key(&self) -> &CallbackKey { &self.callback_key } } -impl Drop for KernelStreamSubscription { +impl Drop for KernelStreamSubscription { fn drop(&mut self) { self.ptr.remove_listener(self.callback_key) } } -impl Stream for KernelStreamSubscription { - type Item = NodeResult; +impl Stream for KernelStreamSubscription { + type Item = NodeResult; fn poll_next( mut self: Pin<&mut Self>, diff --git a/citadel_proto/src/kernel/kernel_executor.rs b/citadel_proto/src/kernel/kernel_executor.rs index 3802db502..bc3375436 100644 --- a/citadel_proto/src/kernel/kernel_executor.rs +++ b/citadel_proto/src/kernel/kernel_executor.rs @@ -49,7 +49,7 @@ use crate::error::NetworkError; use crate::kernel::kernel_communicator::KernelAsyncCallbackHandler; use crate::kernel::kernel_trait::NetKernel; use crate::kernel::{KernelExecutorArguments, KernelExecutorSettings, RuntimeFuture}; -use crate::proto::node::Node; +use crate::proto::node::CitadelNode; use crate::proto::node_result::NodeResult; use crate::proto::outbound_sender::{unbounded, UnboundedReceiver}; use crate::proto::packet_processor::includes::Duration; @@ -58,9 +58,9 @@ use crate::proto::remote::NodeRemote; /// Creates a [KernelExecutor] pub struct KernelExecutor, R: Ratchet> { server_remote: Option>, - server_to_kernel_rx: Option>, + server_to_kernel_rx: Option>>, shutdown_alerter_rx: Option>, - callback_handler: Option, + callback_handler: Option>, context: Option, account_manager: AccountManager, kernel_executor_settings: KernelExecutorSettings, @@ -87,13 +87,13 @@ impl, R: Ratchet> KernelExecutor { client_config, kernel_executor_settings, stun_servers, - server_only_session_password, + server_only_session_init_settings, } = args; let (server_to_kernel_tx, server_to_kernel_rx) = unbounded(); let (server_shutdown_alerter_tx, server_shutdown_alerter_rx) = citadel_io::tokio::sync::oneshot::channel(); // After this gets called, the server starts running and we get a remote - let (remote, future, localset_opt, callback_handler) = Node::::init( + let (remote, future, localset_opt, callback_handler) = CitadelNode::::init( hypernode_type, server_to_kernel_tx, account_manager.clone(), @@ -101,7 +101,7 @@ impl, R: Ratchet> KernelExecutor { underlying_proto, client_config, stun_servers, - server_only_session_password, + server_only_session_init_settings, ) .await .map_err(|err| NetworkError::Generic(err.to_string()))?; @@ -170,10 +170,10 @@ impl, R: Ratchet> KernelExecutor { #[allow(unused_must_use)] async fn kernel_inner_loop( kernel: &mut K, - mut server_to_kernel_rx: UnboundedReceiver, + mut server_to_kernel_rx: UnboundedReceiver>, citadel_server_remote: NodeRemote, shutdown: citadel_io::tokio::sync::oneshot::Receiver<()>, - callback_handler: KernelAsyncCallbackHandler, + callback_handler: KernelAsyncCallbackHandler, kernel_settings: KernelExecutorSettings, ) -> Result<(), NetworkError> { let citadel_server_remote = &citadel_server_remote; @@ -195,7 +195,7 @@ impl, R: Ratchet> KernelExecutor { } }; - reader.try_for_each_concurrent(kernel_settings.max_concurrency, |message: NodeResult| async move { + reader.try_for_each_concurrent(kernel_settings.max_concurrency, |message: NodeResult| async move { log::trace!(target: "citadel", "[KernelExecutor] Received message {:?}", message); match message { NodeResult::Shutdown => { diff --git a/citadel_proto/src/kernel/kernel_trait.rs b/citadel_proto/src/kernel/kernel_trait.rs index 4c2e2f3c0..0eb361bc4 100644 --- a/citadel_proto/src/kernel/kernel_trait.rs +++ b/citadel_proto/src/kernel/kernel_trait.rs @@ -46,7 +46,7 @@ pub trait NetKernel: Send + Sync { /// When the server processes a valid entry, the value is sent here. Each call to 'on_node_event_received' is done /// *concurrently* (but NOT in *parallel*). This allows code inside this function to await without blocking new incoming /// messages - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError>; + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError>; /// When the system is ready to shutdown, this is called async fn on_stop(&mut self) -> Result<(), NetworkError>; } diff --git a/citadel_proto/src/kernel/mod.rs b/citadel_proto/src/kernel/mod.rs index c2cd722f1..de08b62ba 100644 --- a/citadel_proto/src/kernel/mod.rs +++ b/citadel_proto/src/kernel/mod.rs @@ -32,7 +32,8 @@ use crate::error::NetworkError; use crate::macros::ContextRequirements; -use crate::prelude::{PreSharedKey, ServerUnderlyingProtocol}; +use crate::prelude::ServerUnderlyingProtocol; +use crate::proto::session::ServerOnlySessionInitSettings; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::macros::support::Future; use citadel_io::tokio::runtime::Handle; @@ -76,5 +77,5 @@ pub struct KernelExecutorArguments { pub client_config: Option>, pub kernel_executor_settings: KernelExecutorSettings, pub stun_servers: Option>, - pub server_only_session_password: Option, + pub server_only_session_init_settings: Option, } diff --git a/citadel_proto/src/lib.rs b/citadel_proto/src/lib.rs index 2c8786501..ef59120cc 100644 --- a/citadel_proto/src/lib.rs +++ b/citadel_proto/src/lib.rs @@ -76,6 +76,13 @@ )] #![allow(rustdoc::broken_intra_doc_links)] +use crate::error::NetworkError; +use crate::proto::session::UserMessage; +use citadel_crypt::messaging::{ + RatchetManagerMessengerLayer, RatchetManagerMessengerLayerRx, RatchetManagerMessengerLayerTx, +}; +use citadel_crypt::ratchets::ratchet_manager::DefaultRatchetManager; + #[cfg(not(feature = "multi-threaded"))] pub const fn build_tag() -> &'static str { "Single-Threaded" @@ -90,6 +97,7 @@ pub const fn build_tag() -> &'static str { #[macro_use] pub mod macros { use either::Either; + use std::future::Future; use crate::proto::session::CitadelSessionInner; @@ -107,6 +115,9 @@ pub mod macros { pub trait SyncContextRequirements: 'static {} impl SyncContextRequirements for T {} + pub trait FutureRequirements: ContextRequirements + Future {} + impl FutureRequirements for T {} + pub type WeakBorrowType = std::rc::Weak>; pub type SessionBorrow<'a, R> = std::cell::RefMut<'a, CitadelSessionInner>; @@ -283,6 +294,7 @@ pub mod macros { #[macro_use] pub mod macros { use either::Either; + use std::future::Future; use crate::proto::session::CitadelSessionInner; @@ -300,6 +312,9 @@ pub mod macros { pub trait SyncContextRequirements: Send + Sync + 'static {} impl SyncContextRequirements for T {} + pub trait FutureRequirements: ContextRequirements + Future {} + impl FutureRequirements for T {} + pub type WeakBorrowType = std::sync::Weak>; pub type SessionBorrow<'a, R> = citadel_io::RwLockWriteGuard<'a, CitadelSessionInner>; @@ -522,9 +537,8 @@ pub mod prelude { pub use crate::proto::misc::panic_future::ExplicitPanicFuture; pub use crate::proto::misc::session_security_settings::SessionSecuritySettingsBuilder; pub use crate::proto::misc::underlying_proto::ServerUnderlyingProtocol; - pub use crate::proto::node::Node; + pub use crate::proto::node::CitadelNode; pub use crate::proto::outbound_sender::OutboundUdpSender; - pub use crate::proto::packet_crafter::SecureProtocolPacket; pub use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; pub use crate::proto::peer::channel::*; pub use crate::proto::peer::group_channel::{ @@ -551,6 +565,7 @@ pub mod prelude { pub use crate::auth::AuthenticationRequest; #[doc(hidden)] pub use crate::proto::misc::{read_one_packet_as_framed, write_one_packet}; + pub use crate::proto::session::ServerOnlySessionInitSettings; pub use citadel_crypt::scramble::streaming_crypt_scrambler::ObjectSource; pub use citadel_types::crypto::EncryptionAlgorithm; pub use citadel_types::crypto::KemAlgorithm; @@ -583,3 +598,8 @@ mod inner_arg; pub mod kernel; /// The primary module of this crate mod proto; + +pub(crate) type ProtocolRatchetManager = DefaultRatchetManager; +pub type ProtocolMessengerTx = RatchetManagerMessengerLayerTx; +pub type ProtocolMessengerRx = RatchetManagerMessengerLayerRx; +pub type ProtocolMessenger = RatchetManagerMessengerLayer; diff --git a/citadel_proto/src/proto/endpoint_crypto_accessor.rs b/citadel_proto/src/proto/endpoint_crypto_accessor.rs index 69df52e15..e0bce01eb 100644 --- a/citadel_proto/src/proto/endpoint_crypto_accessor.rs +++ b/citadel_proto/src/proto/endpoint_crypto_accessor.rs @@ -23,7 +23,7 @@ #![allow(dead_code)] use crate::error::NetworkError; use crate::inner_arg::ExpectedInnerTargetMut; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; use crate::proto::state_container::{StateContainer, StateContainerInner}; use citadel_crypt::ratchets::Ratchet; @@ -39,34 +39,25 @@ impl EndpointCryptoAccessor { where F: for<'a> FnOnce(&'a R, &mut dyn ExpectedInnerTargetMut>) -> T, { - match self { - Self::P2P(peer_cid, state_container) => { - let mut state_container = inner_mut_state!(state_container); - state_container - .get_peer_session_crypto(*peer_cid) - .ok_or(NetworkError::InternalError("Peer session crypto missing"))? - .get_ratchet(None) - .cloned() - .map(|hr| access(&hr, &mut state_container)) - .ok_or(NetworkError::InternalError("Ratchet does not exist")) - } + let (peer_cid, state_container) = match self { + Self::P2P(peer_cid, state_container) => (*peer_cid, state_container), - Self::C2S(state_container) => { - let mut state_container = inner_mut_state!(state_container); - let hr = state_container - .get_c2s_crypto() - .map(|r| r.get_ratchet(vers).cloned()) - .ok_or(NetworkError::InternalError("C2S container does not exist"))? - .ok_or(NetworkError::InternalError("Ratchet does not exist"))?; - Ok(access(&hr, &mut state_container)) - } - } + Self::C2S(state_container) => (C2S_IDENTITY_CID, state_container), + }; + + let mut state_container = inner_mut_state!(state_container); + state_container + .get_virtual_connection_crypto(peer_cid) + .ok_or(NetworkError::InternalError("Peer session crypto missing"))? + .get_ratchet(vers) + .map(|hr| access(&hr, &mut state_container)) + .ok_or(NetworkError::InternalError("Ratchet does not exist")) } pub fn get_target_cid(&self) -> u64 { match self { Self::P2P(target_cid, ..) => *target_cid, - Self::C2S(..) => C2S_ENCRYPTION_ONLY, + Self::C2S(..) => C2S_IDENTITY_CID, } } } diff --git a/citadel_proto/src/proto/misc/net.rs b/citadel_proto/src/proto/misc/net.rs index b5fe9c1e4..82f159672 100644 --- a/citadel_proto/src/proto/misc/net.rs +++ b/citadel_proto/src/proto/misc/net.rs @@ -233,7 +233,7 @@ impl GenericNetworkListener { listener: TcpListener, redirect_to_quic: Option<(TlsDomain, bool)>, ) -> std::io::Result { - let (send, recv) = citadel_io::tokio::sync::mpsc::channel(1024); + let (inbound_streams_tx, recv) = citadel_io::tokio::sync::mpsc::channel(1024); let local_addr = listener.local_addr()?; let tls_domain = redirect_to_quic.as_ref().and_then(|r| r.0.clone()); @@ -273,9 +273,20 @@ impl GenericNetworkListener { Ok((GenericNetworkStream::Tcp(conn), addr)) } - send.send(handle_stream_non_terminating(stream, addr, redirect_to_quic).await) - .await - .map_err(|err| generic_error(err.to_string()))?; + let redirect_to_quic = redirect_to_quic.clone(); + let inbound_streams_tx = inbound_streams_tx.clone(); + let handle_stream = async move { + if let Err(err) = inbound_streams_tx + .send(handle_stream_non_terminating(stream, addr, &redirect_to_quic).await) + .await + .map_err(|err| generic_error(err.to_string())) + { + log::error!(target: "citadel", "Error sending inbound stream from {addr} to listener: {err}"); + } + }; + + // Spawn to prevent backpressure against pending inbound connections + spawn!(handle_stream); } }; @@ -592,7 +603,7 @@ impl Stream for DualListener { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let Self { future, recv, .. } = &mut *self; - // if this future ends, it's over + // If this future ends, it's over match future.as_mut().poll(cx) { Poll::Pending => {} Poll::Ready(res) => { diff --git a/citadel_proto/src/proto/misc/ordered_channel.rs b/citadel_proto/src/proto/misc/ordered_channel.rs index f6b14b397..380c51da3 100644 --- a/citadel_proto/src/proto/misc/ordered_channel.rs +++ b/citadel_proto/src/proto/misc/ordered_channel.rs @@ -28,21 +28,21 @@ //! - `net.rs`: Network operations use crate::error::NetworkError; +use crate::macros::ContextRequirements; use crate::proto::outbound_sender::UnboundedSender; -use citadel_types::crypto::SecBuffer; use std::collections::HashMap; use std::time::Instant; -pub struct OrderedChannel { - sink: UnboundedSender, - map: HashMap, +pub struct OrderedChannel { + sink: UnboundedSender, + map: HashMap, last_message_received: Option, #[allow(dead_code)] last_message_received_instant: Option, } -impl OrderedChannel { - pub fn new(sink: UnboundedSender) -> Self { +impl OrderedChannel { + pub fn new(sink: UnboundedSender) -> Self { Self { sink, map: HashMap::new(), @@ -52,7 +52,7 @@ impl OrderedChannel { } #[allow(unused_results)] - pub fn on_packet_received(&mut self, id: u64, packet: SecBuffer) -> Result<(), NetworkError> { + pub fn on_packet_received(&mut self, id: u64, packet: T) -> Result<(), NetworkError> { let next_expected_message_id = self .last_message_received .map(|r| r.wrapping_add(1)) @@ -69,7 +69,7 @@ impl OrderedChannel { } #[allow(unused_results)] - fn store_received_packet(&mut self, id: u64, packet: SecBuffer) { + fn store_received_packet(&mut self, id: u64, packet: T) { self.map.insert(id, packet); self.set_last_message_received_instant(); } @@ -82,7 +82,7 @@ impl OrderedChannel { self.last_message_received = Some(id) } - fn send_then_scan(&mut self, new_id: u64, packet: SecBuffer) -> Result<(), NetworkError> { + fn send_then_scan(&mut self, new_id: u64, packet: T) -> Result<(), NetworkError> { self.send_unconditional(new_id, packet)?; if !self.map.is_empty() { self.scan_send(new_id) @@ -102,7 +102,7 @@ impl OrderedChannel { Ok(()) } - fn send_unconditional(&mut self, new_id: u64, packet: SecBuffer) -> Result<(), NetworkError> { + fn send_unconditional(&mut self, new_id: u64, packet: T) -> Result<(), NetworkError> { self.sink .unbounded_send(packet) .map_err(|err| NetworkError::Generic(err.to_string()))?; @@ -131,7 +131,7 @@ mod tests { async fn smoke_ordered() -> Result<(), Box> { citadel_logging::setup_log(); const COUNT: u8 = 100; - let (tx, mut rx) = unbounded(); + let (tx, mut rx) = unbounded::(); let mut ordered_channel = OrderedChannel::new(tx.clone()); let values_ordered = (0..COUNT) .map(|r| (r as _, SecBuffer::from(&[r] as &[u8]))) @@ -164,7 +164,7 @@ mod tests { async fn smoke_unordered() -> Result<(), Box> { citadel_logging::setup_log(); const COUNT: usize = 1000; - let (tx, mut rx) = unbounded(); + let (tx, mut rx) = unbounded::(); let mut ordered_channel = OrderedChannel::new(tx.clone()); let mut values_ordered = (0..COUNT) .map(|r| { @@ -206,7 +206,7 @@ mod tests { #[citadel_io::tokio::test] async fn smoke_unordered_concurrent() -> Result<(), Box> { const COUNT: usize = 10000; - let (tx, mut rx) = unbounded(); + let (tx, mut rx) = unbounded::(); let ordered_channel = OrderedChannel::new(tx.clone()); let mut values_ordered = (0..COUNT) .map(|r| { diff --git a/citadel_proto/src/proto/misc/session_security_settings.rs b/citadel_proto/src/proto/misc/session_security_settings.rs index 6b8cf6e09..e299c5ac3 100644 --- a/citadel_proto/src/proto/misc/session_security_settings.rs +++ b/citadel_proto/src/proto/misc/session_security_settings.rs @@ -27,6 +27,7 @@ //! - `error.rs`: Error handling use citadel_types::crypto::{CryptoParameters, SecrecyMode, SecurityLevel}; +use citadel_types::prelude::HeaderObfuscatorSettings; use citadel_types::proto::SessionSecuritySettings; #[derive(Default)] @@ -34,6 +35,7 @@ pub struct SessionSecuritySettingsBuilder { security_level: Option, secrecy_mode: Option, crypto_params: Option, + header_obfuscator_settings: Option, } impl SessionSecuritySettingsBuilder { @@ -76,12 +78,34 @@ impl SessionSecuritySettingsBuilder { self } + /// Default: HeaderObfuscatorSettings::Disabled + /// ``` + /// use citadel_proto::prelude::*; + /// // Assume `header_psk` is a pre-shared key for only the header block cipher. + /// // WARNING! This should NOT be derived from any secrets related to any other + /// // part of the session. IF you are unsure, do not use this feature or use + /// // `HeaderObfuscatorSettings::Enabled` to generate a random key at the beginning + /// // of the session + /// let header_psk = Uuid::new_v4(); + /// SessionSecuritySettingsBuilder::default() + /// .with_header_obfuscator_settings() + /// .build(); + /// ``` + pub fn with_header_obfuscator_settings( + mut self, + header_obfuscator_settings: impl Into, + ) -> Self { + self.header_obfuscator_settings = Some(header_obfuscator_settings.into()); + self + } + /// Constructs the [`SessionSecuritySettings`] pub fn build(self) -> Result { let settings = SessionSecuritySettings { - security_level: self.security_level.unwrap_or(SecurityLevel::Standard), - secrecy_mode: self.secrecy_mode.unwrap_or(SecrecyMode::BestEffort), + security_level: self.security_level.unwrap_or_default(), + secrecy_mode: self.secrecy_mode.unwrap_or_default(), crypto_params: self.crypto_params.unwrap_or_default(), + header_obfuscator_settings: self.header_obfuscator_settings.unwrap_or_default(), }; citadel_types::utils::validate_crypto_params(&settings.crypto_params)?; diff --git a/citadel_proto/src/proto/node.rs b/citadel_proto/src/proto/node.rs index 77a7786bd..6bcc5f355 100644 --- a/citadel_proto/src/proto/node.rs +++ b/citadel_proto/src/proto/node.rs @@ -26,6 +26,8 @@ //! - `KernelCommunicator`: Handles kernel message passing //! - `NetworkListener`: Manages network socket listeners //! + +use std::collections::HashMap; use std::io; use std::net::SocketAddr; use std::net::ToSocketAddrs; @@ -34,6 +36,7 @@ use std::sync::Arc; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::io::AsyncRead; +use citadel_io::Mutex; use citadel_types::crypto::SecurityLevel; use citadel_user::account_manager::AccountManager; use citadel_wire::exports::tokio_rustls::rustls::{pki_types, ClientConfig}; @@ -48,10 +51,12 @@ use netbeam::time_tracker::TimeTracker; use crate::constants::{MAX_OUTGOING_UNPROCESSED_REQUESTS, TCP_CONN_TIMEOUT}; use crate::error::NetworkError; use crate::functional::PairMap; -use crate::kernel::kernel_communicator::KernelAsyncCallbackHandler; +use crate::kernel::kernel_communicator::{ + KernelAsyncCallbackHandler, KernelAsyncCallbackHandlerInner, +}; use crate::kernel::kernel_executor::LocalSet; use crate::kernel::RuntimeFuture; -use crate::prelude::{DeleteObject, PreSharedKey, PullObject}; +use crate::prelude::{DeleteObject, PullObject}; use crate::proto::misc::net::{ DualListener, FirstPacket, GenericNetworkListener, GenericNetworkStream, TlsListener, }; @@ -61,25 +66,25 @@ use crate::proto::node_request::{ NodeRequest, PeerCommand, ReKey, RegisterToHypernode, SendObject, }; use crate::proto::node_result::{InternalServerError, NodeResult, SessionList}; -use crate::proto::outbound_sender::{unbounded, BoundedReceiver, BoundedSender, UnboundedSender}; +use crate::proto::outbound_sender::{BoundedReceiver, BoundedSender, UnboundedSender}; use crate::proto::packet_processor::includes::Duration; use crate::proto::peer::p2p_conn_handler::generic_error; use crate::proto::remote::{NodeRemote, Ticket}; -use crate::proto::session::{CitadelSession, HdpSessionInitMode}; +use crate::proto::session::{HdpSessionInitMode, ServerOnlySessionInitSettings}; use crate::proto::session_manager::CitadelSessionManager; pub type TlsDomain = Option; // The outermost abstraction for the networking layer. We use Rc to allow ensure single-threaded performance // by default, but settings can be changed in crate::macros::*. -define_outer_struct_wrapper!(Node, NodeInner, , ); +define_outer_struct_wrapper!(CitadelNode, CitadelNodeInner, , ); -/// Inner device for the [`Node`] -pub struct NodeInner { +/// Inner device for the [`CitadelNode`] +pub struct CitadelNodeInner { primary_socket: Option, /// Key: cid (to account for multiple clients from the same node) session_manager: CitadelSessionManager, - to_kernel: UnboundedSender, + to_kernel: UnboundedSender>, local_node_type: NodeType, // Applies only to listeners, not outgoing connections underlying_proto: ServerUnderlyingProtocol, @@ -88,26 +93,26 @@ pub struct NodeInner { client_config: Arc, // All connecting/registering clients must present this pre-shared password in order to register and connect // to the server. This is an additional security measure to prevent unauthorized connections. - server_only_c2s_session_password: PreSharedKey, + server_only_session_init_settings: Option, } -impl Node { - /// Creates a new [`Node`] +impl CitadelNode { + /// Creates a new [`CitadelNode`] #[allow(clippy::too_many_arguments)] pub(crate) async fn init( local_node_type: NodeType, - to_kernel: UnboundedSender, + to_kernel: UnboundedSender>, account_manager: AccountManager, shutdown: citadel_io::tokio::sync::oneshot::Sender<()>, underlying_proto: ServerUnderlyingProtocol, client_config: Option>, stun_servers: Option>, - server_only_c2s_session_password: Option, + server_only_session_init_settings: Option, ) -> io::Result<( NodeRemote, Pin>, Option, - KernelAsyncCallbackHandler, + KernelAsyncCallbackHandler, )> { let (primary_socket, bind_addr) = match local_node_type { NodeType::Server(bind_addr) => { @@ -147,7 +152,7 @@ impl Node { let nat_type = NatType::identify(stun_servers).await?; - let inner = NodeInner { + let inner = CitadelNodeInner { underlying_proto, local_node_type, primary_socket, @@ -155,28 +160,28 @@ impl Node { session_manager, nat_type, client_config, - server_only_c2s_session_password: server_only_c2s_session_password.unwrap_or_default(), + server_only_session_init_settings, }; let this = Self::from(inner); - Ok(Node::load(this, account_manager, shutdown)) + Ok(CitadelNode::load(this, account_manager, shutdown)) } /// Note: spawning via handle is more efficient than joining futures. Source: https://cafbit.com/post/tokio_internals/ /// To handle the shutdown process, we need /// This will panic if called twice in succession without a proper server reload. - /// Returns a handle to communicate with the [Node]. + /// Returns a handle to communicate with the [CitadelNode]. #[allow(clippy::type_complexity)] #[allow(unused_results, unused_must_use)] fn load( - this: Node, + this: CitadelNode, account_manager: AccountManager, shutdown: citadel_io::tokio::sync::oneshot::Sender<()>, ) -> ( NodeRemote, Pin>, Option, - KernelAsyncCallbackHandler, + KernelAsyncCallbackHandler, ) { // Allow the listeners to read data without instantly returning // Load the readers @@ -186,13 +191,12 @@ impl Node { let kernel_tx = read.to_kernel.clone(); let node_type = read.local_node_type; - let (session_spawner_tx, session_spawner_rx) = unbounded(); - let session_spawner = CitadelSession::::session_future_receiver(session_spawner_rx); - let (outbound_send_request_tx, outbound_send_request_rx) = BoundedSender::new(MAX_OUTGOING_UNPROCESSED_REQUESTS); // for the Hdp remote let kernel_async_callback_handler = KernelAsyncCallbackHandler { - inner: Default::default(), + inner: Arc::new(Mutex::new(KernelAsyncCallbackHandlerInner { + map: HashMap::new(), + })), }; let remote = NodeRemote::new( outbound_send_request_tx, @@ -204,7 +208,10 @@ impl Node { .session_manager .load_server_remote_get_tt(remote.clone()); let session_manager = read.session_manager.clone(); - let server_only_session_password = read.server_only_c2s_session_password.clone(); + let server_only_session_settings = read + .server_only_session_init_settings + .clone() + .unwrap_or_default(); drop(read); @@ -220,15 +227,13 @@ impl Node { this.clone(), kernel_tx.clone(), outbound_send_request_rx, - session_spawner_tx.clone(), ); let primary_stream_listener = if node_type.is_server() { Some(Self::listen_primary( this.clone(), tt, kernel_tx.clone(), - server_only_session_password, - session_spawner_tx.clone(), + server_only_session_settings, )) } else { None @@ -250,15 +255,13 @@ impl Node { this.clone(), kernel_tx.clone(), outbound_send_request_rx, - session_spawner_tx.clone(), ); let primary_stream_listener = if node_type.is_server() { Some(Self::listen_primary( this.clone(), tt, kernel_tx.clone(), - server_only_session_password, - session_spawner_tx.clone(), + server_only_session_settings, )) } else { None @@ -283,7 +286,6 @@ impl Node { res1 = primary_stream_listener => res1, res2 = peer_container => res2, - res3 = session_spawner => res3 } } else { citadel_io::tokio::select! { @@ -293,7 +295,6 @@ impl Node { } res1 = peer_container => res1, - res2 = session_spawner => res2 } }; @@ -622,11 +623,10 @@ impl Node { /// will need to be created that is bound to the local primary port and connected to the adjacent hypernode's /// primary port. That socket will be created in the underlying HdpSessionManager during the connection process async fn listen_primary( - server: Node, + server: CitadelNode, _tt: TimeTracker, - to_kernel: UnboundedSender, - server_only_session_password: PreSharedKey, - session_spawner: UnboundedSender>>, + to_kernel: UnboundedSender>, + server_only_session_init_settings: ServerOnlySessionInitSettings, ) -> Result<(), NetworkError> { let primary_port_future = { let mut this = inner_mut!(server); @@ -639,8 +639,7 @@ impl Node { local_nat_type, session_manager, listener, - server_only_session_password, - session_spawner, + server_only_session_init_settings, ) }; @@ -648,12 +647,11 @@ impl Node { } async fn primary_session_creator_loop( - to_kernel: UnboundedSender, + to_kernel: UnboundedSender>, local_nat_type: NatType, session_manager: CitadelSessionManager, mut socket: DualListener, - server_session_password: PreSharedKey, - session_spawner: UnboundedSender>>, + server_only_session_init_settings: ServerOnlySessionInitSettings, ) -> Result<(), NetworkError> { loop { match socket.next().await { @@ -668,12 +666,10 @@ impl Node { local_nat_type.clone(), peer_addr, stream, - server_session_password.clone(), + server_only_session_init_settings.clone(), ) { Ok(session) => { - session_spawner - .unbounded_send(session) - .map_err(|err| NetworkError::Generic(err.to_string()))?; + spawn!(session); } Err(err) => { @@ -706,10 +702,9 @@ impl Node { } async fn outbound_kernel_request_handler( - this: Node, - to_kernel_tx: UnboundedSender, + this: CitadelNode, + to_kernel_tx: UnboundedSender>, mut outbound_send_request_rx: BoundedReceiver<(NodeRequest, Ticket)>, - session_spawner: UnboundedSender>>, ) -> Result<(), NetworkError> { let ( local_node_type, @@ -794,9 +789,7 @@ impl Node { .await { Ok(session) => { - session_spawner - .unbounded_send(session) - .map_err(|err| NetworkError::Generic(err.to_string()))?; + spawn!(session); } Err(err) => { @@ -830,9 +823,7 @@ impl Node { .await { Ok(session) => { - session_spawner - .unbounded_send(session) - .map_err(|err| NetworkError::Generic(err.to_string()))?; + spawn!(session); } Err(err) => { @@ -964,7 +955,7 @@ impl Node { } pub(crate) struct CitadelNodeRemoteInner { - pub callback_handler: KernelAsyncCallbackHandler, + pub callback_handler: KernelAsyncCallbackHandler, pub node_type: NodeType, pub account_manager: AccountManager, } diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs index 1d8937361..29e813813 100644 --- a/citadel_proto/src/proto/node_request.rs +++ b/citadel_proto/src/proto/node_request.rs @@ -33,6 +33,7 @@ use citadel_types::proto::TransferType; use citadel_types::proto::{ConnectMode, SessionSecuritySettings, UdpMode}; use citadel_user::auth::proposed_credentials::ProposedCredentials; use serde::{Deserialize, Serialize}; +use sha3::Digest; use std::fmt::{Debug, Formatter}; use std::net::SocketAddr; use std::path::PathBuf; @@ -170,8 +171,9 @@ impl PreSharedKey { /// must have matching passwords in order to establish a connection. /// Note: The password is hashed using SHA-256 before being added to the list to increase security. pub fn add_password>(mut self, password: T) -> Self { - self.passwords - .push(sha256::digest(password.as_ref()).into_bytes()); + let mut hasher = sha3::Sha3_256::default(); + hasher.update(password.as_ref()); + self.passwords.push(hasher.finalize().to_vec()); self } } diff --git a/citadel_proto/src/proto/node_result.rs b/citadel_proto/src/proto/node_result.rs index c7303b333..eaf7b597e 100644 --- a/citadel_proto/src/proto/node_result.rs +++ b/citadel_proto/src/proto/node_result.rs @@ -32,6 +32,8 @@ use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualConnectionType; use crate::kernel::kernel_communicator::CallbackKey; +use citadel_crypt::prelude::CryptError; +use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::SessionSecuritySettings; use citadel_user::backend::utils::ObjectTransferHandler; use std::net::SocketAddr; @@ -58,7 +60,7 @@ pub struct DeRegistration { } #[derive(Debug)] -pub struct ConnectSuccess { +pub struct ConnectSuccess { pub ticket: Ticket, pub session_cid: u64, pub remote_addr: SocketAddr, @@ -66,8 +68,8 @@ pub struct ConnectSuccess { pub v_conn_type: VirtualConnectionType, pub services: citadel_user::external_services::ServicesObject, pub welcome_message: String, - pub channel: PeerChannel, - pub udp_rx_opt: Option>, + pub channel: PeerChannel, + pub udp_rx_opt: Option>>, pub session_security_settings: SessionSecuritySettings, } @@ -89,7 +91,7 @@ pub struct ReKeyResult { pub enum ReKeyReturnType { Success { version: u32 }, AlreadyInProgress, - Failure, + Failure { err: CryptError }, } #[derive(Debug)] @@ -150,10 +152,10 @@ pub struct InternalServerError { } #[derive(Debug)] -pub struct PeerChannelCreated { +pub struct PeerChannelCreated { pub ticket: Ticket, - pub channel: PeerChannel, - pub udp_rx_opt: Option>, + pub channel: PeerChannel, + pub udp_rx_opt: Option>>, } #[derive(Debug)] @@ -172,7 +174,7 @@ pub struct ReVFSResult { /// This type is for relaying results between the lower-level protocol and the higher-level kernel #[derive(Debug)] -pub enum NodeResult { +pub enum NodeResult { /// Returns the CNAC which was created during the registration process RegisterOkay(RegisterOkay), /// The registration was a failure @@ -180,7 +182,7 @@ pub enum NodeResult { /// When de-registration occurs. Third is_personal, Fourth is true if success, false otherwise DeRegistration(DeRegistration), /// Connection succeeded for the cid self.0. bool is "is personal" - ConnectSuccess(ConnectSuccess), + ConnectSuccess(ConnectSuccess), /// The connection was a failure ConnectFail(ConnectFail), ReKeyResult(ReKeyResult), @@ -202,14 +204,14 @@ pub enum NodeResult { /// An internal error occurred InternalServerError(InternalServerError), /// A channel was created, with channel_id = ticket (same as post-connect ticket received) - PeerChannelCreated(PeerChannelCreated), + PeerChannelCreated(PeerChannelCreated), /// A list of running sessions SessionList(SessionList), /// For shutdowns Shutdown, } -impl NodeResult { +impl NodeResult { pub fn is_connect_success_type(&self) -> bool { matches!(self, NodeResult::ConnectSuccess(ConnectSuccess { .. })) } diff --git a/citadel_proto/src/proto/packet.rs b/citadel_proto/src/proto/packet.rs index b86c8005d..8a5171116 100644 --- a/citadel_proto/src/proto/packet.rs +++ b/citadel_proto/src/proto/packet.rs @@ -30,10 +30,22 @@ //! * `state_container`: Manages packet state //! * `session`: Handles packet sessions //! + use crate::constants::HDP_HEADER_BYTE_LEN; -use bytes::{BufMut, Bytes, BytesMut}; +use crate::error::NetworkError; +use crate::proto::misc::dual_cell::DualCell; +use byteorder::WriteBytesExt; +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use citadel_io as rand; +use citadel_io::RngCore; +use citadel_types::crypto::HeaderObfuscatorSettings; +use rand::Rng; +use rand::ThreadRng; +use sha3::Digest; use std::net::SocketAddr; +use std::num::NonZero; use zerocopy::byteorder::big_endian::{I64, U128, U32, U64}; +use zerocopy::BigEndian; use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref, Unaligned}; pub(crate) mod packet_flags { @@ -46,13 +58,12 @@ pub(crate) mod packet_flags { pub(crate) const GROUP_PACKET: u8 = 2; pub(crate) const DO_REGISTER: u8 = 3; pub(crate) const DO_DISCONNECT: u8 = 4; - pub(crate) const DO_DRILL_UPDATE: u8 = 5; - pub(crate) const DO_DEREGISTER: u8 = 6; - pub(crate) const DO_PRE_CONNECT: u8 = 7; - pub(crate) const PEER_CMD: u8 = 8; - pub(crate) const FILE: u8 = 9; - pub(crate) const UDP: u8 = 10; - pub(crate) const HOLE_PUNCH: u8 = 11; + pub(crate) const DO_DEREGISTER: u8 = 5; + pub(crate) const DO_PRE_CONNECT: u8 = 6; + pub(crate) const PEER_CMD: u8 = 7; + pub(crate) const FILE: u8 = 8; + pub(crate) const UDP: u8 = 9; + pub(crate) const HOLE_PUNCH: u8 = 10; } pub(crate) mod aux { @@ -91,14 +102,6 @@ pub(crate) mod packet_flags { pub(crate) const FINAL: u8 = 1; } - pub(crate) mod do_stacked_ratchet_update { - pub(crate) const STAGE0: u8 = 0; - pub(crate) const STAGE1: u8 = 1; - - pub(crate) const TRUNCATE: u8 = 2; - pub(crate) const TRUNCATE_ACK: u8 = 3; - } - pub(crate) mod do_deregister { /// request pub(crate) const STAGE0: u8 = 0; @@ -166,12 +169,6 @@ pub(crate) mod packet_sizes { /// Group packets pub(crate) const GROUP_HEADER_BASE_LEN: usize = HDP_HEADER_BYTE_LEN + 1; pub(crate) const GROUP_HEADER_ACK_LEN: usize = HDP_HEADER_BYTE_LEN + 1 + 1 + 4 + 4; - - pub(crate) mod do_entropy_bank_update { - use crate::constants::HDP_HEADER_BYTE_LEN; - - pub(crate) const STAGE1: usize = HDP_HEADER_BYTE_LEN + HDP_HEADER_BYTE_LEN; - } } #[derive(Debug, FromZeroes, AsBytes, FromBytes, Unaligned, Clone)] @@ -240,6 +237,10 @@ impl HdpPacket { } } + pub(crate) fn as_bytes(&self) -> &[u8] { + self.packet.as_ref() + } + /// Parses the zerocopy header pub fn parse(&self) -> Option { Ref::new_from_prefix(self.packet.as_ref()) @@ -257,7 +258,7 @@ impl HdpPacket { /// Splits the header's bytes and the header's in Bytes/Mut form pub fn decompose(mut self) -> (B::Immutable, B, SocketAddr, u16) { - let header_bytes = self.packet.split_to(HDP_HEADER_BYTE_LEN).freeze(); + let header_bytes = self.packet.split_to(HDP_HEADER_BYTE_LEN).to_immutable(); let payload_bytes = self.packet; let remote_peer = self.remote_peer; let local_port = self.local_port; @@ -266,120 +267,226 @@ impl HdpPacket { } } -/* +/// Provides random obfuscation to the header of a packet at the start +/// of each session using a key. Helps render deep packet inspection more challenging. +/// +/// This was inspired by using XOR-mapped addresses in NAT-traversal packets +/// to help fool firewalls from extracting network addresses and interfering with connections. +/// +/// As such, the key should not be a real secret nor derived from a secret, but rather, +/// something that can be transferred in the plain and appear pseudorandom to deep packet inspectors. +/// +/// For any given pair of two connnected nodes, one will have to take the +/// "server" role, and the other will take the "client" role. +/// +/// If the "server" has a pre-shared key, it will use that key to obfuscate the header. +/// This implies the client must have a matching key. +/// +/// If the client enabled the header obfuscator without a pre-shared key, the client +/// will generate a random "first packet" that the server will read, interpret, then +/// use for the remainder of the session. +/// +/// If the client disables the header obfuscator, the server will also not obfuscate the contents +/// of the packet (unless the server has a pre-shared key set). +/// +/// Valid combinations: (server = PSK, client = PSK), (server = enabled w/no PSK or off, client = enabled w/no PSK or off) +/// Invalid combinations: (server = PSK, client = off), (server = off, client = PSK) +/// +/// For invalid packet inputs, the obfuscator will fail silently to not interrupt network traffic. +/// Packets that are too small will be ignored. +/// Packets that arrive with an invalid key will be ignored +/// +/// The obfuscator will load the key from the first packet and then store it for the remainder of the session. If there +/// is a pre-existing key, the obfuscator will compare keys, and error if mismatching. #[derive(Clone)] pub struct HeaderObfuscator { - inner: DualCell> + inner: DualCell>>, + pub first_packet: Option, + expected_key: Option>, + disabled: DualCell, + client_intends_disable: DualCell, } +const DISABLED_KEY: u128 = u128::MAX; + impl HeaderObfuscator { - pub fn new(is_server: bool) -> (Self, Option) { + pub fn new(is_server: bool, header_obfuscator_settings: HeaderObfuscatorSettings) -> Self { if is_server { - (Self::new_server(), None) + Self::new_server(header_obfuscator_settings) } else { - Self::new_client() - .map_right(Some) + Self::new_client(header_obfuscator_settings) } } - pub fn on_packet_received(&self, packet: &mut BytesMut) -> Option<()> { - //log::trace!(target: "citadel", "[Header-scrambler] RECV {:?}", &packet[..]); + /// Returns Ok(true) if the packet can be processed by the downstream application + /// Returns Ok(false) if the packet is either frivolous, invalid, or an initial handshake packet + pub fn on_packet_received(&self, packet: &mut BytesMut) -> Result { + if self.is_disabled() { + return Ok(true); + } // disabled + if let Some(val) = self.load() { - //log::trace!(target: "citadel", "[Header-scrambler] received ordinary packet"); + if packet.len() < HDP_HEADER_BYTE_LEN { + log::warn!(target: "citadel", "[Header Obfuscator] Packet too small: {}", packet.len()); + return Ok(false); + } + + log::trace!(target: "citadel", "[Header Obfuscator] Applying inbound cipher w/key {val}"); apply_cipher(val, true, packet); - Some(()) - } else { - if packet.len() >= 16 && packet.len() < HDP_HEADER_BYTE_LEN { - //log::trace!(target: "citadel", "[Header-Scrambler] Loading first-time packet {:?}", &packet[..]); - // we are only interested in taking the first 16 bytes - let val0 = packet.get_u64(); - let val1 = packet.get_u64(); - self.store(val0, val1); - log::trace!(target: "citadel", "[Header obfuscator] initial packet set"); - } else { - log::error!(target: "citadel", "Discarding invalid packet (LEN: {})", packet.len()); + Ok(true) + } else if packet.len() >= 16 { + // We are only interested in taking the first 16 bytes + let key = packet.get_u128(); + + if key == 0 { + log::error!(target: "citadel", "[Header Obfuscator] Invalid first packet key == 0"); + return Err(NetworkError::msg("Invalid first packet key")); } - None + if let Some(expected_key) = self.expected_key { + if key != expected_key.get() { + log::error!(target: "citadel", "[Header Obfuscator] Invalid first packet key {key} != {expected_key}"); + return Err(NetworkError::msg("Invalid first packet key")); + } + } + + if key == DISABLED_KEY { + log::trace!(target: "citadel", "[Header Obfuscator] Disabling obfuscator at client's request"); + self.disabled.set(true); + self.client_intends_disable.set(true); + return Ok(false); + } + + self.store(key); + log::trace!(target: "citadel", "[Header Obfuscator] initial packet set to {key}"); + Ok(false) + } else { + log::warn!(target: "citadel", "[Header Obfuscator] Packet too small (skipping): {}", packet.len()); + Ok(false) } } /// This will only obfuscate packets that are at least HDP_HEADER_BYTE_LEN pub fn prepare_outbound(&self, mut packet: BytesMut) -> Bytes { - if packet.len() >= HDP_HEADER_BYTE_LEN { - //log::trace!(target: "citadel", "[Header-scrambler] Before: {:?}", &packet[..]); - // it is assumed that the value is already loaded - let val = self.load().unwrap(); - apply_cipher(val, false, &mut packet); - //log::trace!(target: "citadel", "[Header-scrambler] After: {:?}", &packet[..]); + if self.client_intends_disable.get() && self.disabled.get() { + return packet.freeze(); + } + + if let Some(key) = self.load() { + if packet.len() >= HDP_HEADER_BYTE_LEN { + log::trace!(target: "citadel", "[Header Obfuscator] Applying outbound cipher w/key {key}"); + apply_cipher(key, false, &mut packet); + + if self.client_intends_disable.get() { + // Prevent further use of the obfuscator + self.disabled.set(true); + } + } } packet.freeze() } /// Returns to the client an instance of self coupled with the required init packet - pub fn new_client() -> (Self, BytesMut) { - let mut rng = ThreadRng::default(); - let mut fill0 = [0u8; 8]; - let mut fill1 = [0u8; 8]; + pub fn new_client(header_obfuscator_settings: HeaderObfuscatorSettings) -> Self { + let key = match header_obfuscator_settings { + HeaderObfuscatorSettings::Enabled => rand::random::(), + HeaderObfuscatorSettings::Disabled => { + let mut disabled_packet = BytesMut::with_capacity(16); + disabled_packet.put_u128(DISABLED_KEY); + return Self { + inner: None.into(), + first_packet: Some(disabled_packet), + expected_key: None, + disabled: true.into(), + client_intends_disable: false.into(), + }; + } + HeaderObfuscatorSettings::EnabledWithKey(key) => key, + }; - rng.fill(&mut fill0); - rng.fill(&mut fill1); + let key = hash_u128(key); - let val0 = u64::from_be_bytes(fill0); - let val1 = u64::from_be_bytes(fill1); - //log::trace!(target: "citadel", "[header-scrambler] {} -> {:?} | {} -> {:?}", val0, &fill0, val1, &fill1); - // we have 16 bytes used. Now, choose a random number of bytes between 0 and HDP_HEADER_BYTE_LEN - 16 to fill - let bytes_to_add = rng.gen_range(0, HDP_HEADER_BYTE_LEN - 17); + let mut rng = ThreadRng::default(); + let bytes_to_add = rng.gen_range(0..(HDP_HEADER_BYTE_LEN - 17)); let mut packet = vec![0; 16 + bytes_to_add]; let tmp = &mut packet[..]; let mut tmp = tmp.writer(); - tmp.write_all(&fill0 as &[u8]).unwrap(); - tmp.write_all(&fill1 as &[u8]).unwrap(); + tmp.write_u128::(key).expect("Should not fail"); rng.fill_bytes(&mut packet[16..]); - //log::trace!(target: "citadel", "[Header-scrambler] Prepared packet: {:?}", &packet[..]); - let packet = BytesMut::from(&packet[..]); - let this = Self::new_from_u64s(val0, val1); - (this, packet) + let first_packet = Some(BytesMut::from(&packet[..])); + Self { + inner: DualCell::from(Some(NonZero::new(key).expect("Hashed key cannot be zero"))), + first_packet, + expected_key: None, + disabled: false.into(), + client_intends_disable: false.into(), + } } - pub fn new_server() -> Self { - Self::from(None) - } + pub fn new_server(header_obfuscator_settings: HeaderObfuscatorSettings) -> Self { + let (inner, expected_key) = match header_obfuscator_settings { + HeaderObfuscatorSettings::Enabled => (DualCell::from(None), None), // Wait for client to set key value + HeaderObfuscatorSettings::Disabled => (DualCell::from(None), None), // No obfuscation; up to the client to enable it + HeaderObfuscatorSettings::EnabledWithKey(key) => { + // Obfuscation is enabled with a pre-shared key + let key = NonZero::new(hash_u128(key)).expect("Hashed key cannot be zero"); + (DualCell::from(Some(key)), Some(key)) + } + }; - fn store(&self, val0: u64, val1: u64) { - self.inner.set(Some(u64s_to_u128(val0, val1))); + Self { + inner, + first_packet: None, + expected_key, + disabled: false.into(), // Let the client enable or disable + client_intends_disable: false.into(), // Let the client enable or disable + } } - fn new_from_u64s(val0: u64, val1: u64) -> Self { - Self::from(Some(u64s_to_u128(val0, val1))) + fn store(&self, key: u128) { + let key = NonZero::new(key).expect("Input key cannot be zero"); + self.inner.set(Some(key)); } fn load(&self) -> Option { - self.inner.get() + Some(self.inner.get()?.get()) } -} -fn u64s_to_u128(val0: u64, val1: u64) -> u128 { - let mut ret = [0u8; 16]; - let val0_bytes = val0.to_be_bytes(); - let val1_bytes = val1.to_be_bytes(); - for x in 0..8 { - ret[x] = val0_bytes[x]; - ret[x + 8] = val1_bytes[x]; + fn is_disabled(&self) -> bool { + self.disabled.get() } +} - u128::from_be_bytes(ret) +fn hash_u128(key: u128) -> u128 { + let mut hasher = sha3::Sha3_256::default(); + hasher.update(key.to_be_bytes()); + let out: [u8; 32] = hasher.finalize().into(); + let slice: [u8; 16] = out[0..16].try_into().unwrap(); + u128::from_be_bytes(slice) } -/// panics if packet is not of proper length +/// # Safety +/// This is NOT a cryptographically-secure cipher since its inverse is relatively trivial: +/// +/// C' = ((C + A) XOR B) mod 2^8 +/// C = ((C' XOR B) - A) mod 2^8 +/// The purpose of this is to obfuscate the header to make deep packet inspection +/// more challenging while providing minimal overhead. As such, the inputs should +/// be data that is acceptable to be plaintext, not ciphertext. +/// +/// # Panics +/// If packet is not of proper length #[inline] fn apply_cipher(val: u128, inverse: bool, packet: &mut BytesMut) { - let ref bytes = val.to_be_bytes(); + let bytes = val.to_be_bytes(); let (bytes0, bytes1) = bytes.split_at(8); - let packet = &mut packet[..HDP_HEADER_BYTE_LEN]; - bytes0.iter().zip(bytes1.iter()) + let packet_len = packet.len().min(HDP_HEADER_BYTE_LEN); + let packet = &mut packet[..packet_len]; + bytes0 + .iter() + .zip(bytes1.iter()) .cycle() .zip(packet.iter_mut()) .for_each(|((a, b), c)| cipher_inner(*a, *b, c, inverse)) @@ -394,19 +501,11 @@ fn cipher_inner(a: u8, b: u8, c: &mut u8, inverse: bool) { } } - -impl From> for HeaderObfuscator { - fn from(inner: Option) -> Self { - Self { inner: DualCell::from(inner) } - } -} -*/ - pub trait HdpBuffer: BufMut + AsRef<[u8]> + AsMut<[u8]> { type Immutable; fn len(&self) -> usize; fn split_to(&mut self, idx: usize) -> Self; - fn freeze(self) -> Self::Immutable; + fn to_immutable(self) -> Self::Immutable; } impl HdpBuffer for BytesMut { @@ -420,27 +519,357 @@ impl HdpBuffer for BytesMut { self.split_to(idx) } - fn freeze(self) -> Self::Immutable { + fn to_immutable(self) -> Self::Immutable { self.freeze() } } impl HdpBuffer for Vec { - type Immutable = Self; + type Immutable = Vec; fn len(&self) -> usize { self.len() } - // return [0, idx), leave self with [idx, len) fn split_to(&mut self, idx: usize) -> Self { - let mut tail = self.split_off(idx); - // swap head into tail - std::mem::swap(self, &mut tail); + let tail = self[..idx].to_vec(); + self.copy_within(idx.., 0); + self.truncate(self.len() - idx); tail // now, tail is the head } - fn freeze(self) -> Self::Immutable { + fn to_immutable(self) -> Self::Immutable { self } } + +#[cfg(test)] +mod tests { + use super::*; + use bytes::BytesMut; + use citadel_types::crypto::HeaderObfuscatorSettings; + + #[test] + fn test_header_obfuscator_client_server_interaction() { + // Test client initialization + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::Enabled); + assert!( + client.first_packet.is_some(), + "Client should have initial packet" + ); + assert!( + client.expected_key.is_none(), + "Client should not have expected key initially" + ); + + // Test server initialization + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Enabled); + assert!( + server.first_packet.is_none(), + "Server should not have initial packet" + ); + assert!( + server.expected_key.is_none(), + "Server should not have expected key initially" + ); + } + + #[test] + fn test_header_obfuscator_key_exchange() { + // Create client and server + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::Enabled); + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Enabled); + + // Get first packet from client + let mut first_packet = client.first_packet.as_ref().unwrap().clone(); + + // Server should process first packet successfully + assert!(server.on_packet_received(&mut first_packet).is_ok()); + + // Server should now have the same key as client + assert_eq!(server.load(), client.load()); + assert!(server.load().is_some(), "Both should have non-None key"); + + // Create and process a test packet + let mut test_packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); + test_packet.resize(HDP_HEADER_BYTE_LEN, 1); // Fill with 1's + + // Process packet through client and server + let client_processed = client.prepare_outbound(test_packet.clone()); + let mut server_packet = BytesMut::from(&client_processed[..]); + + // Server should process the packet successfully + assert!(server.on_packet_received(&mut server_packet).is_ok()); + } + + #[test] + fn test_header_obfuscator_disabled() { + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::Disabled); + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Disabled); + + // Both should have no key and no first packet + assert!(client.load().is_none()); + assert!(server.load().is_none()); + assert!(client.first_packet.is_some()); // Contains to null packet designed for disabling use + assert!(server.first_packet.is_none()); + } + + #[test] + fn test_header_obfuscator_small_packet_ignores() { + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::EnabledWithKey(12345)); + + // Test packet smaller than 16 bytes + let mut small_packet = BytesMut::with_capacity(16); + small_packet.resize(15, 1); + let initial_small_packet = small_packet.clone(); + assert!( + server.on_packet_received(&mut small_packet).is_ok(), + "Packets that are smaller than 16 bytes will just be skipped" + ); + assert_eq!( + initial_small_packet, small_packet, + "Packets that are smaller than 16 bytes should not be modified" + ); + + // Test empty packet + let mut empty_packet = BytesMut::new(); + let initial_empty_packet = empty_packet.clone(); + assert!( + server.on_packet_received(&mut empty_packet).is_ok(), + "Empty packets should be skipped" + ); + assert_eq!( + initial_empty_packet, empty_packet, + "Empty packets should not be modified" + ); + } + + #[test] + fn test_header_obfuscator_invalid_keys() { + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::EnabledWithKey(12345)); + + // Test packet with zero key + let mut zero_key_packet = BytesMut::with_capacity(16); + zero_key_packet.put_u128(0); + assert_eq!(zero_key_packet.len(), 16); + assert!( + server.on_packet_received(&mut zero_key_packet).is_ok(), + "Should silently ignore packet with zero key" + ); + + // Test packet with invalid key + let mut invalid_key_packet = BytesMut::with_capacity(16); + invalid_key_packet.put_u128(54321); // Different from server's key + assert!( + server.on_packet_received(&mut invalid_key_packet).is_ok(), + "Should ignore packet with mismatched key" + ); + } + + #[test] + fn test_header_obfuscator_invalid_keys_no_preset_server_value() { + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Enabled); + + // Test packet with zero key + let mut zero_key_packet = BytesMut::with_capacity(16); + zero_key_packet.put_u128(0); + assert!( + server.on_packet_received(&mut zero_key_packet).is_err(), + "Should error on packet with zero key" + ); + assert!(server.load().is_none(), "Server should have no key until the client sends a valid key since the server has no initial key"); + + let mut good_first_packet = BytesMut::with_capacity(16); + good_first_packet.put_u128(12345); + assert!( + server.on_packet_received(&mut good_first_packet).is_ok(), + "Should accept packet with valid key" + ); + + // Test packet with invalid key + let mut invalid_key_packet = BytesMut::with_capacity(16); + invalid_key_packet.put_u128( + server + .load() + .expect("Server should have key") + .wrapping_add(1), + ); // Different from server's key + assert!( + server.on_packet_received(&mut invalid_key_packet).is_ok(), + "Should ignore packet with mismatched key" + ); + } + + #[test] + fn test_header_obfuscator_disabled_behavior() { + let disabled_server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Disabled); + let disabled_client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::Disabled); + + // Create test packets + let mut small_packet = BytesMut::with_capacity(8); + small_packet.resize(8, 1); + let initial_small = small_packet.clone(); + + let mut full_packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); + full_packet.resize(HDP_HEADER_BYTE_LEN, 2); + let initial_full = full_packet.clone(); + + // Test that disabled obfuscator doesn't modify any packets + assert!(disabled_server + .on_packet_received(&mut small_packet) + .is_ok()); + assert!(disabled_client.on_packet_received(&mut full_packet).is_ok()); + assert_eq!( + initial_small, small_packet, + "Disabled obfuscator should not modify small packets" + ); + assert_eq!( + initial_full, full_packet, + "Disabled obfuscator should not modify full packets" + ); + } + + #[test] + fn test_header_obfuscator_key_exchange_flow() { + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::Enabled); + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Enabled); + + // Get first packet from client + let mut first_packet = client.first_packet.as_ref().unwrap().clone(); + + // Server should process first packet successfully + assert!(server.on_packet_received(&mut first_packet).is_ok()); + assert_ne!( + first_packet, + client.first_packet.as_ref().unwrap().clone(), + "First packet should be modified by server" + ); + + // Both should now have same key + assert_eq!(server.load(), client.load()); + assert!(server.load().is_some(), "Both should have non-None key"); + + // Create and process a test packet + let mut test_packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); + test_packet.resize(HDP_HEADER_BYTE_LEN, 3); + let initial_test = test_packet.clone(); + + // Process packet through client and server + let client_processed = client.prepare_outbound(test_packet.clone()); + let mut server_packet = BytesMut::from(&client_processed[..]); + + // Server should process the packet successfully + assert!(server.on_packet_received(&mut server_packet).is_ok()); + assert_eq!( + server_packet, initial_test, + "Server should decrypt to original packet" + ); + + // Server -> Client + let server_processed = server.prepare_outbound(test_packet.clone()); + let mut client_packet = BytesMut::from(&server_processed[..]); + + // Client should process the packet successfully + assert!(client.on_packet_received(&mut client_packet).is_ok()); + assert_eq!( + client_packet, initial_test, + "Client should decrypt to original packet" + ); + } + + #[test] + fn test_header_obfuscator_preshared_key() { + let psk = 12345u128; + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::EnabledWithKey(psk)); + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::EnabledWithKey(psk)); + + // Both should have the same hashed key + assert_eq!(client.load(), server.load()); + assert!(client.load().is_some()); + + // Create and process a test packet + let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); + packet.resize(HDP_HEADER_BYTE_LEN, 1); // Fill with 1's + + // Process packet through client and server + let client_processed = client.prepare_outbound(packet.clone()); + let mut server_packet = BytesMut::from(&client_processed[..]); + + // Server should process the packet successfully + assert!(server.on_packet_received(&mut server_packet).is_ok()); + } + + #[test] + fn test_header_obfuscator_mismatched_psk() { + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::EnabledWithKey(12345)); + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::EnabledWithKey(54321)); + + // Create a test packet + let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); + packet.resize(HDP_HEADER_BYTE_LEN, 0); + + // Packets should be processed differently due to different keys + let client_processed = client.prepare_outbound(packet.clone()); + let server_processed = server.prepare_outbound(packet.clone()); + assert_ne!(client_processed[..], server_processed[..]); + } + + #[test] + fn test_header_obfuscator_key_validation() { + // Test with pre-shared key enabled + let mut server = + HeaderObfuscator::new_server(HeaderObfuscatorSettings::EnabledWithKey(12345)); + + // Test packet with mismatched key (should fail) + let mut invalid_key_packet = BytesMut::with_capacity(16); + invalid_key_packet.put_u128(54321); // Different from server's key + assert!( + server.on_packet_received(&mut invalid_key_packet).is_ok(), + "Should silently accept packet with mismatched key" + ); + + // Test packet with valid key but too small (should be ignored) + let mut small_valid_key = BytesMut::with_capacity(16); + small_valid_key.put_u128(12345); + assert!( + server.on_packet_received(&mut small_valid_key).is_ok(), + "Should accept packet with valid key even if small" + ); + + // Test with no pre-shared key + server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Enabled); + + // Test packet with any non-zero key (should succeed) + let mut valid_key_packet = BytesMut::with_capacity(16); + valid_key_packet.put_u128(54321); + assert!( + server.on_packet_received(&mut valid_key_packet).is_ok(), + "Should accept any non-zero key when no PSK" + ); + } + + #[test] + fn test_header_obfuscator_psk_mismatch_modes() { + // Test client with PSK connecting to server without PSK + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::EnabledWithKey(12345)); + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::Enabled); + + // Get first packet from client + let mut first_packet = client.first_packet.as_ref().unwrap().clone(); + + // Server should accept the packet since it has no PSK expectations + assert!(server.on_packet_received(&mut first_packet).is_ok()); + assert!(server.load().is_some()); + + // Test server with PSK receiving from client without PSK + let client = HeaderObfuscator::new_client(HeaderObfuscatorSettings::Enabled); + let server = HeaderObfuscator::new_server(HeaderObfuscatorSettings::EnabledWithKey(12345)); + + // Get first packet from client + let mut first_packet = client.first_packet.as_ref().unwrap().clone(); + + // Server should silently ignore the packet since it doesn't match PSK + assert!(server.on_packet_received(&mut first_packet).is_ok()); + assert_eq!(server.load().unwrap(), hash_u128(12345)); + } +} diff --git a/citadel_proto/src/proto/packet_crafter.rs b/citadel_proto/src/proto/packet_crafter.rs index adbaafbfd..4ef6fb390 100644 --- a/citadel_proto/src/proto/packet_crafter.rs +++ b/citadel_proto/src/proto/packet_crafter.rs @@ -43,85 +43,19 @@ //! 3. Security level enforcement //! 4. Timestamp and sequence number management -use bytes::BytesMut; - -use citadel_crypt::scramble::crypt_splitter::{GroupReceiverConfig, GroupSenderDevice}; -use citadel_types::crypto::SecurityLevel; -use netbeam::time_tracker::TimeTracker; - use crate::constants::HDP_HEADER_BYTE_LEN; use crate::error::NetworkError; use crate::proto::outbound_sender::OutboundPrimaryStreamSender; use crate::proto::remote::Ticket; +use crate::proto::session::UserMessage; use crate::proto::state_container::VirtualTargetType; +use bytes::BytesMut; +use citadel_crypt::ratchets::ratchet_manager::RatchetMessage; use citadel_crypt::ratchets::Ratchet; -use citadel_crypt::scramble::crypt_splitter::oneshot_unencrypted_group_unified; -use citadel_crypt::secure_buffer::sec_packet::SecureMessagePacket; +use citadel_crypt::scramble::crypt_splitter::{GroupReceiverConfig, GroupSenderDevice}; +use citadel_types::crypto::SecurityLevel; use citadel_types::prelude::ObjectId; - -/// A secure packet container that provides zero-copy buffer management and -/// automatic memory zeroing for sensitive data. This structure is used as -/// the base for all protocol packets. -/// -/// # Features -/// -/// - Zero-copy buffer management -/// - Automatic memory zeroing -/// - Efficient payload extraction -/// - Secure message encapsulation -/// -/// # Example -/// -/// ```rust -/// use citadel_proto::prelude::SecureProtocolPacket; -/// let mut packet = SecureProtocolPacket::from("Hello, world!"); -/// ``` -#[derive(Debug)] -/// A thin wrapper used for convenient creation of zero-copy outgoing buffers -pub struct SecureProtocolPacket { - inner: SecureMessagePacket, -} - -impl SecureProtocolPacket { - /// Creates a new secure packet - pub(crate) fn new() -> Self { - Self { - inner: SecureMessagePacket::new().unwrap(), - } - } - - /// Extracts the message from a given buffer - pub(crate) fn extract_message(input: &mut BytesMut) -> std::io::Result { - SecureMessagePacket::::extract_payload(input) - } - - /// Creates a new secure packet from an inner packet - pub(crate) fn from_inner(inner: SecureMessagePacket) -> Self { - Self { inner } - } -} - -impl> From for SecureProtocolPacket { - /// Creates a new secure packet from a given byte slice - fn from(bytes: T) -> Self { - let bytes = bytes.as_ref(); - let mut this = Self::new(); - this.inner - .write_payload(bytes.len() as u32, |slice| { - slice.copy_from_slice(bytes); - Ok(()) - }) - .unwrap(); - this - } -} - -impl From for SecureMessagePacket { - /// Converts a secure packet to an inner packet - fn from(val: SecureProtocolPacket) -> Self { - val.inner - } -} +use netbeam::time_tracker::TimeTracker; /// Manages the transmission of group messages and file transfers with support /// for encryption, scrambling, and packet management. This structure handles @@ -134,13 +68,13 @@ impl From for SecureMessagePacket { /// - Automatic packet chunking /// - Progress tracking /// - Error handling -pub struct GroupTransmitter { - pub ratchet_container: RatchetPacketCrafterContainer, +pub struct ObjectTransmitter { + ratchet: R, to_primary_stream: OutboundPrimaryStreamSender, // Handles the encryption and scrambling asynchronously. Also manages missing packets - pub(crate) group_transmitter: GroupSenderDevice, + pub(crate) group_transmitter: Option>, /// Contained within Self::group_transmitter, but is here for convenience - group_config: GroupReceiverConfig, + group_config: Option, /// The ID of the object that is being transmitted pub object_id: ObjectId, pub group_id: u64, @@ -149,35 +83,15 @@ pub struct GroupTransmitter { security_level: SecurityLevel, bytes_encrypted: usize, time_tracker: TimeTracker, - is_message: bool, + is_message: Option>, } -/// The base ratchet is always required, whether between HyperLAN peer to server or hyperlan p2p. -/// base_constructor may not be present, since a concurrent update may already be occurring -/// Fcm may be present, in which case, the innermost encryption pass goes through the ratchets ratchet to ensure -/// Google can't see the information. The ratchets constructor may not be present either, since a concurrent update may -/// be occurring -pub struct RatchetPacketCrafterContainer { - pub base: R, - pub base_constructor: Option, -} - -impl RatchetPacketCrafterContainer { - /// Creates a new ratchet packet crafter container - pub fn new(base: R, base_constructor: Option) -> Self { - Self { - base, - base_constructor, - } - } -} - -impl GroupTransmitter { +impl ObjectTransmitter { /// Scrambled packets will use this pub fn new_from_group_sender( to_primary_stream: OutboundPrimaryStreamSender, group_sender: GroupSenderDevice, - stacked_ratchet: RatchetPacketCrafterContainer, + ratchet: R, object_id: ObjectId, ticket: Ticket, security_level: SecurityLevel, @@ -187,12 +101,11 @@ impl GroupTransmitter { let group_id = cfg.group_id; let bytes_encrypted = cfg.plaintext_length as usize; Self { - ratchet_container: stacked_ratchet, - // This must be false - is_message: false, - group_transmitter: group_sender, + ratchet, + is_message: None, + group_transmitter: Some(group_sender), to_primary_stream, - group_config: cfg, + group_config: Some(cfg), object_id, group_id, ticket, @@ -204,57 +117,34 @@ impl GroupTransmitter { /// Creates a new stream for a request #[allow(clippy::too_many_arguments)] - pub fn new_message( + pub fn transmit_message( to_primary_stream: OutboundPrimaryStreamSender, object_id: ObjectId, - stacked_ratchet: RatchetPacketCrafterContainer, - input_packet: SecureProtocolPacket, + ratchet: R, + input_message: RatchetMessage, security_level: SecurityLevel, group_id: u64, ticket: Ticket, time_tracker: TimeTracker, - ) -> Option { + virtual_target_type: VirtualTargetType, + ) -> Result<(), NetworkError> { // Gets the latest entropy_bank version by default for this operation - log::trace!(target: "citadel", "Will use R v{} to encrypt group {}", stacked_ratchet.base.version(), group_id); - - let plaintext_len = input_packet.inner.message_len(); //the number of bytes that will be encrypted - // + 1 byte source port offset (needed for sending across port-address-translation networks) - // + 1 byte recv port offset - - let is_empty = plaintext_len == 0; - const HDP_HEADER_EXTENDED_BYTE_LEN: usize = HDP_HEADER_BYTE_LEN + 2; - //let res = encrypt_group_unified(input_packet.into_buffer(), &stacked_ratchet.base, HDP_HEADER_EXTENDED_BYTE_LEN, target_cid, object_id, group_id, craft_wave_payload_packet_into); - let res = oneshot_unencrypted_group_unified( - input_packet.into(), - HDP_HEADER_EXTENDED_BYTE_LEN, - group_id, + log::trace!(target: "citadel", "Will use R v{} to encrypt group {}", ratchet.version(), group_id); + let mut this = Self { + ratchet, + is_message: Some(input_message), object_id, - is_empty, - ); - - match res { - Ok(group_transmitter) => { - let group_config: GroupReceiverConfig = group_transmitter.get_receiver_config(); - Some(Self { - ratchet_container: stacked_ratchet, - is_message: true, - object_id, - to_primary_stream, - group_transmitter, - group_config, - bytes_encrypted: plaintext_len, - security_level, - group_id, - ticket, - time_tracker, - }) - } + to_primary_stream, + group_transmitter: None, + group_config: None, + bytes_encrypted: 0, // Irrelevant + security_level, + group_id, + ticket, + time_tracker, + }; - Err(_err) => { - log::error!(target: "citadel", "The udp packet processor stream was unable to generate the sender for group {}. Aborting", group_id); - None - } - } + this.transmit_group_header(virtual_target_type) } pub fn transmit_group_header( @@ -273,12 +163,6 @@ impl GroupTransmitter { group::craft_group_header_packet(self, virtual_target) } - pub(super) fn get_unencrypted_oneshot_packet(&mut self) -> Option { - self.group_transmitter - .get_oneshot() - .map(SecureProtocolPacket::from_inner) - } - /// Returns the number of bytes that would be encrypted pub fn get_total_plaintext_bytes(&self) -> usize { self.bytes_encrypted @@ -286,21 +170,26 @@ impl GroupTransmitter { #[allow(unused_results)] pub fn transmit_tcp_file_transfer(&mut self) -> bool { - let to_primary_stream = &self.to_primary_stream; - log::trace!(target: "citadel", "[Q-TCP] Payload packets to send: {} | Max packets per wave: {}", self.group_config.packets_needed, self.group_config.max_packets_per_wave); - let to_primary_stream = to_primary_stream.clone(); - let packets = self.group_transmitter.take_all_packets(); - - log::trace!(target: "citadel", "Will transfer {} packets", packets.len()); - for packet in packets { - if let Err(err) = to_primary_stream.unbounded_send(packet.packet) { - log::error!(target: "citadel", "[FILE] to_primary_stream died {:?}", err); + let group_config = self.group_config.as_ref().unwrap(); + log::trace!(target: "citadel", "[Q-TCP] Payload packets to send: {} | Max packets per wave: {}", group_config.packets_needed, group_config.max_packets_per_wave); + + if let Some(transmitter) = &mut self.group_transmitter { + let to_primary_stream = &self.to_primary_stream; + let packets = transmitter.take_all_packets(); + + log::trace!(target: "citadel", "Will transfer {} packets", packets.len()); + for packet in packets { + if let Err(err) = to_primary_stream.unbounded_send(packet.packet) { + log::error!(target: "citadel", "[FILE] to_primary_stream died {:?}", err); + } } - } - log::trace!(target: "citadel", "Group {} has finished transmission", self.group_id); + log::trace!(target: "citadel", "Group {} has finished transmission", self.group_id); - true + true + } else { + false + } } } @@ -314,11 +203,10 @@ pub(crate) mod group { use crate::proto::packet::packet_sizes; use crate::proto::packet::packet_sizes::GROUP_HEADER_ACK_LEN; use crate::proto::packet::{packet_flags, HdpHeader}; - use crate::proto::packet_crafter::GroupTransmitter; + use crate::proto::packet_crafter::ObjectTransmitter; use crate::proto::remote::Ticket; use crate::proto::state_container::VirtualTargetType; use crate::proto::validation::group::{GroupHeader, GroupHeaderAck, WaveAck}; - use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::ObjectId; use citadel_user::serialization::SyncIO; @@ -326,62 +214,48 @@ pub(crate) mod group { /// Crafts a group header packet for a given group transmitter and virtual target pub(super) fn craft_group_header_packet( - processor: &mut GroupTransmitter, + processor: &mut ObjectTransmitter, virtual_target: VirtualTargetType, ) -> BytesMut { let target_cid = virtual_target.get_target_cid(); - let is_fast_message = u8::from(processor.is_message); let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), cmd_primary: packet_flags::cmd::primary::GROUP_PACKET, cmd_aux: packet_flags::cmd::aux::group::GROUP_HEADER, - algorithm: is_fast_message, + algorithm: 0, security_level: processor.security_level.value(), context_info: U128::new(processor.ticket.0), group: U64::new(processor.group_id), wave_id: U32::new(0), - session_cid: U64::new(processor.ratchet_container.base.get_cid()), - entropy_bank_version: U32::new(processor.ratchet_container.base.version()), + session_cid: U64::new(processor.ratchet.get_cid()), + entropy_bank_version: U32::new(processor.ratchet.version()), timestamp: I64::new(processor.time_tracker.get_global_time_ns()), target_cid: U64::new(target_cid), }; - let mut packet = if processor.is_message { - let mut packet = processor.get_unencrypted_oneshot_packet().unwrap().inner; - packet - .write_header(|buf| { - header.inscribe_into(&mut *buf); - Ok(()) - }) - .unwrap(); - // both the header and payload are now written. Just have to extend the kem info - let kem = processor - .ratchet_container - .base_constructor - .as_mut() - .map(|res| res.stage0_alice().unwrap()); - let expected_len = kem.serialized_size().unwrap(); + let mut packet = if let Some(ratchet_message) = processor.is_message.take() { + // For messages, the RatchetManager/Messenger will provide SecureMessagePackets for us, stored inside the below field + // The header, as always, will need to be written + // We no long have to worry about setting up key exchange here; that is taken care of by the Messenger + // In fact, on the receiving end, we just forward this packet to the messenger, where it will automatically + // be forwarded to the consumer, bytpassing the kernel + let mut packet = BytesMut::with_capacity(packet_sizes::GROUP_HEADER_BASE_LEN); + header.inscribe_into(&mut packet); + let header = GroupHeader::Ratchet(ratchet_message, processor.object_id); + header.serialize_into_buf(&mut packet).unwrap(); packet - .write_payload_extension(expected_len as _, |slice| { - kem.serialize_into_slice(slice).map_err(|err| { - std::io::Error::new(std::io::ErrorKind::Other, err.into_string()) - }) - }) - .unwrap() } else { let mut packet = BytesMut::with_capacity(packet_sizes::GROUP_HEADER_BASE_LEN); header.inscribe_into(&mut packet); - let header = GroupHeader::Standard(processor.group_config.clone(), virtual_target); + let header = + GroupHeader::Standard(processor.group_config.clone().unwrap(), virtual_target); header.serialize_into_buf(&mut packet).unwrap(); packet }; - packet.put_u128(processor.object_id.0); - processor - .ratchet_container - .base + .ratchet .protect_message_packet( Some(processor.security_level), HDP_HEADER_BYTE_LEN, @@ -395,7 +269,7 @@ pub(crate) mod group { /// Crafts a group header acknowledgement packet for a given group transmitter and virtual target #[allow(clippy::too_many_arguments)] pub(crate) fn craft_group_header_ack( - stacked_ratchet: &R, + ratchet: &R, group_id: u64, target_cid: u64, object_id: ObjectId, @@ -403,7 +277,6 @@ pub(crate) mod group { initial_wave_window: Option>, fast_msg: bool, timestamp: i64, - transfer: KemTransferStatus, security_level: SecurityLevel, ) -> BytesMut { let header = HdpHeader { @@ -415,8 +288,8 @@ pub(crate) mod group { context_info: U128::new(ticket.0), group: U64::new(group_id), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -424,7 +297,6 @@ pub(crate) mod group { let header_ack = GroupHeaderAck::ReadyToReceive { fast_msg, initial_window: initial_wave_window, - transfer, object_id, }; @@ -434,7 +306,7 @@ pub(crate) mod group { header_ack.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -476,7 +348,7 @@ pub(crate) mod group { /// Crafts a wave acknowledgement packet for a given group transmitter and virtual target #[allow(clippy::too_many_arguments)] pub(crate) fn craft_wave_ack( - stacked_ratchet: &R, + ratchet: &R, object_id: ObjectId, target_cid: u64, group_id: u64, @@ -494,8 +366,8 @@ pub(crate) mod group { context_info: U128::new(object_id.0), group: U64::new(group_id), wave_id: U32::new(wave_id), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -506,7 +378,7 @@ pub(crate) mod group { header.inscribe_into(&mut packet); wave_ack.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -538,7 +410,7 @@ pub(crate) mod do_connect { /// Alice receives the nonce from Bob. She must now inscribe her username/password #[allow(unused_results)] pub(crate) fn craft_stage0_packet( - stacked_ratchet: &R, + ratchet: &R, proposed_credentials: ProposedCredentials, timestamp: i64, security_level: SecurityLevel, @@ -554,8 +426,8 @@ pub(crate) mod do_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -572,7 +444,7 @@ pub(crate) mod do_connect { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -602,7 +474,7 @@ pub(crate) mod do_connect { #[allow(clippy::too_many_arguments)] pub(crate) fn craft_final_status_packet, R: Ratchet>( - stacked_ratchet: &R, + ratchet: &R, success: bool, mailbox: Option, post_login_object: citadel_user::external_services::ServicesObject, @@ -636,8 +508,8 @@ pub(crate) mod do_connect { context_info: U128::new(0), group: U64::new(is_filesystem as u64), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -647,7 +519,7 @@ pub(crate) mod do_connect { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -657,7 +529,7 @@ pub(crate) mod do_connect { /// Crafts a do-connect success acknowledgement packet for a given timestamp and security level #[allow(unused_results)] pub(crate) fn craft_success_ack( - stacked_ratchet: &R, + ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { @@ -670,8 +542,8 @@ pub(crate) mod do_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -679,7 +551,7 @@ pub(crate) mod do_connect { let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); header.inscribe_into(&mut packet); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -697,7 +569,7 @@ pub(crate) mod keep_alive { /// Crafts a keep-alive packet for a given timestamp and security level pub(crate) fn craft_keep_alive_packet( - stacked_ratchet: &R, + ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { @@ -710,14 +582,14 @@ pub(crate) mod keep_alive { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -823,7 +695,7 @@ pub(crate) mod do_register { /// Alice sends this. The stage 3 packet contains the encrypted username, password, and full name of the registering client #[allow(unused_results)] pub(crate) fn craft_stage2( - stacked_ratchet: &R, + ratchet: &R, algorithm: u8, timestamp: i64, credentials: &ProposedCredentials, @@ -838,7 +710,7 @@ pub(crate) mod do_register { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), + session_cid: U64::new(ratchet.get_cid()), entropy_bank_version: U32::new(0), timestamp: I64::new(timestamp), target_cid: U64::new(0), @@ -852,7 +724,7 @@ pub(crate) mod do_register { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -861,7 +733,7 @@ pub(crate) mod do_register { /// Crafts a do-register success packet for a given success message and timestamp pub(crate) fn craft_success, R: Ratchet>( - stacked_ratchet: &R, + ratchet: &R, algorithm: u8, timestamp: i64, success_message: T, @@ -878,7 +750,7 @@ pub(crate) mod do_register { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), + session_cid: U64::new(ratchet.get_cid()), entropy_bank_version: U32::new(0), timestamp: I64::new(timestamp), target_cid: U64::new(0), @@ -888,7 +760,7 @@ pub(crate) mod do_register { header.inscribe_into(&mut packet); packet.put(success_message); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -940,7 +812,7 @@ pub(crate) mod do_disconnect { /// Crafts a do-disconnect stage 0 packet for a given ticket, timestamp, and security level #[allow(unused_results)] pub(crate) fn craft_stage0( - stacked_ratchet: &R, + ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, @@ -954,14 +826,14 @@ pub(crate) mod do_disconnect { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -971,7 +843,7 @@ pub(crate) mod do_disconnect { /// Crafts a do-disconnect final packet for a given ticket, timestamp, and security level #[allow(unused_results)] pub(crate) fn craft_final( - stacked_ratchet: &R, + ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, @@ -985,186 +857,15 @@ pub(crate) mod do_disconnect { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - stacked_ratchet - .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) - .unwrap(); - packet - } -} - -pub(crate) mod do_entropy_bank_update { - use bytes::BytesMut; - use zerocopy::{I64, U128, U32, U64}; - - use crate::constants::HDP_HEADER_BYTE_LEN; - use crate::proto::packet::{packet_flags, packet_sizes, HdpHeader}; - use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; - use citadel_crypt::ratchets::Ratchet; - use citadel_types::crypto::SecurityLevel; - use citadel_user::serialization::SyncIO; - use serde::{Deserialize, Serialize}; - - /// Crafts a do-entropy_bank update stage 0 packet for a given transfer, timestamp, target CID, and security level - #[allow(unused_results)] - pub(crate) fn craft_stage0( - stacked_ratchet: &R, - transfer: >::AliceToBobWireTransfer, - timestamp: i64, - target_cid: u64, - security_level: SecurityLevel, - ) -> BytesMut { - let header = HdpHeader { - protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), - cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE0, - algorithm: 0, - security_level: security_level.value(), - context_info: U128::new(0), - group: U64::new(0), - wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), - timestamp: I64::new(timestamp), - target_cid: U64::new(target_cid), - }; - - let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN); - header.inscribe_into(&mut packet); - transfer.serialize_into_buf(&mut packet).unwrap(); - - stacked_ratchet - .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) - .unwrap(); - packet - } - - /// Crafts a do-entropy_bank update stage 1 packet for a given update status, timestamp, target CID, and security level - #[derive(Serialize, Deserialize)] - pub(crate) struct Stage1UpdatePacket { - #[serde(bound = "")] - pub(crate) update_status: KemTransferStatus, - } - - #[allow(unused_results)] - pub(crate) fn craft_stage1( - stacked_ratchet: &R, - update_status: KemTransferStatus, - timestamp: i64, - target_cid: u64, - security_level: SecurityLevel, - ) -> BytesMut { - let header = HdpHeader { - protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), - cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE1, - algorithm: 0, - security_level: security_level.value(), - context_info: U128::new(0), - group: U64::new(0), - wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), - timestamp: I64::new(timestamp), - target_cid: U64::new(target_cid), - }; - - let mut packet = BytesMut::with_capacity(packet_sizes::do_entropy_bank_update::STAGE1); - header.inscribe_into(&mut packet); - - let stage1_packet = Stage1UpdatePacket { update_status }; - stage1_packet.serialize_into_buf(&mut packet).unwrap(); - - stacked_ratchet - .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) - .unwrap(); - packet - } - - /// Crafts a do-entropy_bank update truncate packet for a given truncate version, target CID, timestamp, and security level - #[derive(Serialize, Deserialize)] - pub(crate) struct TruncatePacket { - pub(crate) truncate_version: Option, - } - - #[allow(unused_results)] - pub(crate) fn craft_truncate( - stacked_ratchet: &R, - truncate_version: Option, - target_cid: u64, - timestamp: i64, - security_level: SecurityLevel, - ) -> BytesMut { - let header = HdpHeader { - protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), - cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE, - algorithm: 0, - security_level: security_level.value(), - context_info: U128::new(0), - group: U64::new(0), - wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), - timestamp: I64::new(timestamp), - target_cid: U64::new(target_cid), - }; - - let mut packet = BytesMut::with_capacity(packet_sizes::do_entropy_bank_update::STAGE1); - header.inscribe_into(&mut packet); - // encrypt the nonce into the packet - TruncatePacket { truncate_version } - .serialize_into_buf(&mut packet) - .unwrap(); - - stacked_ratchet - .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) - .unwrap(); - packet - } - - /// Crafts a do-entropy_bank update truncate acknowledgement packet for a given truncated version, target CID, timestamp, and security level - #[derive(Serialize, Deserialize)] - pub(crate) struct TruncateAckPacket { - pub(crate) truncated_version: u32, - } - - pub(crate) fn craft_truncate_ack( - stacked_ratchet: &R, - truncated_version: u32, - target_cid: u64, - timestamp: i64, - security_level: SecurityLevel, - ) -> BytesMut { - let header = HdpHeader { - protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), - cmd_primary: packet_flags::cmd::primary::DO_DRILL_UPDATE, - cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE_ACK, - algorithm: 0, - security_level: security_level.value(), - context_info: U128::new(0), - group: U64::new(0), - wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), - timestamp: I64::new(timestamp), - target_cid: U64::new(target_cid), - }; - - let mut packet = BytesMut::with_capacity(packet_sizes::do_entropy_bank_update::STAGE1); - header.inscribe_into(&mut packet); - - TruncateAckPacket { truncated_version } - .serialize_into_buf(&mut packet) - .unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1181,28 +882,28 @@ pub(crate) mod do_deregister { /// Crafts a do-deregister stage 0 packet for a given timestamp and security level pub(crate) fn craft_stage0( - stacked_ratchet: &R, + ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { let header = HdpHeader { protocol_version: (*crate::constants::PROTOCOL_VERSION).into(), cmd_primary: packet_flags::cmd::primary::DO_DEREGISTER, - cmd_aux: packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE0, + cmd_aux: packet_flags::cmd::aux::do_deregister::STAGE0, algorithm: 0, security_level: security_level.value(), context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1212,7 +913,7 @@ pub(crate) mod do_deregister { /// Crafts a do-deregister final packet for a given success flag, timestamp, and security level #[allow(unused_results)] pub(crate) fn craft_final( - stacked_ratchet: &R, + ratchet: &R, success: bool, timestamp: i64, security_level: SecurityLevel, @@ -1232,15 +933,15 @@ pub(crate) mod do_deregister { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1381,7 +1082,7 @@ pub(crate) mod pre_connect { // This gets sent from Alice to Bob pub(crate) fn craft_stage0( - stacked_ratchet: &R, + ratchet: &R, timestamp: i64, node_type: NodeType, security_level: SecurityLevel, @@ -1395,8 +1096,8 @@ pub(crate) mod pre_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; @@ -1408,7 +1109,7 @@ pub(crate) mod pre_connect { .serialize_into_buf(&mut packet) .unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1417,7 +1118,7 @@ pub(crate) mod pre_connect { /// Crafts a pre-connect final packet for a given success flag, TCP-only flag, timestamp, and security level pub(crate) fn craft_stage_final( - stacked_ratchet: &R, + ratchet: &R, success: bool, tcp_only: bool, timestamp: i64, @@ -1444,15 +1145,15 @@ pub(crate) mod pre_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1460,7 +1161,7 @@ pub(crate) mod pre_connect { /// Crafts a pre-connect begin connect packet for a given timestamp and security level pub(crate) fn craft_begin_connect( - stacked_ratchet: &R, + ratchet: &R, timestamp: i64, security_level: SecurityLevel, ) -> BytesMut { @@ -1473,14 +1174,14 @@ pub(crate) mod pre_connect { context_info: U128::new(0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(0), }; let mut packet = header.as_packet(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1527,14 +1228,14 @@ pub(crate) mod peer_cmd { use zerocopy::{I64, U128, U32, U64}; /// Crafts a peer signal packet for a given peer command, ticket, timestamp, and security level - pub(crate) const C2S_ENCRYPTION_ONLY: u64 = 0; + pub(crate) const C2S_IDENTITY_CID: u64 = 0; /* */ /// Peer signals, unlike channels, DO NOT get a target_cid because they require the central server's participation to increase security between the /// two nodes pub(crate) fn craft_peer_signal( - stacked_ratchet: &R, + ratchet: &R, peer_command: T, ticket: Ticket, timestamp: i64, @@ -1549,10 +1250,10 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), - target_cid: U64::new(C2S_ENCRYPTION_ONLY), + target_cid: U64::new(C2S_IDENTITY_CID), }; let peer_cmd_serialized_len = peer_command.serialized_size().unwrap(); @@ -1562,7 +1263,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); peer_command.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1572,7 +1273,7 @@ pub(crate) mod peer_cmd { /// Crafts a peer signal endpoint packet for a given peer command, ticket, timestamp, target CID, and security level #[allow(dead_code)] pub(crate) fn craft_peer_signal_endpoint( - stacked_ratchet: &R, + ratchet: &R, peer_command: T, ticket: Ticket, timestamp: i64, @@ -1588,8 +1289,8 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1601,7 +1302,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); peer_command.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1611,7 +1312,7 @@ pub(crate) mod peer_cmd { /// Crafts a channel packet for a given payload, ticket, proxy target CID, timestamp, and security level #[allow(dead_code)] pub(crate) fn craft_channel_packet( - stacked_ratchet: &R, + ratchet: &R, payload: ChannelPacket, ticket: Ticket, proxy_target_cid: u64, @@ -1627,8 +1328,8 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(proxy_target_cid), }; @@ -1639,7 +1340,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1649,7 +1350,7 @@ pub(crate) mod peer_cmd { /// Crafts a group message packet for a given payload, ticket, proxy target CID, timestamp, and security level #[allow(dead_code)] pub(crate) fn craft_group_message_packet( - stacked_ratchet: &R, + ratchet: &R, payload: &GroupBroadcast, ticket: Ticket, proxy_target_cid: u64, @@ -1665,8 +1366,8 @@ pub(crate) mod peer_cmd { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(proxy_target_cid), }; @@ -1677,7 +1378,7 @@ pub(crate) mod peer_cmd { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); packet @@ -1707,7 +1408,7 @@ pub(crate) mod file { } pub(crate) fn craft_file_error_packet( - stacked_ratchet: &R, + ratchet: &R, ticket: Ticket, security_level: SecurityLevel, virtual_target: VirtualTargetType, @@ -1724,8 +1425,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(virtual_target.get_target_cid()), }; @@ -1739,7 +1440,7 @@ pub(crate) mod file { payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1756,7 +1457,7 @@ pub(crate) mod file { #[allow(clippy::too_many_arguments)] pub(crate) fn craft_file_header_packet( - stacked_ratchet: &R, + ratchet: &R, group_start: u64, ticket: Ticket, security_level: SecurityLevel, @@ -1774,8 +1475,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(group_start), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(virtual_target.get_target_cid()), }; @@ -1790,7 +1491,7 @@ pub(crate) mod file { payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1808,7 +1509,7 @@ pub(crate) mod file { #[allow(clippy::too_many_arguments)] pub(crate) fn craft_file_header_ack_packet( - stacked_ratchet: &R, + ratchet: &R, success: bool, object_id: ObjectId, target_cid: u64, @@ -1827,8 +1528,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1845,7 +1546,7 @@ pub(crate) mod file { payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1861,7 +1562,7 @@ pub(crate) mod file { } pub fn craft_revfs_pull( - stacked_ratchet: &R, + ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -1878,8 +1579,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1894,7 +1595,7 @@ pub(crate) mod file { }; payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1908,7 +1609,7 @@ pub(crate) mod file { } pub fn craft_revfs_delete( - stacked_ratchet: &R, + ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -1924,8 +1625,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1936,7 +1637,7 @@ pub(crate) mod file { let payload = ReVFSDeletePacket { virtual_path }; payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1951,7 +1652,7 @@ pub(crate) mod file { } pub fn craft_revfs_ack( - stacked_ratchet: &R, + ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -1967,8 +1668,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -1980,7 +1681,7 @@ pub(crate) mod file { let payload = ReVFSAckPacket { success, error_msg }; payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -1995,7 +1696,7 @@ pub(crate) mod file { } pub fn craft_revfs_pull_ack( - stacked_ratchet: &R, + ratchet: &R, security_level: SecurityLevel, ticket: Ticket, timestamp: i64, @@ -2011,8 +1712,8 @@ pub(crate) mod file { context_info: U128::new(ticket.0), group: U64::new(0), wave_id: U32::new(0), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: I64::new(timestamp), target_cid: U64::new(target_cid), }; @@ -2021,7 +1722,7 @@ pub(crate) mod file { header.inscribe_into(&mut packet); payload.serialize_into_buf(&mut packet).unwrap(); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -2039,7 +1740,7 @@ pub(crate) mod udp { /// Crafts a UDP packet for a given command auxiliary, payload, target CID, and security level pub(crate) fn craft_udp_packet( - stacked_ratchet: &R, + ratchet: &R, cmd_aux: u8, payload: BytesMut, target_cid: u64, @@ -2054,8 +1755,8 @@ pub(crate) mod udp { context_info: Default::default(), group: Default::default(), wave_id: Default::default(), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: Default::default(), target_cid: U64::new(target_cid), }; @@ -2064,7 +1765,7 @@ pub(crate) mod udp { header.inscribe_into(&mut packet); packet.extend_from_slice(&payload[..]); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -2082,7 +1783,7 @@ pub(crate) mod hole_punch { /// Crafts a hole punch packet for a given plaintext, security level, target CID, and hyper ratchet pub fn generate_packet( - stacked_ratchet: &R, + ratchet: &R, plaintext: &[u8], security_level: SecurityLevel, target_cid: u64, @@ -2096,8 +1797,8 @@ pub(crate) mod hole_punch { context_info: Default::default(), group: Default::default(), wave_id: Default::default(), - session_cid: U64::new(stacked_ratchet.get_cid()), - entropy_bank_version: U32::new(stacked_ratchet.version()), + session_cid: U64::new(ratchet.get_cid()), + entropy_bank_version: U32::new(ratchet.version()), timestamp: Default::default(), target_cid: U64::new(target_cid), }; @@ -2106,7 +1807,7 @@ pub(crate) mod hole_punch { header.inscribe_into(&mut packet); packet.put(plaintext); - stacked_ratchet + ratchet .protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet) .unwrap(); @@ -2115,7 +1816,7 @@ pub(crate) mod hole_punch { /// Decrypts a hole punch packet for a given packet, hyper ratchet, security level, and target CID pub fn decrypt_packet( - stacked_ratchet: &R, + ratchet: &R, packet: &[u8], security_level: SecurityLevel, ) -> Option { @@ -2127,7 +1828,7 @@ pub(crate) mod hole_punch { let mut packet = BytesMut::from(packet); let header = packet.split_to(HDP_HEADER_BYTE_LEN); - stacked_ratchet + ratchet .validate_message_packet_in_place_split(Some(security_level), &header, &mut packet) .ok()?; diff --git a/citadel_proto/src/proto/packet_processor/connect_packet.rs b/citadel_proto/src/proto/packet_processor/connect_packet.rs index 5f56aeb8b..9f24fd558 100644 --- a/citadel_proto/src/proto/packet_processor/connect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/connect_packet.rs @@ -35,6 +35,7 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{ConnectFail, ConnectSuccess, MailboxDelivery}; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; use crate::proto::packet_processor::primary_group_packet::get_orientation_safe_ratchet; use citadel_crypt::ratchets::Ratchet; use citadel_types::proto::ConnectMode; @@ -83,7 +84,7 @@ pub async fn process_connect( let (header, payload, _, _) = packet.decompose(); - let (header, payload, stacked_ratchet) = return_if_none!( + let (header, payload, ratchet) = return_if_none!( validation::aead::validate(hr, &header, payload), "Unable to validate connect packet" ); @@ -109,7 +110,7 @@ pub async fn process_connect( session .file_transfer_compatible .set_once(local_uses_file_system && stage0_packet.uses_filesystem); - let cid = stacked_ratchet.get_cid(); + let cid = ratchet.get_cid(); let success_time = session.time_tracker.get_global_time_ns(); let addr = session.remote_peer; let is_personal = !session.is_server; @@ -127,7 +128,6 @@ pub async fn process_connect( .take(); let channel = state_container.init_new_c2s_virtual_connection( &cnac, - security_level, kernel_ticket, header.session_cid.get(), session, @@ -144,8 +144,6 @@ pub async fn process_connect( return Ok(PrimaryProcessorResult::EndSession("Unable to upgrade from a provisional to a protected connection (Server)")); } - //cnac.update_post_quantum_container(post_quantum).await?; - //cnac.spawn_save_task_on_threadpool(); // register w/ peer layer, get mail in the process let account_manager = session.account_manager.clone(); @@ -171,7 +169,7 @@ pub async fn process_connect( let success_packet = packet_crafter::do_connect::craft_final_status_packet( - &stacked_ratchet, + &ratchet, true, mailbox_items, post_login_object.clone(), @@ -201,7 +199,7 @@ pub async fn process_connect( }); // safe unwrap. Store the signal inner_mut_state!(session.state_container) - .c2s_channel_container + .get_endpoint_container_mut(C2S_IDENTITY_CID) .as_mut() .unwrap() .channel_signal = Some(channel_signal); @@ -215,7 +213,7 @@ pub async fn process_connect( //session.state = SessionState::NeedsConnect; let packet = packet_crafter::do_connect::craft_final_status_packet( - &stacked_ratchet, + &ratchet, false, None, ServicesObject::default(), @@ -244,7 +242,7 @@ pub async fn process_connect( let message = String::from_utf8(payload.message.to_vec()) .unwrap_or_else(|_| "Invalid UTF-8 message".to_string()); log::error!(target: "citadel", "The server refused to login the user. Reason: {}", &message); - let cid = stacked_ratchet.get_cid(); + let cid = ratchet.get_cid(); state_container.connect_state.on_fail(); drop(state_container); @@ -290,7 +288,7 @@ pub async fn process_connect( let message = String::from_utf8(payload.message.to_vec()) .unwrap_or_else(|_| String::from("Invalid message")); let kernel_ticket = session.kernel_ticket.get(); - let cid = stacked_ratchet.get_cid(); + let cid = ratchet.get_cid(); state_container.connect_state.on_success(); state_container.connect_state.on_connect_packet_received(); @@ -308,7 +306,6 @@ pub async fn process_connect( let channel = state_container.init_new_c2s_virtual_connection( &cnac, - security_level, kernel_ticket, header.session_cid.get(), session, @@ -347,7 +344,7 @@ pub async fn process_connect( session.state.set(SessionState::Connected); let success_ack = packet_crafter::do_connect::craft_success_ack( - &stacked_ratchet, + &ratchet, timestamp, security_level, ); @@ -426,7 +423,7 @@ pub async fn process_connect( if use_ka { let ka = packet_crafter::keep_alive::craft_keep_alive_packet( - &stacked_ratchet, + &ratchet, timestamp, security_level, ); @@ -453,9 +450,7 @@ pub async fn process_connect( log::trace!(target: "citadel", "RECV SUCCESS_ACK"); if session.is_server { let signal = inner_mut_state!(session.state_container) - .c2s_channel_container - .as_mut() - .ok_or_else(|| NetworkError::InternalError("C2S channel not loaded"))? + .get_endpoint_container_mut(C2S_IDENTITY_CID)? .channel_signal .take() .ok_or(NetworkError::InternalError("Channel signal missing"))?; diff --git a/citadel_proto/src/proto/packet_processor/deregister_packet.rs b/citadel_proto/src/proto/packet_processor/deregister_packet.rs index 8ab24982f..d76bec564 100644 --- a/citadel_proto/src/proto/packet_processor/deregister_packet.rs +++ b/citadel_proto/src/proto/packet_processor/deregister_packet.rs @@ -67,7 +67,7 @@ pub async fn process_deregister( let timestamp = session.time_tracker.get_global_time_ns(); let (header, payload, _, _) = packet.decompose(); - let (header, _payload, stacked_ratchet) = return_if_none!( + let (header, _payload, ratchet) = return_if_none!( validation::aead::validate(hr, &header, payload), "Unable to validate dereg packet" ); @@ -81,7 +81,7 @@ pub async fn process_deregister( deregister_client_from_self( session_cid, session, - &stacked_ratchet, + &ratchet, timestamp, security_level, ) diff --git a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs index 64d1c7bea..02b835a7e 100644 --- a/citadel_proto/src/proto/packet_processor/disconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/disconnect_packet.rs @@ -64,7 +64,7 @@ pub async fn process_disconnect( }; let (header, payload, _, _) = packet.decompose(); - let (header, _, stacked_ratchet) = return_if_none!( + let (header, _, ratchet) = return_if_none!( validation::aead::validate(hr, &header, payload), "Unable to validate" ); @@ -76,7 +76,7 @@ pub async fn process_disconnect( packet_flags::cmd::aux::do_disconnect::STAGE0 => { log::trace!(target: "citadel", "STAGE 0 DISCONNECT PACKET RECEIVED"); let packet = packet_crafter::do_disconnect::craft_final( - &stacked_ratchet, + &ratchet, ticket, timestamp, security_level, diff --git a/citadel_proto/src/proto/packet_processor/file_packet.rs b/citadel_proto/src/proto/packet_processor/file_packet.rs index 1c45a42ba..48e49c774 100644 --- a/citadel_proto/src/proto/packet_processor/file_packet.rs +++ b/citadel_proto/src/proto/packet_processor/file_packet.rs @@ -66,7 +66,7 @@ pub fn process_file_packet( let header_bytes = &header[..]; let header = return_if_none!(Ref::new(header_bytes), "Unable to validate header layout") as Ref<&[u8], HdpHeader>; - let stacked_ratchet = return_if_none!( + let ratchet = return_if_none!( get_orientation_safe_ratchet( header.entropy_bank_version.get(), &state_container, @@ -79,7 +79,7 @@ pub fn process_file_packet( let ts = session.time_tracker.get_global_time_ns(); // ALL FILE packets must be authenticated - match validation::group::validate(&stacked_ratchet, security_level, header_bytes, payload) { + match validation::group::validate(&ratchet, security_level, header_bytes, payload) { Some(payload) => { match header.cmd_aux { packet_flags::cmd::aux::file::FILE_ERROR => { @@ -157,7 +157,7 @@ pub fn process_file_packet( vfm, session.account_manager.get_persistence_handler(), session.state_container.clone(), - stacked_ratchet, + ratchet, target_cid, v_target_flipped, preferred_primary_stream, @@ -287,7 +287,7 @@ pub fn process_file_packet( // on top of spawning the file transfer subroutine prior to this, // we will also send a REVFS pull ack let response_packet = packet_crafter::file::craft_revfs_pull_ack( - &stacked_ratchet, + &ratchet, security_level, ticket, ts, @@ -330,7 +330,7 @@ pub fn process_file_packet( .err() .map(|e| e.into_string()); let response_packet = packet_crafter::file::craft_revfs_ack( - &stacked_ratchet, + &ratchet, security_level, ticket, ts, @@ -360,7 +360,7 @@ pub fn process_file_packet( error_message: payload.error_msg, data: None, ticket, - session_cid: stacked_ratchet.get_cid(), + session_cid: ratchet.get_cid(), }); session.send_to_kernel(response)?; @@ -390,7 +390,7 @@ pub fn process_file_packet( error_message: Some(error), data: None, ticket, - session_cid: stacked_ratchet.get_cid(), + session_cid: ratchet.get_cid(), }); session.send_to_kernel(error_signal)?; diff --git a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs index 16ad7b3e6..171449036 100644 --- a/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs +++ b/citadel_proto/src/proto/packet_processor/keep_alive_packet.rs @@ -73,8 +73,7 @@ pub async fn process_keep_alive( let (header, payload, _, _) = packet.decompose(); - if let Some((header, _payload, _stacked_ratchet)) = - validation::aead::validate(hr, &header, payload) + if let Some((header, _payload, _ratchet)) = validation::aead::validate(hr, &header, payload) { let current_timestamp_ns = session.time_tracker.get_global_time_ns(); let to_primary_stream = return_if_none!( diff --git a/citadel_proto/src/proto/packet_processor/mod.rs b/citadel_proto/src/proto/packet_processor/mod.rs index 0a6f95b39..f3bf3f399 100644 --- a/citadel_proto/src/proto/packet_processor/mod.rs +++ b/citadel_proto/src/proto/packet_processor/mod.rs @@ -65,7 +65,6 @@ //! //! - **Maintenance Packets**: //! - `keep_alive_packet`: Connection maintenance -//! - `rekey_packet`: Key rotation //! - `hole_punch`: NAT traversal //! //! ## Processing Flow @@ -82,7 +81,7 @@ //! - Replay attacks are prevented through sequence numbers use crate::proto::packet::HdpHeader; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; use crate::proto::state_container::VirtualConnectionType; use bytes::BytesMut; use citadel_user::re_exports::__private::Formatter; @@ -118,7 +117,6 @@ pub mod preconnect_packet; pub mod primary_group_packet; pub mod raw_primary_packet; pub mod register_packet; -pub mod rekey_packet; pub mod udp_packet; // pub mod hole_punch; @@ -160,7 +158,7 @@ impl std::fmt::Debug for PrimaryProcessorResult { pub(crate) fn header_to_response_vconn_type(header: &HdpHeader) -> VirtualConnectionType { let session_cid = header.session_cid.get(); let target_cid = header.target_cid.get(); - if target_cid != C2S_ENCRYPTION_ONLY { + if target_cid != C2S_IDENTITY_CID { // the peer_cid and implicated cid must be flipped VirtualConnectionType::LocalGroupPeer { session_cid: target_cid, diff --git a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs index 5ee489e7c..4c80a2e85 100644 --- a/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs +++ b/citadel_proto/src/proto/packet_processor/peer/group_broadcast.rs @@ -34,7 +34,7 @@ use super::super::includes::*; use crate::error::NetworkError; use crate::functional::*; use crate::proto::node_result::{GroupChannelCreated, GroupEvent}; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; use crate::proto::peer::group_channel::GroupBroadcastPayload; use crate::proto::remote::Ticket; use citadel_crypt::ratchets::Ratchet; @@ -255,7 +255,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -291,7 +291,7 @@ pub async fn process_group_broadcast( sess_ratchet, &error, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -306,7 +306,7 @@ pub async fn process_group_broadcast( sess_ratchet, &success, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -320,7 +320,7 @@ pub async fn process_group_broadcast( peer_hr, &GroupBroadcast::RequestJoin { sender, key }, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ) @@ -331,7 +331,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -361,7 +361,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -397,7 +397,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -457,7 +457,7 @@ pub async fn process_group_broadcast( sess_ratchet, &resp, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -521,7 +521,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -542,7 +542,7 @@ pub async fn process_group_broadcast( peer_hr, &GroupBroadcast::DeclineMembership { target, key }, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ) @@ -556,7 +556,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -629,7 +629,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -705,7 +705,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -717,7 +717,7 @@ pub async fn process_group_broadcast( sess_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); @@ -762,7 +762,7 @@ pub async fn process_group_broadcast( sess_ratchet, &resp, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ); diff --git a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs index 0c9aff8f8..6cb16bb98 100644 --- a/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs +++ b/citadel_proto/src/proto/packet_processor/peer/peer_cmd_packet.rs @@ -63,6 +63,7 @@ use crate::proto::peer::peer_layer::{ }; use crate::proto::remote::Ticket; use crate::proto::session_manager::CitadelSessionManager; +use crate::proto::state_container::OutgoingPeerConnectionAttempt; use crate::proto::state_subcontainers::peer_kem_state_container::PeerKemStateContainer; use netbeam::sync::network_endpoint::NetworkEndpoint; @@ -89,13 +90,13 @@ pub async fn process_peer_cmd( let session = session_orig.clone(); let (header, payload, _peer_addr, _) = packet.decompose(); - let (session_cid, sess_stacked_ratchet, payload, security_level) = { + let (session_cid, sess_ratchet, payload, security_level) = { // Some PEER_CMD packets get encrypted using the endpoint crypto log::trace!(target: "citadel", "RECV PEER CMD packet (proxy: {})", endpoint_cid_info.is_some()); let state_container = inner_state!(session.state_container); let session_cid = return_if_none!(session.session_cid.get()); - let sess_stacked_ratchet = return_if_none!( + let sess_ratchet = return_if_none!( get_orientation_safe_ratchet( header_entropy_bank_version, &state_container, @@ -105,12 +106,12 @@ pub async fn process_peer_cmd( ); let (header, payload) = return_if_none!( - validation::aead::validate_custom(&sess_stacked_ratchet, &header, payload), + validation::aead::validate_custom(&sess_ratchet, &header, payload), "Unable to validate peer CMD packet" ); let security_level = header.security_level.into(); log::trace!(target: "citadel", "PEER CMD packet authenticated"); - (session_cid, sess_stacked_ratchet, payload, security_level) + (session_cid, sess_ratchet, payload, security_level) }; let task = async move { @@ -124,7 +125,7 @@ pub async fn process_peer_cmd( session, header, &payload[..], - &sess_stacked_ratchet, + &sess_ratchet, ) .await } @@ -148,51 +149,12 @@ pub async fn process_peer_cmd( .as_ref() .map(|_| vconn.get_original_session_cid()) .unwrap_or_else(|| vconn.get_original_target_cid()); - let state_container = inner_state!(session.state_container); + let mut state_container = inner_mut_state!(session.state_container); if let Some(v_conn) = - state_container.active_virtual_connections.get(&target) + state_container.active_virtual_connections.remove(&target) { - v_conn.is_active.store(false, Ordering::SeqCst); //prevent further messages from being sent from this node - // ... but, we still want any messages already sent to be processed - - let last_packet = v_conn.last_delivered_message_timestamp.clone(); - let state_container_ref = session.state_container.clone(); - - std::mem::drop(state_container); - - let task = async move { - loop { - if let Some(ts) = last_packet.get() { - if ts.elapsed() > Duration::from_millis(1500) - && inner_mut_state!(state_container_ref) - .enqueued_packets - .entry(target) - .or_default() - .is_empty() - { - break; - } - } else if inner_mut_state!(state_container_ref) - .enqueued_packets - .entry(target) - .or_default() - .is_empty() - { - break; - } - - citadel_io::tokio::time::sleep(Duration::from_millis(1500)) - .await; - } - - log::trace!(target: "citadel", "[Peer Vconn] No packets received in the last 1500ms; will drop the connection cleanly"); - // once we're done waiting for packets to stop showing up, we can remove the container to end the underlying TCP stream - let mut state_container = inner_mut_state!(state_container_ref); - let _ = - state_container.active_virtual_connections.remove(&target); - }; - - spawn!(task); + v_conn.is_active.store(false, Ordering::SeqCst); + //prevent further messages from being sent from this node } session.send_to_kernel(NodeResult::PeerEvent(PeerEvent { @@ -378,7 +340,7 @@ pub async fn process_peer_cmd( let stage0_peer_kem = packet_crafter::peer_cmd::craft_peer_signal( - &sess_stacked_ratchet, + &sess_ratchet, signal, ticket, timestamp, @@ -479,7 +441,7 @@ pub async fn process_peer_cmd( drop(state_container); let stage1_kem = packet_crafter::peer_cmd::craft_peer_signal( - &sess_stacked_ratchet, + &sess_ratchet, signal, ticket, timestamp, @@ -507,7 +469,7 @@ pub async fn process_peer_cmd( udp_rx_opt, sync_instant, encrypted_config_container, - ticket_for_chan, + local_outgoing_attempt_metadata, needs_turn, ) = { let mut state_container = @@ -546,7 +508,7 @@ pub async fn process_peer_cmd( }; let error_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_stacked_ratchet, + &sess_ratchet, error_signal, ticket, timestamp, @@ -556,15 +518,15 @@ pub async fn process_peer_cmd( error_packet, )); } - let stacked_ratchet = return_if_none!( + let ratchet = return_if_none!( alice_constructor.finish_with_custom_cid(this_cid) ); - let endpoint_stacked_ratchet = stacked_ratchet.clone(); + let endpoint_ratchet = ratchet.clone(); // now, create a new toolset and encrypt it // NOTE: when this toolset gets transmitted, it retains this_cid // As such, the other end MUST change the CID internally for BOTH // toolset AND the single entropy_bank - let toolset = Toolset::new(this_cid, stacked_ratchet); + let toolset = Toolset::new(this_cid, ratchet); // now, register the loaded PQC + toolset into the virtual conn let peer_crypto = PeerSessionCrypto::new(toolset, true); let vconn_type = VirtualConnectionType::LocalGroupPeer { @@ -582,18 +544,17 @@ pub async fn process_peer_cmd( BackendType::Filesystem(..) ); - let channel = state_container - .insert_new_peer_virtual_connection_as_endpoint( - bob_predicted_socket_addr, - session_security_settings, - ticket, - peer_cid, - vconn_type, - peer_crypto, - session, - local_is_file_transfer_compat - && *peer_file_transfer_compat, - ); + let channel = state_container.create_virtual_connection( + //bob_predicted_socket_addr, + session_security_settings, + ticket, + peer_cid, + vconn_type, + peer_crypto, + session, + local_is_file_transfer_compat + && *peer_file_transfer_compat, + ); // load the channel now that the keys have been exchanged kem_state.local_is_initiator = true; @@ -614,23 +575,23 @@ pub async fn process_peer_cmd( }; let endpoint_security_level = - endpoint_stacked_ratchet.get_default_security_level(); + endpoint_ratchet.get_default_security_level(); let hole_punch_compat_stream = ReliableOrderedCompatStream::::new( return_if_none!(session.to_primary_stream.clone()), &mut state_container, peer_cid, - endpoint_stacked_ratchet.clone(), + endpoint_ratchet.clone(), endpoint_security_level, ); - let ticket_for_chan = state_container + let local_outgoing_attempt_metadata = state_container .outgoing_peer_connect_attempts .remove(&peer_cid); drop(state_container); let stun_servers = session.stun_servers.clone(); let encrypted_config_container = generate_hole_punch_crypt_container( - endpoint_stacked_ratchet, + endpoint_ratchet, SecurityLevel::Standard, peer_cid, stun_servers, @@ -639,7 +600,7 @@ pub async fn process_peer_cmd( // we need to use the session pqc since this signal needs to get processed by the center node let stage2_kem_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_stacked_ratchet, + &sess_ratchet, signal, ticket, timestamp, @@ -655,7 +616,7 @@ pub async fn process_peer_cmd( udp_rx_opt, sync_instant, encrypted_config_container, - ticket_for_chan, + local_outgoing_attempt_metadata, needs_turn, ) }; @@ -666,9 +627,19 @@ pub async fn process_peer_cmd( UdpMode::Disabled }; + let Some(OutgoingPeerConnectionAttempt { + ticket: init_ticket, + session_security_settings, + }) = local_outgoing_attempt_metadata + else { + // TODO: Send error + log::error!(target: "citadel", "Attempted to create a virtual connection, but could not find local outgoing attempt metadata"); + return Ok(PrimaryProcessorResult::Void); + }; + let channel_signal = NodeResult::PeerChannelCreated(PeerChannelCreated { - ticket: ticket_for_chan.unwrap_or(ticket), + ticket: init_ticket, channel, udp_rx_opt, }); @@ -702,6 +673,7 @@ pub async fn process_peer_cmd( encrypted_config_container, client_config, udp_mode, + session_security_settings, ) .await; } @@ -725,8 +697,8 @@ pub async fn process_peer_cmd( hole_punch_compat_stream, channel, udp_rx_opt, - endpoint_stacked_ratchet, - ticket_for_chan, + endpoint_ratchet, + local_outgoing_connection_attempt_metadata, needs_turn, ) = { let mut state_container = @@ -741,15 +713,13 @@ pub async fn process_peer_cmd( let bob_constructor = return_if_none!(kem.constructor.take()); let udp_rx_opt = kem.udp_channel_sender.rx.take(); - let endpoint_stacked_ratchet = return_if_none!( + let endpoint_ratchet = return_if_none!( bob_constructor.finish_with_custom_cid(this_cid) ); let endpoint_security_level = - endpoint_stacked_ratchet.get_default_security_level(); - let toolset = Toolset::new( - this_cid, - endpoint_stacked_ratchet.clone(), - ); + endpoint_ratchet.get_default_security_level(); + let toolset = + Toolset::new(this_cid, endpoint_ratchet.clone()); let peer_crypto = PeerSessionCrypto::new(toolset, false); // create an endpoint vconn @@ -768,40 +738,40 @@ pub async fn process_peer_cmd( log::trace!(target: "citadel", "[STUN] Peer public addr: {:?} || needs TURN? {}", &alice_predicted_socket_addr, needs_turn); - let channel = state_container - .insert_new_peer_virtual_connection_as_endpoint( - alice_predicted_socket_addr, - session_security_settings, - ticket, - peer_cid, - vconn_type, - peer_crypto, - session, - local_is_file_transfer_compat - && *peer_file_transfer_compat, - ); + let channel = state_container.create_virtual_connection( + //alice_predicted_socket_addr, + session_security_settings, + ticket, + peer_cid, + vconn_type, + peer_crypto, + session, + local_is_file_transfer_compat + && *peer_file_transfer_compat, + ); log::trace!(target: "citadel", "Virtual connection forged on endpoint tuple {} -> {}", this_cid, peer_cid); // We can now send the channel to the kernel, where TURN traversal is immediantly available. // however, STUN-like traversal will proceed in the background //state_container.kernel_tx.unbounded_send(HdpServerResult::PeerChannelCreated(ticket, channel, udp_rx_opt)).ok()?; - let ticket_for_chan = state_container - .outgoing_peer_connect_attempts - .remove(&peer_cid); + let local_outgoing_connection_attempt_metadata = + state_container + .outgoing_peer_connect_attempts + .remove(&peer_cid); let hole_punch_compat_stream = ReliableOrderedCompatStream::::new( return_if_none!(session.to_primary_stream.clone()), &mut state_container, peer_cid, - endpoint_stacked_ratchet.clone(), + endpoint_ratchet.clone(), endpoint_security_level, ); ( hole_punch_compat_stream, channel, udp_rx_opt, - endpoint_stacked_ratchet, - ticket_for_chan, + endpoint_ratchet, + local_outgoing_connection_attempt_metadata, needs_turn, ) }; @@ -812,9 +782,19 @@ pub async fn process_peer_cmd( UdpMode::Disabled }; + let Some(OutgoingPeerConnectionAttempt { + ticket: init_ticket, + session_security_settings, + }) = local_outgoing_connection_attempt_metadata + else { + // TODO: Send error + log::error!(target: "citadel", "Attempted to create a virtual connection, but could not find local outgoing attempt metadata"); + return Ok(PrimaryProcessorResult::Void); + }; + let channel_signal = NodeResult::PeerChannelCreated(PeerChannelCreated { - ticket: ticket_for_chan.unwrap_or(ticket), + ticket: init_ticket, channel, udp_rx_opt, }); @@ -832,7 +812,7 @@ pub async fn process_peer_cmd( let stun_servers = session.stun_servers.clone(); let encrypted_config_container = generate_hole_punch_crypt_container( - endpoint_stacked_ratchet, + endpoint_ratchet, SecurityLevel::Standard, peer_cid, stun_servers, @@ -861,6 +841,7 @@ pub async fn process_peer_cmd( encrypted_config_container, client_config, udp_mode, + session_security_settings, ) .await; } @@ -900,7 +881,7 @@ pub async fn process_peer_cmd( session, signal, ticket, - sess_stacked_ratchet, + sess_ratchet, header, timestamp, security_level, @@ -925,7 +906,7 @@ async fn process_signal_command_as_server( sess_ref: &CitadelSession, signal: PeerSignal, ticket: Ticket, - sess_stacked_ratchet: R, + sess_ratchet: R, header: Ref<&[u8], HdpHeader>, timestamp: i64, security_level: SecurityLevel, @@ -971,16 +952,16 @@ async fn process_signal_command_as_server( peer_conn_type: conn, kex_payload: kep, }; - if sess_stacked_ratchet.get_cid() == conn.get_original_target_cid() { + if sess_ratchet.get_cid() == conn.get_original_target_cid() { log::error!(target: "citadel", "Error (equivalent CIDs)"); return Ok(PrimaryProcessorResult::Void); } let peer_cid = conn.get_original_target_cid(); - let res = sess_mgr.send_signal_to_peer_direct(peer_cid, move |peer_stacked_ratchet| { + let res = sess_mgr.send_signal_to_peer_direct(peer_cid, move |peer_ratchet| { packet_crafter::peer_cmd::craft_peer_signal( - peer_stacked_ratchet, + peer_ratchet, signal_to, ticket, timestamp, @@ -991,7 +972,7 @@ async fn process_signal_command_as_server( if let Err(err) = res { reply_to_sender_err( err, - &sess_stacked_ratchet, + &sess_ratchet, ticket, timestamp, security_level, @@ -1029,7 +1010,7 @@ async fn process_signal_command_as_server( target_cid, timestamp, session, - &sess_stacked_ratchet, + &sess_ratchet, security_level, ) .await @@ -1072,7 +1053,7 @@ async fn process_signal_command_as_server( target_cid, timestamp, session, - &sess_stacked_ratchet, + &sess_ratchet, security_level, ) .await?; @@ -1097,7 +1078,7 @@ async fn process_signal_command_as_server( }; let rebound_accept = packet_crafter::peer_cmd::craft_peer_signal( - &sess_stacked_ratchet, + &sess_ratchet, cmd, ticket, timestamp, @@ -1123,7 +1104,7 @@ async fn process_signal_command_as_server( ticket, &to_primary_stream, &sess_mgr, - &sess_stacked_ratchet, + &sess_ratchet, security_level, &mut peer_layer, ) @@ -1203,7 +1184,7 @@ async fn process_signal_command_as_server( // now, send a success packet to the client let success_cmd = PeerSignal::DeregistrationSuccess { peer_conn_type }; let rebound_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_stacked_ratchet, + &sess_ratchet, success_cmd, ticket, timestamp, @@ -1224,7 +1205,7 @@ async fn process_signal_command_as_server( }, }; let error_packet = packet_crafter::peer_cmd::craft_peer_signal( - &sess_stacked_ratchet, + &sess_ratchet, error_signal, ticket, timestamp, @@ -1272,7 +1253,7 @@ async fn process_signal_command_as_server( target_cid, timestamp, sess_ref, - &sess_stacked_ratchet, + &sess_ratchet, security_level, ) .await @@ -1301,7 +1282,7 @@ async fn process_signal_command_as_server( target_cid, timestamp, sess_ref, - &sess_stacked_ratchet, + &sess_ratchet, security_level, ) .await?; @@ -1323,7 +1304,7 @@ async fn process_signal_command_as_server( ticket, &to_primary_stream, &sess_mgr, - &sess_stacked_ratchet, + &sess_ratchet, security_level, &mut peer_layer, ) @@ -1352,89 +1333,62 @@ async fn process_signal_command_as_server( session_cid, peer_cid: target_cid, } => { - let state_container = inner_state!(session.state_container); - if let Some(v_conn) = - state_container.active_virtual_connections.get(&target_cid) + let mut state_container = inner_mut_state!(session.state_container); + if state_container + .active_virtual_connections + .remove(&target_cid) + .is_some() { - // ... but, we still want any messages already sent to be processed - - let last_packet = v_conn.last_delivered_message_timestamp.clone(); - let state_container_ref = session.state_container.clone(); - let session_manager = session.session_manager.clone(); - - std::mem::drop(state_container); + // note: this is w.r.t the server. + log::trace!(target: "citadel", "[Peer Vconn @ Server] will drop the virtual connection"); + let resp = Some(resp.unwrap_or(PeerResponse::Disconnected(format!( + "Peer {session_cid} closed the virtual connection to {target_cid}" + )))); - let task = async move { - // note: this is w.r.t the server. - while let Some(ts) = last_packet.get() { - if ts.elapsed() > Duration::from_millis(1500) { - break; - } - - citadel_io::tokio::time::sleep(Duration::from_millis(1500)).await; - } - - log::trace!(target: "citadel", "[Peer Vconn @ Server] No packets received in the last 1500ms; will drop the virtual connection cleanly"); - // once we're done waiting for packets to stop showing up, we can remove the container to end the underlying TCP stream - let mut state_container = inner_mut_state!(state_container_ref); - let _ = state_container - .active_virtual_connections - .remove(&target_cid) - .map(|v_conn| v_conn.is_active.store(false, Ordering::SeqCst)); - - let resp = Some(resp.unwrap_or(PeerResponse::Disconnected(format!( - "Peer {session_cid} closed the virtual connection to {target_cid}" - )))); - let signal_to_peer = PeerSignal::Disconnect { - peer_conn_type: PeerConnectionType::LocalGroupPeer { - session_cid, - peer_cid: target_cid, - }, - disconnect_response: resp, - }; - - // now, remove target CID's v_conn to `session_cid` - std::mem::drop(state_container); - let _ = session_manager.disconnect_virtual_conn( - session_cid, - target_cid, - move |peer_stacked_ratchet| { - // send signal to peer - packet_crafter::peer_cmd::craft_peer_signal( - peer_stacked_ratchet, - signal_to_peer, - ticket, - timestamp, - security_level, - ) - }, - ); - }; - - spawn!(task); - - let rebound_signal = PeerSignal::Disconnect { + let signal_to_peer = PeerSignal::Disconnect { peer_conn_type: PeerConnectionType::LocalGroupPeer { session_cid, peer_cid: target_cid, }, - disconnect_response: Some(PeerResponse::Disconnected( - "Server has begun disconnection".to_string(), - )), + disconnect_response: resp, }; - reply_to_sender( - rebound_signal, - &sess_stacked_ratchet, - ticket, - timestamp, - security_level, - ) - } else { - //reply_to_sender_err(format!("{} is not connected to {}", session_cid, target_cid), &sess_stacked_ratchet, ticket, timestamp, security_level) - // connection may already be dc'ed from another dc attempt. Just say nothing - Ok(PrimaryProcessorResult::Void) + // now, remove target CID's v_conn to `session_cid` + drop(state_container); + let _ = session.session_manager.disconnect_virtual_conn( + session_cid, + target_cid, + move |peer_ratchet| { + // send signal to peer + packet_crafter::peer_cmd::craft_peer_signal( + peer_ratchet, + signal_to_peer, + ticket, + timestamp, + security_level, + ) + }, + ); } + + // Regardless, always rebound a D/C signal + let rebound_signal = PeerSignal::Disconnect { + peer_conn_type: PeerConnectionType::LocalGroupPeer { + session_cid, + peer_cid: target_cid, + }, + disconnect_response: Some(PeerResponse::Disconnected( + "Server has begun disconnection".to_string(), + )), + }; + + reply_to_sender( + rebound_signal, + &sess_ratchet, + ticket, + timestamp, + security_level, + ) } _ => { @@ -1487,7 +1441,7 @@ async fn process_signal_command_as_server( log::trace!(target: "citadel", "[GetRegisteredPeers] Done getting list"); reply_to_sender( rebound_signal, - &sess_stacked_ratchet, + &sess_ratchet, ticket, timestamp, security_level, @@ -1535,7 +1489,7 @@ async fn process_signal_command_as_server( log::trace!(target: "citadel", "[GetMutuals] Done getting list"); reply_to_sender( rebound_signal, - &sess_stacked_ratchet, + &sess_ratchet, ticket, timestamp, security_level, @@ -1580,7 +1534,7 @@ async fn process_signal_command_as_server( .unbounded_send(NodeResult::PeerEvent(PeerEvent { event: signal.clone(), ticket, - session_cid: sess_stacked_ratchet.get_cid(), + session_cid: sess_ratchet.get_cid(), }))?; let peer_cid = peer_connection_type.get_original_target_cid(); @@ -1593,9 +1547,9 @@ async fn process_signal_command_as_server( let res = inner!(session.session_manager).send_signal_to_peer_direct( peer_cid, - move |peer_stacked_ratchet| { + move |peer_ratchet| { packet_crafter::peer_cmd::craft_peer_signal( - peer_stacked_ratchet, + peer_ratchet, signal, ticket, timestamp, @@ -1607,7 +1561,7 @@ async fn process_signal_command_as_server( if let Err(err) = res { reply_to_sender_err( err, - &sess_stacked_ratchet, + &sess_ratchet, ticket, timestamp, security_level, @@ -1624,7 +1578,7 @@ async fn process_signal_command_as_server( .unbounded_send(NodeResult::PeerEvent(PeerEvent { event: signal, ticket, - session_cid: sess_stacked_ratchet.get_cid(), + session_cid: sess_ratchet.get_cid(), }))?; Ok(PrimaryProcessorResult::Void) } @@ -1644,13 +1598,13 @@ async fn process_signal_command_as_server( /// This just makes the repeated operation above cleaner. By itself does not send anything; must return the result of this closure directly fn reply_to_sender( signal: PeerSignal, - stacked_ratchet: &R, + ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, ) -> Result { let packet = packet_crafter::peer_cmd::craft_peer_signal( - stacked_ratchet, + ratchet, signal, ticket, timestamp, @@ -1661,27 +1615,20 @@ fn reply_to_sender( fn reply_to_sender_err( err: E, - stacked_ratchet: &R, + ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, peer_cid: u64, ) -> Result { Ok(PrimaryProcessorResult::ReplyToSender( - construct_error_signal( - err, - stacked_ratchet, - ticket, - timestamp, - security_level, - peer_cid, - ), + construct_error_signal(err, ratchet, ticket, timestamp, security_level, peer_cid), )) } fn construct_error_signal( err: E, - stacked_ratchet: &R, + ratchet: &R, ticket: Ticket, timestamp: i64, security_level: SecurityLevel, @@ -1691,12 +1638,12 @@ fn construct_error_signal( ticket, error: err.to_string(), peer_connection_type: PeerConnectionType::LocalGroupPeer { - session_cid: stacked_ratchet.get_cid(), + session_cid: ratchet.get_cid(), peer_cid, }, }; packet_crafter::peer_cmd::craft_peer_signal( - stacked_ratchet, + ratchet, err_signal, ticket, timestamp, @@ -1714,21 +1661,21 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( ticket: Ticket, to_primary_stream: &OutboundPrimaryStreamSender, sess_mgr: &CitadelSessionManager, - sess_stacked_ratchet: &R, + sess_ratchet: &R, security_level: SecurityLevel, peer_layer: &mut CitadelNodePeerLayerInner, ) -> Result { - let sess_stacked_ratchet_2 = sess_stacked_ratchet.clone(); + let sess_ratchet_2 = sess_ratchet.clone(); let to_primary_stream = to_primary_stream.clone(); // Give the target_cid 10 seconds to respond - let res = sess_mgr.route_signal_primary(peer_layer, session_cid, target_cid, ticket, signal.clone(), move |peer_stacked_ratchet| { - packet_crafter::peer_cmd::craft_peer_signal(peer_stacked_ratchet, signal.clone(), ticket, timestamp, security_level) + let res = sess_mgr.route_signal_primary(peer_layer, session_cid, target_cid, ticket, signal.clone(), move |peer_ratchet| { + packet_crafter::peer_cmd::craft_peer_signal(peer_ratchet, signal.clone(), ticket, timestamp, security_level) }, timeout, move |stale_signal| { // on timeout, run this // TODO: Use latest ratchet, otherwise, may expire log::warn!(target: "citadel", "Running timeout closure. Sending error message to {}", session_cid); - let error_packet = packet_crafter::peer_cmd::craft_peer_signal(&sess_stacked_ratchet_2, stale_signal, ticket, timestamp, security_level); + let error_packet = packet_crafter::peer_cmd::craft_peer_signal(&sess_ratchet_2, stale_signal, ticket, timestamp, security_level); let _ = to_primary_stream.unbounded_send(error_packet); }).await; @@ -1736,7 +1683,7 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( if let Err(err) = res { reply_to_sender_err( err, - sess_stacked_ratchet, + sess_ratchet, ticket, timestamp, security_level, @@ -1746,7 +1693,7 @@ pub(crate) async fn route_signal_and_register_ticket_forwards( let received_signal = PeerSignal::SignalReceived { ticket }; reply_to_sender( received_signal, - sess_stacked_ratchet, + sess_ratchet, ticket, timestamp, security_level, @@ -1763,7 +1710,7 @@ pub(crate) async fn route_signal_response( timestamp: i64, ticket: Ticket, session: CitadelSession, - sess_stacked_ratchet: &R, + sess_ratchet: &R, on_route_finished: impl FnOnce(&CitadelSession, &CitadelSession, PeerSignal), security_level: SecurityLevel, ) -> Result { @@ -1777,9 +1724,9 @@ pub(crate) async fn route_signal_response( target_cid, ticket, sess_ref, - move |peer_stacked_ratchet| { + move |peer_ratchet| { packet_crafter::peer_cmd::craft_peer_signal( - peer_stacked_ratchet, + peer_ratchet, signal, ticket, timestamp, @@ -1791,7 +1738,7 @@ pub(crate) async fn route_signal_response( let received_signal = PeerSignal::SignalReceived { ticket }; let ret = reply_to_sender( received_signal, - sess_stacked_ratchet, + sess_ratchet, ticket, timestamp, security_level, @@ -1811,7 +1758,7 @@ pub(crate) async fn route_signal_response( log::warn!(target: "citadel", "Unable to route signal! {:?}", err); reply_to_sender_err( err, - sess_stacked_ratchet, + sess_ratchet, ticket, timestamp, security_level, diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs index b33ddf6ba..d817017f1 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_connect.rs @@ -59,7 +59,7 @@ pub(crate) async fn handle_response_phase_post_connect( target_cid: u64, timestamp: i64, session: &CitadelSession, - sess_stacked_ratchet: &R, + sess_ratchet: &R, security_level: SecurityLevel, ) -> Result { // the signal is going to be routed from HyperLAN Client B to HyperLAN client A (response phase) @@ -70,7 +70,7 @@ pub(crate) async fn handle_response_phase_post_connect( session_security_settings: endpoint_security_level, udp_mode: udp_enabled, session_password: None, - }, session_cid, target_cid, timestamp, ticket, session.clone(), sess_stacked_ratchet, + }, session_cid, target_cid, timestamp, ticket, session.clone(), sess_ratchet, |this_sess, peer_sess, _original_tracked_posting| { // when the route finishes, we need to update both sessions to allow high-level message-passing // In other words, forge a virtual connection diff --git a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs index 22ce1fe43..c4bd27f2b 100644 --- a/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs +++ b/citadel_proto/src/proto/packet_processor/peer/server/post_register.rs @@ -56,7 +56,7 @@ pub async fn handle_response_phase_post_register( target_cid: u64, timestamp: i64, session: &CitadelSession, - sess_stacked_ratchet: &R, + sess_ratchet: &R, security_level: SecurityLevel, ) -> Result { let decline = matches!(&peer_response, PeerResponse::Decline); @@ -67,7 +67,7 @@ pub async fn handle_response_phase_post_register( invitee_username: None, ticket_opt: Some(ticket), invitee_response: Some(peer_response), - }, session_cid, target_cid, timestamp, ticket, session.clone(), sess_stacked_ratchet, + }, session_cid, target_cid, timestamp, ticket, session.clone(), sess_ratchet, |this_sess, _peer_sess, _original_tracked_posting| { if !decline { let account_manager = this_sess.account_manager.clone(); @@ -78,9 +78,7 @@ pub async fn handle_response_phase_post_register( } }; - let handle = citadel_io::tokio::task::spawn(task); - // dont process the handle - std::mem::drop(handle); + spawn!(task); } }, security_level).await } diff --git a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs index 3825256c2..2f676998f 100644 --- a/citadel_proto/src/proto/packet_processor/preconnect_packet.rs +++ b/citadel_proto/src/proto/packet_processor/preconnect_packet.rs @@ -39,7 +39,7 @@ use crate::proto::misc::udp_internal_interface::{ QuicUdpSocketConnector, RawUdpSocketConnector, UdpSplittableTypes, }; use crate::proto::packet::packet_flags::payload_identifiers; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; use crate::proto::peer::hole_punch_compat_sink_stream::ReliableOrderedCompatStream; use crate::proto::state_container::{StateContainerInner, VirtualTargetType}; use citadel_types::proto::UdpMode; @@ -113,7 +113,8 @@ pub async fn process_preconnect( .await? { let mut state_container = inner_mut_state!(session.state_container); - + state_container + .store_session_password(C2S_IDENTITY_CID, session.session_password.clone()); match validation::pre_connect::validate_syn( &cnac, packet, @@ -128,11 +129,10 @@ pub async fn process_preconnect( udp_mode, kat, nat_type, - new_stacked_ratchet, + new_ratchet, )) => { session.adjacent_nat_type.set_once(Some(nat_type)); - state_container.pre_connect_state.generated_ratchet = - Some(new_stacked_ratchet); + state_container.pre_connect_state.generated_ratchet = Some(new_ratchet); // since the SYN's been validated, the CNACs toolset has been updated let new_session_sec_lvl = transfer.security_level(); @@ -188,7 +188,7 @@ pub async fn process_preconnect( )); let session_cid = header.session_cid.get(); - let (stream, new_stacked_ratchet) = { + let (stream, new_ratchet) = { let mut state_container = inner_mut_state!(session.state_container); if state_container.pre_connect_state.last_stage == packet_flags::cmd::aux::do_preconnect::SYN_ACK @@ -199,7 +199,7 @@ pub async fn process_preconnect( "Alice constructor not loaded" ); let session_cid = header.session_cid.get(); - if let Some((new_stacked_ratchet, nat_type)) = + if let Some((new_ratchet, nat_type)) = validation::pre_connect::validate_syn_ack( &session.session_password, cnac, @@ -209,7 +209,7 @@ pub async fn process_preconnect( { session.adjacent_nat_type.set_once(Some(nat_type)); state_container.pre_connect_state.generated_ratchet = - Some(new_stacked_ratchet.clone()); + Some(new_ratchet.clone()); let local_node_type = session.local_node_type; let timestamp = session.time_tracker.get_global_time_ns(); @@ -217,7 +217,7 @@ pub async fn process_preconnect( if state_container.udp_mode == UdpMode::Disabled { let stage0_preconnect_packet = packet_crafter::pre_connect::craft_stage0( - &new_stacked_ratchet, + &new_ratchet, timestamp, local_node_type, security_level, @@ -239,7 +239,7 @@ pub async fn process_preconnect( quic_conn, session.local_bind_addr, )), - &new_stacked_ratchet, + &new_ratchet, session, security_level, session_cid, @@ -249,7 +249,7 @@ pub async fn process_preconnect( let stage0_preconnect_packet = packet_crafter::pre_connect::craft_stage0( - &new_stacked_ratchet, + &new_ratchet, timestamp, local_node_type, security_level, @@ -263,11 +263,11 @@ pub async fn process_preconnect( let stream = ReliableOrderedCompatStream::::new( to_primary_stream, &mut state_container, - C2S_ENCRYPTION_ONLY, - new_stacked_ratchet.clone(), + C2S_IDENTITY_CID, + new_ratchet.clone(), security_level, ); - (stream, new_stacked_ratchet) + (stream, new_ratchet) } else { log::error!(target: "citadel", "Invalid SYN_ACK"); return Ok(PrimaryProcessorResult::Void); @@ -285,9 +285,9 @@ pub async fn process_preconnect( let stun_servers = session.stun_servers.clone(); let res = conn .begin_udp_hole_punch(generate_hole_punch_crypt_container( - new_stacked_ratchet.clone(), + new_ratchet.clone(), SecurityLevel::Standard, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, stun_servers, )) .await; @@ -297,7 +297,7 @@ pub async fn process_preconnect( log::trace!(target: "citadel", "Initiator finished NAT traversal ..."); send_success_as_initiator( Some(get_raw_udp_interface(ret)), - &new_stacked_ratchet, + &new_ratchet, session, security_level, session_cid, @@ -309,7 +309,7 @@ pub async fn process_preconnect( log::warn!(target: "citadel", "Hole punch attempt failed {:?}", err.to_string()); send_success_as_initiator( None, - &new_stacked_ratchet, + &new_ratchet, session, security_level, session_cid, @@ -323,10 +323,10 @@ pub async fn process_preconnect( log::trace!(target: "citadel", "RECV STAGE 0 PRE_CONNECT PACKET"); let session_cid = header.session_cid.get(); - let (stacked_ratchet, stream) = { + let (ratchet, stream) = { let mut state_container = inner_mut_state!(session.state_container); // At this point, the user's static-key identity has been verified. We can now check the online status to ensure no double-logins - let stacked_ratchet = return_if_none!( + let ratchet = return_if_none!( get_orientation_safe_ratchet( header.entropy_bank_version.get(), &state_container, @@ -338,9 +338,7 @@ pub async fn process_preconnect( if state_container.pre_connect_state.last_stage == packet_flags::cmd::aux::do_preconnect::SYN_ACK { - if validation::pre_connect::validate_stage0(&stacked_ratchet, packet) - .is_some() - { + if validation::pre_connect::validate_stage0(&ratchet, packet).is_some() { let timestamp = session.time_tracker.get_global_time_ns(); //let peer_nat_type = return_if_none!(session.adjacent_nat_type.clone(), "adjacent NAT type not loaded"); @@ -351,7 +349,7 @@ pub async fn process_preconnect( // We have to modify the state to ensure that this node can receive a DO_CONNECT packet state_container.pre_connect_state.success = true; let packet = packet_crafter::pre_connect::craft_begin_connect( - &stacked_ratchet, + &ratchet, timestamp, security_level, ); @@ -367,11 +365,11 @@ pub async fn process_preconnect( let stream = ReliableOrderedCompatStream::::new( to_primary_stream, &mut state_container, - C2S_ENCRYPTION_ONLY, - stacked_ratchet.clone(), + C2S_IDENTITY_CID, + ratchet.clone(), security_level, ); - (stacked_ratchet, stream) + (ratchet, stream) } else { log::error!(target: "citadel", "Unable to validate stage 0 packet"); return Ok(PrimaryProcessorResult::Void); @@ -389,9 +387,9 @@ pub async fn process_preconnect( let stun_servers = session.stun_servers.clone(); let res = conn .begin_udp_hole_punch(generate_hole_punch_crypt_container( - stacked_ratchet.clone(), + ratchet.clone(), SecurityLevel::Standard, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, stun_servers, )) .await; @@ -436,8 +434,7 @@ pub async fn process_preconnect( let cnac = &(return_if_none!(state_container.cnac.clone(), "Sess CNAC not loaded")); let tcp_only = header.algorithm == payload_identifiers::do_preconnect::TCP_ONLY; let (header, packet, ..) = packet.decompose(); - if let Some((header, _, stacked_ratchet)) = - validation::aead::validate(hr, &header, packet) + if let Some((header, _, ratchet)) = validation::aead::validate(hr, &header, packet) { state_container.pre_connect_state.success = true; if !success { @@ -448,7 +445,7 @@ pub async fn process_preconnect( if tcp_only { log::warn!(target: "citadel", "Received signal to fall-back to TCP only mode"); let begin_connect = packet_crafter::pre_connect::craft_begin_connect( - &stacked_ratchet, + &ratchet, timestamp, security_level, ); @@ -488,7 +485,7 @@ pub async fn process_preconnect( )) } else { let begin_connect = packet_crafter::pre_connect::craft_begin_connect( - &stacked_ratchet, + &ratchet, timestamp, security_level, ); @@ -513,13 +510,12 @@ pub async fn process_preconnect( == packet_flags::cmd::aux::do_preconnect::SUCCESS { let (header, payload, _, _) = packet.decompose(); - if let Some((_, _, stacked_ratchet)) = - validation::aead::validate(hr, &header, payload) + if let Some((_, _, ratchet)) = validation::aead::validate(hr, &header, payload) { state_container.pre_connect_state.success = true; std::mem::drop(state_container); // now, begin stage 0 connect - begin_connect_process(session, &stacked_ratchet, security_level) + begin_connect_process(session, &ratchet, security_level) } else { log::error!(target: "citadel", "Unable to validate success_ack packet. Dropping"); Ok(PrimaryProcessorResult::Void) @@ -557,7 +553,7 @@ pub async fn process_preconnect( fn begin_connect_process( session: &CitadelSession, - stacked_ratchet: &R, + ratchet: &R, security_level: SecurityLevel, ) -> Result { // at this point, the session keys have already been re-established. We just need to begin the login stage @@ -569,7 +565,7 @@ fn begin_connect_process( ); let stage0_connect_packet = crate::proto::packet_crafter::do_connect::craft_stage0_packet( - stacked_ratchet, + ratchet, proposed_credentials, timestamp, security_level, @@ -589,7 +585,7 @@ fn begin_connect_process( fn send_success_as_initiator( udp_splittable: Option, - stacked_ratchet: &R, + ratchet: &R, session: &CitadelSession, security_level: SecurityLevel, session_cid: u64, @@ -598,7 +594,7 @@ fn send_success_as_initiator( let _ = handle_success_as_receiver(udp_splittable, session, session_cid, state_container)?; let success_packet = packet_crafter::pre_connect::craft_stage_final( - stacked_ratchet, + ratchet, true, false, session.time_tracker.get_global_time_ns(), @@ -649,28 +645,24 @@ fn handle_success_as_receiver( } pub(crate) fn generate_hole_punch_crypt_container( - stacked_ratchet: R, + ratchet: R, security_level: SecurityLevel, target_cid: u64, stun_servers: Option>, ) -> HolePunchConfigContainer { - let stacked_ratchet_cloned = stacked_ratchet.clone(); + let ratchet_cloned = ratchet.clone(); HolePunchConfigContainer::new( move |plaintext| { packet_crafter::hole_punch::generate_packet( - &stacked_ratchet, + &ratchet, plaintext, security_level, target_cid, ) }, move |packet| { - packet_crafter::hole_punch::decrypt_packet( - &stacked_ratchet_cloned, - packet, - security_level, - ) + packet_crafter::hole_punch::decrypt_packet(&ratchet_cloned, packet, security_level) }, stun_servers, ) diff --git a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs index ab22d4450..257fd593d 100644 --- a/citadel_proto/src/proto/packet_processor/primary_group_packet.rs +++ b/citadel_proto/src/proto/packet_processor/primary_group_packet.rs @@ -34,18 +34,13 @@ use crate::constants::GROUP_EXPIRE_TIME_MS; use crate::error::NetworkError; use crate::functional::IfTrueConditional; use crate::inner_arg::ExpectedInnerTarget; -use crate::prelude::{InternalServerError, PreSharedKey}; +use crate::prelude::InternalServerError; use crate::proto::node_result::OutboundRequestRejected; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; use crate::proto::session_queue_handler::QueueWorkerResult; use crate::proto::state_container::{FileKey, GroupKey, StateContainerInner}; use crate::proto::validation::group::{GroupHeader, GroupHeaderAck, WaveAck}; -use citadel_crypt::endpoint_crypto_container::{ - EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, -}; -use citadel_crypt::misc::CryptError; use citadel_crypt::ratchets::Ratchet; -use citadel_types::crypto::SecrecyMode; use citadel_types::prelude::ObjectId; use citadel_types::proto::UdpMode; use std::ops::Deref; @@ -99,7 +94,7 @@ pub fn process_primary_packet( let header_bytes = &header[..]; let header = return_if_none!(Ref::new(header_bytes), "Unable to load header [PGP]") as Ref<&[u8], HdpHeader>; - let stacked_ratchet = return_if_none!( + let ratchet = return_if_none!( get_orientation_safe_ratchet( header.entropy_bank_version.get(), &state_container, @@ -108,16 +103,12 @@ pub fn process_primary_packet( "Unable to get proper StackedRatchet [PGP]" ); let security_level = header.security_level.into(); - //log::trace!(target: "citadel", "[Peer StackedRatchet] Obtained version {} w/ CID {} (local CID: {})", stacked_ratchet.version(), stacked_ratchet.get_cid(), header.session_cid.get()); + //log::trace!(target: "citadel", "[Peer StackedRatchet] Obtained version {} w/ CID {} (local CID: {})", ratchet.version(), ratchet.get_cid(), header.session_cid.get()); match header.cmd_aux { packet_flags::cmd::aux::group::GROUP_PAYLOAD => { log::trace!(target: "citadel", "RECV GROUP PAYLOAD {:?}", header); // These packets do not get encrypted with the message key. They get scrambled and encrypted - match state_container.on_group_payload_received( - &header, - payload.freeze(), - &stacked_ratchet, - ) { + match state_container.on_group_payload_received(&header, payload.freeze(), &ratchet) { Ok(res) => { state_container.meta_expiry_state.on_event_confirmation(); Ok(res) @@ -147,7 +138,7 @@ pub fn process_primary_packet( // Finally, alert the adjacent endpoint by crafting an error packet let error_packet = packet_crafter::file::craft_file_error_packet( - &stacked_ratchet, + &ratchet, ticket, security_level, v_conn, @@ -161,163 +152,144 @@ pub fn process_primary_packet( } _ => { - match validation::aead::validate_custom(&stacked_ratchet, &*header, payload) { - Some((header, mut payload)) => { + match validation::aead::validate_custom(&ratchet, &*header, payload) { + Some((header, payload)) => { state_container.meta_expiry_state.on_event_confirmation(); match cmd_aux { packet_flags::cmd::aux::group::GROUP_HEADER => { log::trace!(target: "citadel", "RECV GROUP HEADER"); - let is_message = header.algorithm == 1; - if is_message { - let (plaintext, transfer, object_id) = return_if_none!( - validation::group::validate_message::(&mut payload), - "Bad GROUP HEADER packet" - ); - log::trace!(target: "citadel", "Recv FastMessage. version {} w/ CID {} (local CID: {})", stacked_ratchet.version(), stacked_ratchet.get_cid(), header.session_cid.get()); - // Here, we do not go through all the fiasco like above. We just forward the message to the kernel, then send an ACK - // so that the sending side can be notified of a successful send - let resp_target_cid = get_resp_target_cid_from_header(&header); - log::trace!(target: "citadel", "Resp target cid {} obtained. version {} w/ CID {} (local CID: {})", resp_target_cid, stacked_ratchet.version(), stacked_ratchet.get_cid(), header.session_cid.get()); - let ticket = header.context_info.get().into(); - // we call this to ensure a flood of these packets doesn't cause ordinary groups from being dropped - - // now, update the keys (if applicable) - let transfer = return_if_none!( - attempt_kem_as_bob( - session, - resp_target_cid, - &header, - transfer, - &mut state_container, - &stacked_ratchet - ), - "Unable to attempt_kem_as_bob [PGP]" - ); - - let target_cid = - if let Some((original_session_cid, _original_target_cid)) = - proxy_cid_info + let group_header = return_if_none!( + validation::group::validate_header(&payload), + "Bad non-message group header" + ); + + match group_header { + GroupHeader::Ratchet(ratchet_message, object_id) => { + log::trace!(target: "citadel", "Recv FastMessage. version {} w/ CID {} (local CID: {})", ratchet.version(), ratchet.get_cid(), header.session_cid.get()); + // Here, we do not go through all the fiasco like above. We just forward the message to the kernel, then send an ACK + // so that the sending side can be notified of a successful send + let resp_target_cid = get_resp_target_cid_from_header(&header); + log::trace!(target: "citadel", "Resp target cid {} obtained. version {} w/ CID {} (local CID: {})", resp_target_cid, ratchet.version(), ratchet.get_cid(), header.session_cid.get()); + let ticket = header.context_info.get().into(); + // we call this to ensure a flood of these packets doesn't cause ordinary groups from being dropped + // let v_conn = get_v_conn_from_header(&header); + let target_cid = + if let Some((original_session_cid, _original_target_cid)) = + proxy_cid_info + { + original_session_cid + } else { + 0 + }; + + if let Err(err) = state_container + .forward_data_to_ordered_channel( + target_cid, + header.group.get(), + ratchet_message, + ) { - original_session_cid - } else { - 0 - }; + log::error!(target: "citadel", "Unable to forward data to channel (peer: {target_cid}): {err:?}"); + return Ok(PrimaryProcessorResult::Void); + } - if !state_container.forward_data_to_ordered_channel( - target_cid, - header.group.get(), - plaintext, - ) { - log::error!(target: "citadel", "Unable to forward data to channel (peer: {})", target_cid); - return Ok(PrimaryProcessorResult::Void); + let group_header_ack = + packet_crafter::group::craft_group_header_ack( + &ratchet, + header.group.get(), + resp_target_cid, + object_id, + ticket, + None, + true, + timestamp, + security_level, + ); + + Ok(PrimaryProcessorResult::ReplyToSender(group_header_ack)) } - let group_header_ack = - packet_crafter::group::craft_group_header_ack( - &stacked_ratchet, - header.group.get(), - resp_target_cid, - object_id, - ticket, - None, - true, - timestamp, - transfer, - security_level, + GroupHeader::Standard(group_receiver_config, virtual_target) => { + // First, check to make sure the virtual target can accept + let object_id = group_receiver_config.object_id; + let ticket = header.context_info.get().into(); + + //let sess_session_cid = session.session_cid.load(Ordering::Relaxed)?; + //let target_cid_header = header.target_cid.get(); + // for HyperLAN conns, this is true + + let resp_target_cid = return_if_none!( + get_resp_target_cid(&virtual_target), + "Unable to get resp_target_cid [PGP]" ); - Ok(PrimaryProcessorResult::ReplyToSender(group_header_ack)) - } else { - let group_header = return_if_none!( - validation::group::validate_header(&payload), - "Bad non-message group header" - ); - match group_header { - GroupHeader::Standard( - group_receiver_config, - virtual_target, - ) => { - // First, check to make sure the virtual target can accept - let object_id = group_receiver_config.object_id; - let ticket = header.context_info.get().into(); - - //let sess_session_cid = session.session_cid.load(Ordering::Relaxed)?; - //let target_cid_header = header.target_cid.get(); - // for HyperLAN conns, this is true - - let resp_target_cid = return_if_none!( - get_resp_target_cid(&virtual_target), - "Unable to get resp_target_cid [PGP]" - ); - // the below will return None if not ready to accept - let initial_wave_window = state_container - .on_group_header_received( - &header, - group_receiver_config, - virtual_target, - ); - if initial_wave_window.is_some() { - // register group timeout device - //std::mem::drop(state_container); - let group_id = header.group.get(); - let peer_cid = header.session_cid.get(); - - session.queue_handle.insert_ordinary(group_id as usize, peer_cid, GROUP_EXPIRE_TIME_MS, move |state_container| { - let key = GroupKey::new(peer_cid, group_id, object_id); - if let Some(group) = state_container.inbound_groups.get(&key) { - if group.has_begun { - if group.receiver.has_expired(GROUP_EXPIRE_TIME_MS) { - if state_container.meta_expiry_state.expired() { - log::warn!(target: "citadel", "Inbound group {} has expired; removing for {}.", group_id, peer_cid); - if let Some(group) = state_container.inbound_groups.remove(&key) { - if group.object_id != ObjectId::zero() { - // belongs to a file. Delete file; stop transmission - let key = FileKey::new(group.object_id); - if let Some(_file) = state_container.inbound_files.remove(&key) { - // dropping this will automatically drop the future streaming to HD - log::warn!(target: "citadel", "File transfer expired"); - // TODO: Create file FIN - } - - let _ = state_container.file_transfer_handles.remove(&key); + // the below will return None if not ready to accept + let initial_wave_window = state_container + .on_group_header_received( + &header, + group_receiver_config, + virtual_target, + ); + if initial_wave_window.is_some() { + // register group timeout device + //std::mem::drop(state_container); + let group_id = header.group.get(); + let peer_cid = header.session_cid.get(); + + session.queue_handle.insert_ordinary(group_id as usize, peer_cid, GROUP_EXPIRE_TIME_MS, move |state_container| { + let key = GroupKey::new(peer_cid, group_id, object_id); + if let Some(group) = state_container.inbound_groups.get(&key) { + if group.has_begun { + if group.receiver.has_expired(GROUP_EXPIRE_TIME_MS) { + if state_container.meta_expiry_state.expired() { + log::warn!(target: "citadel", "Inbound group {} has expired; removing for {}.", group_id, peer_cid); + if let Some(group) = state_container.inbound_groups.remove(&key) { + if group.object_id != ObjectId::zero() { + // belongs to a file. Delete file; stop transmission + let key = FileKey::new(group.object_id); + if let Some(_file) = state_container.inbound_files.remove(&key) { + // dropping this will automatically drop the future streaming to HD + log::warn!(target: "citadel", "File transfer expired"); + // TODO: Create file FIN } - } - QueueWorkerResult::Complete - } else { - log::trace!(target: "citadel", "Other inbound groups being processed; patiently awaiting group {}", group_id); - QueueWorkerResult::Incomplete + let _ = state_container.file_transfer_handles.remove(&key); + } } + + QueueWorkerResult::Complete } else { - // The inbound group is still receiving, and it hasn't expired. Keep polling + log::trace!(target: "citadel", "Other inbound groups being processed; patiently awaiting group {}", group_id); QueueWorkerResult::Incomplete } } else { - // group has not started; previous group is still transferring. Do no interrupt transfer + // The inbound group is still receiving, and it hasn't expired. Keep polling QueueWorkerResult::Incomplete } } else { - // has been removed, thus is complete - QueueWorkerResult::Complete + // group has not started; previous group is still transferring. Do no interrupt transfer + QueueWorkerResult::Incomplete } - }); - } - - let group_header_ack = - packet_crafter::group::craft_group_header_ack( - &stacked_ratchet, - header.group.get(), - resp_target_cid, - object_id, - ticket, - initial_wave_window, - false, - timestamp, - KemTransferStatus::Empty, - security_level, - ); - Ok(PrimaryProcessorResult::ReplyToSender(group_header_ack)) + } else { + // has been removed, thus is complete + QueueWorkerResult::Complete + } + }); } + + let group_header_ack = + packet_crafter::group::craft_group_header_ack( + &ratchet, + header.group.get(), + resp_target_cid, + object_id, + ticket, + initial_wave_window, + false, + timestamp, + security_level, + ); + Ok(PrimaryProcessorResult::ReplyToSender(group_header_ack)) } } } @@ -327,7 +299,6 @@ pub fn process_primary_packet( match validation::group::validate_header_ack(&payload) { Some(GroupHeaderAck::ReadyToReceive { initial_window, - transfer, fast_msg, object_id, }) => { @@ -341,22 +312,9 @@ pub fn process_primary_packet( let resp_target_cid = get_resp_target_cid_from_header(&header); let peer_cid = header.session_cid.get(); - //let mut state_container = session.state_container.borrow_mut(); let group_id = header.group.get(); - let target_cid = header.target_cid.get(); - let needs_truncate = transfer.requires_truncation(); - - let transfer_occurred = transfer.has_some(); - let secrecy_mode = return_if_none!( - state_container - .session_security_settings - .as_ref() - .map(|r| r.secrecy_mode), - "Unable to get secrecy mode [PGP]" - ); - - if resp_target_cid != C2S_ENCRYPTION_ONLY { + if resp_target_cid != C2S_IDENTITY_CID { // If there is a pending disconnect, we need to make sure the session gets dropped until after all packets get processed let vconn = return_if_none!( state_container @@ -371,50 +329,12 @@ pub fn process_primary_packet( // TODO: make the below function return a result, not bools if state_container.on_group_header_ack_received( - session, - secrecy_mode, peer_cid, - target_cid, group_id, object_id, initial_wave_window, - transfer, fast_msg, ) { - //std::mem::drop(state_container); - log::trace!(target: "citadel", "[Toolset Update] Needs truncation? {:?}", &needs_truncate); - - //session.send_to_kernel(HdpServerResult::MessageDelivered(header.context_info.get().into()))?; - // now, we need to do one last thing. We need to send a truncate packet to at least allow bob to begin sending packets using the latest HR - // we need to send a truncate packet. BUT, only if the package was SOME. Just b/c it is some does not mean a truncation is necessary - if transfer_occurred { - let target_cid = if target_cid != C2S_ENCRYPTION_ONLY { - peer_cid - } else { - C2S_ENCRYPTION_ONLY - }; - let truncate_packet = - packet_crafter::do_entropy_bank_update::craft_truncate( - &stacked_ratchet, - needs_truncate, - target_cid, - timestamp, - security_level, - ); - log::trace!(target: "citadel", "About to send TRUNCATE packet to MAYBE remove v {:?} | HR v {} | HR CID {}", needs_truncate, stacked_ratchet.version(), stacked_ratchet.get_cid()); - session - .send_to_primary_stream(None, truncate_packet)?; - } - - //std::mem::drop(state_container); - - // if a transfer occurred, we will get polled once we get an TRUNCATE_ACK. No need to double poll - if secrecy_mode == SecrecyMode::Perfect { - log::trace!(target: "citadel", "Polling next in pgp"); - let _ = state_container - .poll_next_enqueued(resp_target_cid)?; - } - Ok(PrimaryProcessorResult::Void) } else if udp_mode == UdpMode::Disabled { Ok(PrimaryProcessorResult::EndSession( @@ -479,7 +399,7 @@ pub fn process_primary_packet( // the window is done. Since this node is the transmitter, we then make a call to begin sending the next wave if !state_container - .on_wave_ack_received(stacked_ratchet.get_cid(), &header) + .on_wave_ack_received(ratchet.get_cid(), &header) { if udp_mode == UdpMode::Disabled { log::error!(target: "citadel", "There was an error sending the TCP window; Cancelling connection"); @@ -535,9 +455,7 @@ pub(super) fn get_orientation_safe_ratchet( .get(&original_session_cid) { //log::trace!(target: "citadel", "[Peer StackedRatchet] v{} from vconn w/ {}", header_entropy_bank_vers, original_session_cid); - vconn - .borrow_endpoint_stacked_ratchet(Some(header_entropy_bank_vers)) - .cloned() + vconn.get_endpoint_ratchet(Some(header_entropy_bank_vers)) } else { log::warn!(target: "citadel", "Unable to find vconn for {}. Unable to process primary group packet", original_session_cid); None @@ -548,11 +466,10 @@ pub(super) fn get_orientation_safe_ratchet( state_container.pre_connect_state.generated_ratchet.clone() } else { state_container - .c2s_channel_container - .as_ref()? - .peer_session_crypto + .get_endpoint_container(C2S_IDENTITY_CID) + .ok()? + .ratchet_manager .get_ratchet(Some(header_entropy_bank_vers)) - .cloned() } } } @@ -586,238 +503,18 @@ pub fn get_resp_target_cid(virtual_target: &VirtualConnectionType) -> Option u64 { - if header.target_cid.get() != C2S_ENCRYPTION_ONLY { + if header.target_cid.get() != C2S_IDENTITY_CID { header.session_cid.get() } else { - C2S_ENCRYPTION_ONLY - } -} - -#[allow(unused)] -pub struct ToolsetUpdate<'a, R: Ratchet> { - pub(crate) crypt: &'a mut PeerSessionCrypto, - pub(crate) local_cid: u64, -} - -impl ToolsetUpdate<'_, R> { - pub(crate) fn update( - &mut self, - constructor: R::Constructor, - local_is_alice: bool, - ) -> Result, CryptError> { - self.crypt.update_sync_safe(constructor, local_is_alice) - } - - /// This should only be called after an update - pub(crate) fn post_stage1_alice_or_bob(&mut self) { - self.crypt.post_alice_stage1_or_post_stage1_bob() + C2S_IDENTITY_CID } - - pub(crate) fn deregister(&mut self, version: u32) -> Result<(), NetworkError> { - self.crypt - .deregister_oldest_stacked_ratchet(version) - .map_err(|err| NetworkError::Generic(err.to_string())) - } - - /// Unlocks the internal state, allowing future upgrades to the system. Returns the latest hyper ratchet - pub(crate) fn unlock(&mut self, _requires_locked_by_alice: bool) -> Option { - self.crypt.maybe_unlock().cloned() - } - - pub(crate) fn get_local_cid(&self) -> u64 { - self.local_cid - } - - pub(crate) fn get_latest_ratchet(&self) -> Option<&R> { - self.crypt.get_ratchet(None) - } -} - -/// peer_cid: from header.session_cid -/// target_cid: from header.target_cid -/// Returns: Ok(latest_stacked_ratchet) -pub(crate) fn attempt_kem_as_alice_finish( - session: &CitadelSession, - base_session_secrecy_mode: SecrecyMode, - peer_cid: u64, - target_cid: u64, - transfer: KemTransferStatus, - state_container: &mut StateContainerInner, - constructor: Option, -) -> Result, ()> { - let (mut toolset_update_method, secrecy_mode, session_password) = if target_cid - != C2S_ENCRYPTION_ONLY - { - let session_password = state_container.get_session_password(peer_cid).cloned(); - let endpoint_container = state_container - .active_virtual_connections - .get_mut(&peer_cid) - .ok_or(())? - .endpoint_container - .as_mut() - .ok_or(())?; - let crypt = &mut endpoint_container.endpoint_crypto; - if session_password.is_none() { - log::error!(target: "citadel", "Session password not found for peer_cid {}", peer_cid); - return Err(()); - } - ( - ToolsetUpdate { - crypt, - local_cid: target_cid, - }, - endpoint_container.default_security_settings.secrecy_mode, - session_password.unwrap(), - ) - } else { - let crypt = &mut state_container - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; - ( - ToolsetUpdate { - crypt, - local_cid: peer_cid, - }, - base_session_secrecy_mode, - session.session_password.clone(), - ) - }; - - let requires_truncation = transfer.requires_truncation(); - - match transfer { - KemTransferStatus::Some(transfer, ..) => { - if let Some(mut constructor) = constructor { - if let Err(err) = constructor.stage1_alice(transfer, session_password.as_ref()) { - log::error!(target: "citadel", "Unable to construct hyper ratchet {:?}", err); - return Err(()); - } - - if let Err(err) = toolset_update_method.update(constructor, true) { - log::error!(target: "citadel", "Unable to update container (X-01) | {:?}", err); - return Err(()); - } - - if let Some(version) = requires_truncation { - if let Err(err) = toolset_update_method.deregister(version) { - log::error!(target: "citadel", "[Toolset Update] Unable to update Alice's toolset: {:?}", err); - return Err(()); - } - } - - // Since alice has updated, and bob has the latest ratchet committed (but not yet able to use it), we can begin sending packets from the latest version to bob - // in order for bob to begin using the latest version, he needs to receive the TRUNCATE_STATUS packet - toolset_update_method.post_stage1_alice_or_bob(); - - match secrecy_mode { - SecrecyMode::Perfect | SecrecyMode::BestEffort => { - if requires_truncation.is_some() { - // we unlock once we get the truncate ack - Ok(Some( - toolset_update_method - .get_latest_ratchet() - .cloned() - .ok_or(())?, - )) - } else { - Ok(Some(toolset_update_method.unlock(true).ok_or(())?)) - } - } - } - } else { - log::error!(target: "citadel", "No constructor, yet, KemTransferStatus is Some??"); - Ok(None) - } - } - - KemTransferStatus::Contended => match secrecy_mode { - SecrecyMode::Perfect => Ok(Some(toolset_update_method.unlock(true).ok_or(())?)), - SecrecyMode::BestEffort => Ok(Some(toolset_update_method.unlock(true).ok_or(())?)), - }, - - KemTransferStatus::StatusNoTransfer(_status) => { - log::error!(target: "citadel", "Unaccounted program logic @ StatusNoTransfer! Report to developers"); - Err(()) - } - - _ => Ok(None), - } -} - -/// NOTE! Assumes the `hr` passed is the latest version IF the transfer is some -pub(crate) fn attempt_kem_as_bob( - session: &CitadelSession, - resp_target_cid: u64, - header: &Ref<&[u8], HdpHeader>, - transfer: Option<>::AliceToBobWireTransfer>, - state_container: &mut StateContainerInner, - hr: &R, -) -> Option> { - if let Some(transfer) = transfer { - let (update, session_password) = if resp_target_cid != C2S_ENCRYPTION_ONLY { - let session_password = state_container - .get_session_password(resp_target_cid) - .cloned(); - if session_password.is_none() { - log::error!(target: "citadel", "Session password not found for peer_cid {}", resp_target_cid); - return None; - } - - let crypt = &mut state_container - .active_virtual_connections - .get_mut(&resp_target_cid)? - .endpoint_container - .as_mut()? - .endpoint_crypto; - ( - ToolsetUpdate { - crypt, - local_cid: header.target_cid.get(), - }, - session_password.unwrap(), - ) - } else { - let crypt = &mut state_container - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; - ( - ToolsetUpdate { - crypt, - local_cid: header.session_cid.get(), - }, - session.session_password.clone(), - ) - }; - - update_toolset_as_bob(update, transfer, hr, session_password) - } else { - Some(KemTransferStatus::Empty) - } -} - -pub(crate) fn update_toolset_as_bob( - mut update_method: ToolsetUpdate<'_, R>, - transfer: >::AliceToBobWireTransfer, - hr: &R, - session_password: PreSharedKey, -) -> Option> { - let cid = update_method.get_local_cid(); - let opts = hr.get_next_constructor_opts(); - - let constructor = - EndpointRatchetConstructor::::new_bob(cid, opts, transfer, session_password.as_ref())?; - update_method.update(constructor, false).ok() } /// Returns the virtual connection type for the response target cid. Is relative to the current node, not the receiving node pub fn get_v_conn_from_header(header: &HdpHeader) -> VirtualConnectionType { let target_cid = header.session_cid.get(); let session_cid = header.target_cid.get(); - if target_cid != C2S_ENCRYPTION_ONLY { + if target_cid != C2S_IDENTITY_CID { VirtualConnectionType::LocalGroupPeer { session_cid, peer_cid: target_cid, diff --git a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs index bd40ebdda..bf16f468b 100644 --- a/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs +++ b/citadel_proto/src/proto/packet_processor/raw_primary_packet.rs @@ -62,6 +62,11 @@ pub async fn process_raw_packet( ) -> Result { //return_if_none!(header_obfuscator.on_packet_received(&mut packet)); let packet = HdpPacket::new_recv(packet, remote_peer, local_primary_port); + if packet.parse().is_none() { + log::warn!(target: "citadel", "Unable to parse packet {:?} | Len: {}", packet.as_bytes(), packet.get_length()); + return Ok(PrimaryProcessorResult::Void); + } + log::trace!(target: "citadel", "RECV Raw packet: {:?}", &packet.parse().unwrap().0); let (header, _payload) = return_if_none!(packet.parse(), "Unable to parse packet"); @@ -120,13 +125,6 @@ pub async fn process_raw_packet( .await } - packet_flags::cmd::primary::DO_DRILL_UPDATE => super::rekey_packet::process_rekey( - session, - packet, - header_entropy_bank_vers, - endpoint_cid_info, - ), - packet_flags::cmd::primary::DO_DEREGISTER => { super::deregister_packet::process_deregister( session, diff --git a/citadel_proto/src/proto/packet_processor/register_packet.rs b/citadel_proto/src/proto/packet_processor/register_packet.rs index 8fb5f7fdc..66b64db10 100644 --- a/citadel_proto/src/proto/packet_processor/register_packet.rs +++ b/citadel_proto/src/proto/packet_processor/register_packet.rs @@ -31,7 +31,9 @@ use super::includes::*; use crate::error::NetworkError; use crate::proto::node_result::{RegisterFailure, RegisterOkay}; -use citadel_crypt::endpoint_crypto_container::{AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, PeerSessionCrypto}; +use citadel_crypt::endpoint_crypto_container::{ + AssociatedCryptoParams, AssociatedSecurityLevel, EndpointRatchetConstructor, PeerSessionCrypto, +}; use citadel_crypt::prelude::{ConstructorOpts, Toolset}; use citadel_crypt::ratchets::Ratchet; use citadel_user::serialization::SyncIO; @@ -128,7 +130,7 @@ pub async fn process_register( let mut state_container = inner_mut_state!(session.state_container); - state_container.register_state.created_stacked_ratchet = + state_container.register_state.created_ratchet = Some(return_if_none!( bob_constructor.finish(), "Unable to finish bob constructor" @@ -184,7 +186,7 @@ pub async fn process_register( alice_constructor .stage1_alice(transfer, session.session_password.as_ref()) .map_err(|err| NetworkError::Generic(err.to_string()))?; - let new_stacked_ratchet = return_if_none!( + let new_ratchet = return_if_none!( alice_constructor.finish(), "Unable to finish alice constructor" ); @@ -196,7 +198,7 @@ pub async fn process_register( ); let stage2_packet = packet_crafter::do_register::craft_stage2( - &new_stacked_ratchet, + &new_ratchet, algorithm, timestamp, proposed_credentials, @@ -204,8 +206,7 @@ pub async fn process_register( ); //let mut state_container = inner_mut!(session.state_container); - state_container.register_state.created_stacked_ratchet = - Some(new_stacked_ratchet); + state_container.register_state.created_ratchet = Some(new_ratchet); state_container.register_state.last_stage = packet_flags::cmd::aux::do_register::STAGE2; state_container.register_state.on_register_packet_received(); @@ -231,16 +232,13 @@ pub async fn process_register( == packet_flags::cmd::aux::do_register::STAGE1 { let algorithm = header.algorithm; - let stacked_ratchet = return_if_none!( - state_container - .register_state - .created_stacked_ratchet - .clone(), + let ratchet = return_if_none!( + state_container.register_state.created_ratchet.clone(), "Unable to load created hyper ratchet" ); if let Some((stage2_packet, conn_info)) = validation::do_register::validate_stage2( - &stacked_ratchet, + &ratchet, &header, payload, remote_addr, @@ -250,7 +248,11 @@ pub async fn process_register( let timestamp = session.time_tracker.get_global_time_ns(); let account_manager = session.account_manager.clone(); std::mem::drop(state_container); - let session_crypto_state = initialize_peer_session_crypto(stacked_ratchet.get_cid(), stacked_ratchet.clone(), true); + let session_crypto_state = initialize_peer_session_crypto( + ratchet.get_cid(), + ratchet.clone(), + true, + ); // we must now create the CNAC async move { match account_manager @@ -267,7 +269,7 @@ pub async fn process_register( session.create_register_success_message(); let packet = packet_crafter::do_register::craft_success( - &stacked_ratchet, + &ratchet, algorithm, timestamp, success_message, @@ -320,17 +322,14 @@ pub async fn process_register( if state_container.register_state.last_stage == packet_flags::cmd::aux::do_register::STAGE2 { - let stacked_ratchet = return_if_none!( - state_container - .register_state - .created_stacked_ratchet - .clone(), + let ratchet = return_if_none!( + state_container.register_state.created_ratchet.clone(), "Unable to load created hyper ratchet" ); if let Some((success_message, conn_info)) = validation::do_register::validate_success( - &stacked_ratchet, + &ratchet, &header, payload, remote_addr, @@ -353,8 +352,8 @@ pub async fn process_register( let account_manager = session.account_manager.clone(); let kernel_tx = session.kernel_tx.clone(); - let session_crypto_state = initialize_peer_session_crypto(stacked_ratchet.get_cid(), stacked_ratchet, false); - + let session_crypto_state = + initialize_peer_session_crypto(ratchet.get_cid(), ratchet, false); async move { match account_manager @@ -459,6 +458,10 @@ pub async fn process_register( } // Only for registration; does not start the messenger/ratchet manager -fn initialize_peer_session_crypto(cid: u64, initial_ratchet: R, is_server: bool) -> PeerSessionCrypto { +fn initialize_peer_session_crypto( + cid: u64, + initial_ratchet: R, + is_server: bool, +) -> PeerSessionCrypto { PeerSessionCrypto::new(Toolset::new(cid, initial_ratchet), !is_server) -} \ No newline at end of file +} diff --git a/citadel_proto/src/proto/packet_processor/rekey_packet.rs b/citadel_proto/src/proto/packet_processor/rekey_packet.rs deleted file mode 100644 index f96f2f6e2..000000000 --- a/citadel_proto/src/proto/packet_processor/rekey_packet.rs +++ /dev/null @@ -1,336 +0,0 @@ -//! Rekey Packet Processor for Citadel Protocol -//! -//! This module implements the key rotation mechanism in the Citadel Protocol network. -//! It manages the secure update of cryptographic keys between nodes to maintain -//! perfect forward secrecy and post-quantum security. -//! -//! # Features -//! -//! - Secure key rotation -//! - Multi-stage key exchange -//! - Post-quantum cryptography -//! - Perfect forward secrecy -//! - State synchronization -//! - Proxy support -//! -//! # Important Notes -//! -//! - Requires connected session state -//! - All packets must be authenticated -//! - Supports both C2S and P2P connections -//! - Manages ratchet state updates -//! - Handles key truncation -//! -//! # Related Components -//! -//! - `StateContainer`: Manages rekey state -//! - `StackedRatchet`: Provides cryptographic primitives -//! - `RatchetUpdateState`: Tracks key updates -//! - `SecurityLevel`: Manages encryption levels -//! -//! ``` - -use super::includes::*; -use crate::error::NetworkError; -use crate::prelude::ReKeyReturnType; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; -use crate::proto::packet_processor::header_to_response_vconn_type; -use crate::proto::packet_processor::primary_group_packet::{ - attempt_kem_as_alice_finish, attempt_kem_as_bob, get_orientation_safe_ratchet, - get_resp_target_cid_from_header, ToolsetUpdate, -}; -use citadel_crypt::ratchets::Ratchet; -use citadel_types::crypto::SecrecyMode; -use std::ops::Deref; - -#[cfg_attr(feature = "localhost-testing", tracing::instrument( - level = "trace", - target = "citadel", - skip_all, - ret, - err, - fields(is_server = session.is_server, src = packet.parse().unwrap().0.session_cid.get(), target = packet.parse().unwrap().0.target_cid.get() - ) -))] -pub fn process_rekey( - session: &CitadelSession, - packet: HdpPacket, - header_entropy_bank_vers: u32, - proxy_cid_info: Option<(u64, u64)>, -) -> Result { - if !session.state.is_connected() { - log::error!(target: "citadel", "Session state is not connected; dropping entropy_bank update packet"); - return Ok(PrimaryProcessorResult::Void); - } - - let CitadelSessionInner { - state_container, - time_tracker, - .. - } = session.inner.deref(); - - let (header, payload, _, _) = packet.decompose(); - let mut state_container = inner_mut_state!(state_container); - - let stacked_ratchet = return_if_none!( - get_orientation_safe_ratchet(header_entropy_bank_vers, &state_container, proxy_cid_info), - "Unable to get proper HR" - ); - let (header, payload) = return_if_none!( - validation::aead::validate_custom(&stacked_ratchet, &header, payload), - "Unable to validate packet" - ); - let payload = &payload[..]; - - let security_level = header.security_level.into(); - let timestamp = time_tracker.get_global_time_ns(); - - match header.cmd_aux { - // Bob - packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE0 => { - log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE STAGE 0 PACKET RECV"); - match validation::do_stacked_ratchet_update::validate_stage0::(payload) { - Some(transfer) => { - let resp_target_cid = get_resp_target_cid_from_header(&header); - let status = return_if_none!( - attempt_kem_as_bob( - session, - resp_target_cid, - &header, - Some(transfer), - &mut state_container, - &stacked_ratchet - ), - "Unable to attempt KEM as Bob" - ); - let packet = packet_crafter::do_entropy_bank_update::craft_stage1( - &stacked_ratchet, - status, - timestamp, - resp_target_cid, - security_level, - ); - Ok(PrimaryProcessorResult::ReplyToSender(packet)) - } - - _ => { - log::error!(target: "citadel", "Invalid stage0 DO_STACKED_RATCHET_UPDATE packet"); - Ok(PrimaryProcessorResult::Void) - } - } - } - - // Alice - packet_flags::cmd::aux::do_stacked_ratchet_update::STAGE1 => { - log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE STAGE 1 PACKET RECV"); - match validation::do_stacked_ratchet_update::validate_stage1(payload) { - Some(transfer) => { - //let mut state_container = inner_mut!(session.state_container); - let peer_cid = header.session_cid.get(); - let target_cid = header.target_cid.get(); - let resp_target_cid = get_resp_target_cid_from_header(&header); - let needs_truncate = transfer.update_status.requires_truncation(); - let constructor = if target_cid != C2S_ENCRYPTION_ONLY { - return_if_none!(state_container - .ratchet_update_state - .p2p_updates - .remove(&peer_cid)) - } else { - return_if_none!(state_container - .ratchet_update_state - .alice_stacked_ratchet - .take()) - }; - log::trace!(target: "citadel", "Obtained constructor for {}", resp_target_cid); - let secrecy_mode = return_if_none!(state_container - .session_security_settings - .as_ref() - .map(|r| r.secrecy_mode)); - - let needs_early_kernel_alert = transfer.update_status.omitted(); - - let latest_hr = return_if_none!( - attempt_kem_as_alice_finish( - session, - secrecy_mode, - peer_cid, - target_cid, - transfer.update_status, - &mut state_container, - Some(constructor) - ) - .ok(), - "Unable to attempt KEM as alice finish" - ) - .unwrap_or(stacked_ratchet); - - let truncate_packet = packet_crafter::do_entropy_bank_update::craft_truncate( - &latest_hr, - needs_truncate, - resp_target_cid, - timestamp, - security_level, - ); - - if needs_truncate.is_none() || needs_early_kernel_alert { - // we only alert the user once truncate_ack received - state_container.ratchet_update_state.on_complete( - header_to_response_vconn_type(&header), - &session.kernel_tx, - ReKeyReturnType::Success { - version: latest_hr.version(), - }, - )?; - } - - Ok(PrimaryProcessorResult::ReplyToSender(truncate_packet)) - } - - _ => { - log::error!(target: "citadel", "Invalid stage1 DO_STACKED_RATCHET_UPDATE packet"); - Ok(PrimaryProcessorResult::Void) - } - } - } - - // Bob will always receive this, whether the toolset being upgraded or not. This allows Bob to begin using the latest entropy_bank version - packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE => { - log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE TRUNCATE PACKET RECV"); - let truncate_packet = return_if_none!( - validation::do_stacked_ratchet_update::validate_truncate(payload), - "Invalid truncate" - ); - let resp_target_cid = get_resp_target_cid_from_header(&header); - - let (mut method, secrecy_mode) = if resp_target_cid != C2S_ENCRYPTION_ONLY { - let endpoint_container = return_if_none!(return_if_none!(state_container - .active_virtual_connections - .get_mut(&resp_target_cid)) - .endpoint_container - .as_mut()); - let crypt = &mut endpoint_container.endpoint_crypto; - let local_cid = header.target_cid.get(); - ( - ToolsetUpdate { crypt, local_cid }, - endpoint_container.default_security_settings.secrecy_mode, - ) - } else { - let secrecy_mode = state_container - .session_security_settings - .as_ref() - .map(|r| r.secrecy_mode) - .unwrap(); - let crypt = &mut state_container - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; - let local_cid = header.session_cid.get(); - (ToolsetUpdate { crypt, local_cid }, secrecy_mode) - }; - - // We optionally deregister at this endpoint to prevent any further packets with this version from being sent - if let Some(truncate_vers) = truncate_packet.truncate_version { - match method.deregister(truncate_vers) { - Ok(_) => { - log::trace!(target: "citadel", "[Toolset Update] Successfully truncated version {}", truncate_vers) - } - Err(err) => { - log::error!(target: "citadel", "[Toolset Update] Error truncating vers {}: {:?}", truncate_vers, err); - } - } - } - - // We update the internal latest version usable - method.post_stage1_alice_or_bob(); - - let _lock_set_by_alice = return_if_none!(method.unlock(false)); - - // if lock set by bob, do poll - //let do_poll = lock_set_by_alice.map(|r| !r).unwrap_or(false); - let do_poll = true; - - // If we didn't have to deregister, then our job is done. alice does not need to hear from Bob - // But, if deregistration occurred, we need to alert alice that way she can unlock hers - if let Some(truncate_vers) = truncate_packet.truncate_version { - let truncate_ack = packet_crafter::do_entropy_bank_update::craft_truncate_ack( - &stacked_ratchet, - truncate_vers, - resp_target_cid, - timestamp, - security_level, - ); - session.send_to_primary_stream(None, truncate_ack)?; - } - - //std::mem::drop(state_container); - - if secrecy_mode == SecrecyMode::Perfect && do_poll { - let _ = state_container.poll_next_enqueued(resp_target_cid)?; - } - - Ok(PrimaryProcessorResult::Void) - } - - packet_flags::cmd::aux::do_stacked_ratchet_update::TRUNCATE_ACK => { - log::trace!(target: "citadel", "DO_STACKED_RATCHET_UPDATE TRUNCATE_ACK PACKET RECV"); - let truncate_ack_packet = return_if_none!( - validation::do_stacked_ratchet_update::validate_truncate_ack(payload), - "Unable to validate truncate ack" - ); - log::trace!(target: "citadel", "Adjacent node has finished deregistering version {}", truncate_ack_packet.truncated_version); - - let resp_target_cid = get_resp_target_cid_from_header(&header); - - let (mut method, secrecy_mode) = if resp_target_cid != C2S_ENCRYPTION_ONLY { - let endpoint_container = return_if_none!(return_if_none!(state_container - .active_virtual_connections - .get_mut(&resp_target_cid)) - .endpoint_container - .as_mut()); - let crypt = &mut endpoint_container.endpoint_crypto; - let local_cid = header.target_cid.get(); - ( - ToolsetUpdate { crypt, local_cid }, - endpoint_container.default_security_settings.secrecy_mode, - ) - } else { - let secrecy_mode = state_container - .session_security_settings - .as_ref() - .map(|r| r.secrecy_mode) - .unwrap(); - let crypt = &mut state_container - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; - let local_cid = header.session_cid.get(); - (ToolsetUpdate { crypt, local_cid }, secrecy_mode) - }; - - let _ = return_if_none!(method.unlock(true)); // unconditional unlock - - // now, we can poll any packets - //std::mem::drop(state_container); - if secrecy_mode == SecrecyMode::Perfect { - let _ = state_container.poll_next_enqueued(resp_target_cid)?; - } - - state_container.ratchet_update_state.on_complete( - header_to_response_vconn_type(&header), - &session.kernel_tx, - ReKeyReturnType::Success { - version: stacked_ratchet.version(), - }, - )?; - - Ok(PrimaryProcessorResult::Void) - } - - _ => { - log::error!(target: "citadel", "Invalid auxiliary command for DO_STACKED_RATCHET_UPDATE packet. Dropping"); - Ok(PrimaryProcessorResult::Void) - } - } -} diff --git a/citadel_proto/src/proto/peer/channel.rs b/citadel_proto/src/proto/peer/channel.rs index 25a9b2697..09a575a4a 100644 --- a/citadel_proto/src/proto/peer/channel.rs +++ b/citadel_proto/src/proto/peer/channel.rs @@ -20,9 +20,10 @@ //! use citadel_proto::prelude::*; //! use futures::StreamExt; //! use citadel_types::crypto::SecurityLevel; +//! use citadel_crypt::ratchets::stacked::StackedRatchet; //! # async fn run() -> Result<(), Box> { //! -//! # let channel: PeerChannel = todo!(); +//! # let channel: PeerChannel = todo!(); //! // Split into send and receive halves //! let (send_half, recv_half) = channel.split(); //! @@ -54,13 +55,12 @@ use crate::error::NetworkError; use crate::proto::node_request::{NodeRequest, PeerCommand}; -use crate::proto::outbound_sender::{OutboundUdpSender, Sender, UnboundedReceiver}; -use crate::proto::packet_crafter::SecureProtocolPacket; -use crate::proto::packet_processor::raw_primary_packet::ReceivePortType; +use crate::proto::outbound_sender::{OutboundUdpSender, UnboundedReceiver}; use crate::proto::peer::peer_layer::{PeerConnectionType, PeerSignal}; use crate::proto::remote::{NodeRemote, Ticket}; -use crate::proto::session::SessionRequest; +use crate::proto::session::UserMessage; use crate::proto::state_container::VirtualConnectionType; +use crate::{ProtocolMessenger, ProtocolMessengerRx, ProtocolMessengerTx}; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::macros::support::Pin; use citadel_types::crypto::SecBuffer; @@ -74,26 +74,25 @@ use std::sync::Arc; // 1 peer channel per virtual connection. This enables high-level communication between the [HdpServer] and the API-layer. #[derive(Debug)] -pub struct PeerChannel { - send_half: PeerChannelSendHalf, - recv_half: PeerChannelRecvHalf, +pub struct PeerChannel { + send_half: PeerChannelSendHalf, + recv_half: PeerChannelRecvHalf, } -impl PeerChannel { +impl PeerChannel { #[allow(clippy::too_many_arguments)] - pub(crate) fn new( + pub(crate) fn new( node_remote: NodeRemote, target_cid: u64, vconn_type: VirtualConnectionType, channel_id: Ticket, security_level: SecurityLevel, is_alive: Arc, - receiver: UnboundedReceiver, - to_outbound_stream: Sender, + messenger: ProtocolMessenger, ) -> Self { let session_cid = vconn_type.get_session_cid(); - let recv_type = ReceivePortType::OrderedReliable; - let server_remote = thin_remote_from_node_remote(node_remote); + + let (to_outbound_stream, from_inbound_stream) = messenger.split(); let send_half = PeerChannelSendHalf { to_outbound_stream, @@ -105,13 +104,14 @@ impl PeerChannel { }; let recv_half = PeerChannelRecvHalf { - server_remote, - receiver, + node_remote, + receiver: ReceiverType::OrderedReliable { + rx: from_inbound_stream, + }, target_cid, vconn_type, channel_id, is_alive, - recv_type, }; PeerChannel { @@ -138,14 +138,13 @@ impl PeerChannel { /// In order to use the [PeerChannel] properly, split must be called in order to receive /// an asynchronous interface. The SendHalf implements Sink, whereas the RecvHalf implements /// Stream - pub fn split(self) -> (PeerChannelSendHalf, PeerChannelRecvHalf) { + pub fn split(self) -> (PeerChannelSendHalf, PeerChannelRecvHalf) { (self.send_half, self.recv_half) } } -#[derive(Clone)] -pub struct PeerChannelSendHalf { - to_outbound_stream: Sender, +pub struct PeerChannelSendHalf { + to_outbound_stream: ProtocolMessengerTx, target_cid: u64, #[allow(dead_code)] session_cid: u64, @@ -154,29 +153,26 @@ pub struct PeerChannelSendHalf { security_level: SecurityLevel, } -impl Debug for PeerChannelSendHalf { +impl Debug for PeerChannelSendHalf { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "PeerChannel {:?}", self.vconn_type) } } -impl PeerChannelSendHalf { +impl PeerChannelSendHalf { pub fn set_security_level(&mut self, security_level: SecurityLevel) { self.security_level = security_level; } /// Sends a message through the channel - pub async fn send_message>( - &self, - message: T, - ) -> Result<(), NetworkError> { - let (ticket, packet, target, security_level) = self.get_args(message.into()); - let request = SessionRequest::SendMessage { - ticket, - packet, - target, - security_level, + pub async fn send_message>(&self, message: T) -> Result<(), NetworkError> { + let request = UserMessage { + ticket: self.channel_id, + packet: message.into(), + target: self.vconn_type, + security_level: self.security_level, }; + self.to_outbound_stream .send(request) .await @@ -187,86 +183,90 @@ impl PeerChannelSendHalf { pub fn channel_id(&self) -> Ticket { self.channel_id } - - #[inline] - fn get_args( - &self, - packet: SecureProtocolPacket, - ) -> ( - Ticket, - SecureProtocolPacket, - VirtualConnectionType, - SecurityLevel, - ) { - ( - self.channel_id, - packet, - self.vconn_type, - self.security_level, - ) - } } -impl Unpin for PeerChannelRecvHalf {} +impl Unpin for PeerChannelRecvHalf {} /// A stream interface for receiving secure packets /// NOTE: on drop, if this is used for a P2P connection, disconnection /// will occur -pub struct PeerChannelRecvHalf { +pub struct PeerChannelRecvHalf { // when the state container removes the vconn, this will get closed - receiver: UnboundedReceiver, + receiver: ReceiverType, pub target_cid: u64, pub vconn_type: VirtualConnectionType, #[allow(dead_code)] channel_id: Ticket, is_alive: Arc, - recv_type: ReceivePortType, - server_remote: Box, + node_remote: NodeRemote, +} + +enum ReceiverType { + OrderedReliable { rx: ProtocolMessengerRx }, + UnorderedUnreliable { rx: UnboundedReceiver }, } -pub(crate) trait ThinRemoteFn: - Fn(NodeRequest) -> Result<(), NetworkError> + Send + Sync + 'static -{ +impl Debug for ReceiverType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let receiver_type = match self { + Self::OrderedReliable { .. } => "OrderedReliable", + Self::UnorderedUnreliable { .. } => "UnorderedUnreliable", + }; + write!(f, "{receiver_type}") + } } -impl Result<(), NetworkError> + Send + Sync + 'static> ThinRemoteFn for T {} -impl Debug for PeerChannelRecvHalf { +impl Debug for PeerChannelRecvHalf { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "PeerChannel Rx {:?}", self.vconn_type) } } -impl Stream for PeerChannelRecvHalf { +impl Stream for PeerChannelRecvHalf { type Item = SecBuffer; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if !self.is_alive.load(Ordering::SeqCst) { // close the stream - log::trace!(target: "citadel", "[POLL] closing PeerChannel RecvHalf"); + log::trace!(target: "citadel", "[POLL] closing PeerChannel RecvHalf for {:?}", self.receiver); Poll::Ready(None) } else { - match futures::ready!(Pin::new(&mut self.receiver).poll_recv(cx)) { - Some(data) => Poll::Ready(Some(data)), - _ => { - log::trace!(target: "citadel", "[PeerChannelRecvHalf] ending"); - Poll::Ready(None) + match &mut self.receiver { + ReceiverType::OrderedReliable { rx } => { + match futures::ready!(Pin::new(rx).poll_next(cx)) { + Some(data) => Poll::Ready(Some(data.packet)), + _ => { + log::trace!(target: "citadel", "[PeerChannelRecvHalf] ending for {:?}", self.receiver); + Poll::Ready(None) + } + } + } + + ReceiverType::UnorderedUnreliable { rx } => { + match futures::ready!(Pin::new(rx).poll_recv(cx)) { + Some(data) => Poll::Ready(Some(data)), + _ => { + log::trace!(target: "citadel", "[PeerChannelRecvHalf] ending for {:?}", self.receiver); + Poll::Ready(None) + } + } } } } } } -impl Drop for PeerChannelRecvHalf { +impl Drop for PeerChannelRecvHalf { fn drop(&mut self) { if let VirtualConnectionType::LocalGroupPeer { session_cid: local_cid, peer_cid, } = self.vconn_type { - log::trace!(target: "citadel", "[PeerChannelRecvHalf] Dropping {:?} type. Will maybe set is_alive to false if this is a tcp p2p connection", self.recv_type); + log::trace!(target: "citadel", "[PeerChannelRecvHalf] Dropping {:?} type. Will maybe set is_alive to false if this is a tcp p2p connection", self.receiver); - let command = match self.recv_type { - ReceivePortType::OrderedReliable => { + let command = match &self.receiver { + ReceiverType::OrderedReliable { .. } => { self.is_alive.store(false, Ordering::SeqCst); NodeRequest::PeerCommand(PeerCommand { session_cid: local_cid, @@ -280,7 +280,7 @@ impl Drop for PeerChannelRecvHalf { }) } - ReceivePortType::UnorderedUnreliable => { + ReceiverType::UnorderedUnreliable { .. } => { if let Some(peer_conn_type) = self.vconn_type.try_as_peer_connection() { NodeRequest::PeerCommand(PeerCommand { session_cid: local_cid, @@ -293,7 +293,7 @@ impl Drop for PeerChannelRecvHalf { } }; - if let Err(err) = (self.server_remote)(command) { + if let Err(err) = self.node_remote.try_send(command) { log::warn!(target: "citadel", "[PeerChannelRecvHalf] unable to send stop signal to session: {:?}", err); } } @@ -301,63 +301,50 @@ impl Drop for PeerChannelRecvHalf { } #[derive(Debug)] -pub struct UdpChannel { +pub struct UdpChannel { send_half: OutboundUdpSender, - recv_half: PeerChannelRecvHalf, + recv_half: PeerChannelRecvHalf, } -pub(crate) fn thin_remote_from_node_remote( - node_remote: NodeRemote, -) -> Box { - Box::new(move |command| { - node_remote - .try_send(command) - .map_err(|err| NetworkError::Generic(err.to_string())) - }) -} - -impl UdpChannel { - pub fn new( +impl UdpChannel { + pub fn new( send_half: OutboundUdpSender, - receiver: UnboundedReceiver, + rx: UnboundedReceiver, target_cid: u64, vconn_type: VirtualConnectionType, channel_id: Ticket, is_alive: Arc, - server_remote: NodeRemote, + node_remote: NodeRemote, ) -> Self { - let server_remote = thin_remote_from_node_remote(server_remote); - Self { send_half, recv_half: PeerChannelRecvHalf { - receiver, + receiver: ReceiverType::UnorderedUnreliable { rx }, target_cid, vconn_type, channel_id, is_alive, - server_remote, - recv_type: ReceivePortType::UnorderedUnreliable, + node_remote, }, } } - pub fn split(self) -> (OutboundUdpSender, PeerChannelRecvHalf) { + pub fn split(self) -> (OutboundUdpSender, PeerChannelRecvHalf) { (self.send_half, self.recv_half) } #[cfg(feature = "webrtc")] #[cfg_attr(docsrs, doc(cfg(feature = "webrtc")))] - pub fn into_webrtc_compat(self) -> WebRTCCompatChannel { + pub fn into_webrtc_compat(self) -> WebRTCCompatChannel { self.into() } } #[cfg(feature = "webrtc")] #[cfg_attr(docsrs, doc(cfg(feature = "webrtc")))] -pub struct WebRTCCompatChannel { +pub struct WebRTCCompatChannel { send_half: OutboundUdpSender, - recv_half: citadel_io::tokio::sync::Mutex, + recv_half: citadel_io::tokio::sync::Mutex>, } #[cfg(feature = "webrtc")] @@ -367,9 +354,11 @@ mod rtc_impl { use crate::proto::peer::channel::WebRTCCompatChannel; use async_trait::async_trait; use bytes::BytesMut; + use citadel_crypt::ratchets::Ratchet; + use futures::StreamExt; - impl From for WebRTCCompatChannel { - fn from(this: UdpChannel) -> Self { + impl From> for WebRTCCompatChannel { + fn from(this: UdpChannel) -> Self { Self { send_half: this.send_half, recv_half: citadel_io::tokio::sync::Mutex::new(this.recv_half), @@ -378,14 +367,14 @@ mod rtc_impl { } #[async_trait] - impl webrtc_util::Conn for WebRTCCompatChannel { + impl webrtc_util::Conn for WebRTCCompatChannel { async fn connect(&self, _addr: SocketAddr) -> Result<(), webrtc_util::Error> { // we assume we are already connected to the target addr by the time we get the UdpChannel Ok(()) } async fn recv(&self, buf: &mut [u8]) -> Result { - match self.recv_half.lock().await.receiver.recv().await { + match self.recv_half.lock().await.receiver.next().await { Some(input) => { buf.copy_from_slice(input.as_ref()); Ok(input.len()) diff --git a/citadel_proto/src/proto/peer/group_channel.rs b/citadel_proto/src/proto/peer/group_channel.rs index 70a39464c..451cfe3d9 100644 --- a/citadel_proto/src/proto/peer/group_channel.rs +++ b/citadel_proto/src/proto/peer/group_channel.rs @@ -51,7 +51,7 @@ use crate::error::NetworkError; use crate::proto::outbound_sender::{Sender, UnboundedReceiver}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; use crate::proto::remote::Ticket; -use crate::proto::session::SessionRequest; +use crate::proto::session::{Group, SessionRequest}; use citadel_io::tokio_stream::StreamExt; use citadel_types::crypto::SecBuffer; use citadel_types::proto::MessageGroupKey; @@ -202,10 +202,10 @@ impl GroupChannelSendHalf { async fn send_group_command(&self, broadcast: GroupBroadcast) -> Result<(), NetworkError> { self.tx - .send(SessionRequest::Group { + .send(SessionRequest::Group(Group { ticket: self.ticket, broadcast, - }) + })) .await .map_err(|err| NetworkError::msg(err.to_string())) } @@ -250,10 +250,10 @@ impl Stream for GroupChannelRecvHalf { impl Drop for GroupChannelRecvHalf { fn drop(&mut self) { log::trace!(target: "citadel", "Dropping group channel recv half for {:?} | {:?}", self.session_cid, self.key); - let request = SessionRequest::Group { + let request = SessionRequest::Group(Group { ticket: self.ticket, broadcast: GroupBroadcast::LeaveRoom { key: self.key }, - }; + }); // TODO: remove group channel locally on the inner process in state container if let Err(err) = self.tx.try_send(request) { diff --git a/citadel_proto/src/proto/peer/p2p_conn_handler.rs b/citadel_proto/src/proto/peer/p2p_conn_handler.rs index fdba6ebc2..6c2da57de 100644 --- a/citadel_proto/src/proto/peer/p2p_conn_handler.rs +++ b/citadel_proto/src/proto/peer/p2p_conn_handler.rs @@ -40,10 +40,11 @@ use crate::proto::misc; use crate::proto::misc::dual_rwlock::DualRwLock; use crate::proto::misc::net::{GenericNetworkListener, GenericNetworkStream}; use crate::proto::misc::udp_internal_interface::{QuicUdpSocketConnector, UdpSplittableTypes}; -use crate::proto::node::Node; +use crate::proto::node::CitadelNode; use crate::proto::node_result::NodeResult; use crate::proto::outbound_sender::OutboundPrimaryStreamSender; use crate::proto::outbound_sender::{unbounded, OutboundPrimaryStreamReceiver, UnboundedSender}; +use crate::proto::packet::HeaderObfuscator; use crate::proto::packet_processor::includes::{Duration, Instant, SocketAddr}; use crate::proto::peer::peer_crypt::PeerNatInfo; use crate::proto::peer::peer_layer::PeerConnectionType; @@ -51,7 +52,7 @@ use crate::proto::remote::Ticket; use crate::proto::session::CitadelSession; use crate::proto::state_container::VirtualConnectionType; use citadel_crypt::ratchets::Ratchet; -use citadel_types::prelude::UdpMode; +use citadel_types::prelude::{SessionSecuritySettings, UdpMode}; use citadel_user::re_exports::__private::Formatter; use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::udp_traversal::hole_punched_socket::TargettedSocketAddr; @@ -63,7 +64,7 @@ use std::sync::Arc; pub struct DirectP2PRemote { // immediately causes connection to end - stopper: Option>, + pub(crate) stopper: Option>, pub p2p_primary_stream: OutboundPrimaryStreamSender, pub from_listener: bool, } @@ -102,6 +103,7 @@ impl Drop for DirectP2PRemote { } } +#[allow(clippy::too_many_arguments)] async fn setup_listener_non_initiator( local_bind_addr: SocketAddr, remote_addr: SocketAddr, @@ -110,9 +112,10 @@ async fn setup_listener_non_initiator( hole_punched_addr: TargettedSocketAddr, ticket: Ticket, udp_mode: UdpMode, + session_security_settings: SessionSecuritySettings, ) -> Result<(), NetworkError> { - // TODO: use custom self-signed - let (listener, _) = Node::::create_listen_socket( + // TODO: allow custom certs for p2p conns + let (listener, _) = CitadelNode::::create_listen_socket( ServerUnderlyingProtocol::new_quic_self_signed(), None, None, @@ -126,10 +129,12 @@ async fn setup_listener_non_initiator( hole_punched_addr, ticket, udp_mode, + session_security_settings, ) .await } +#[allow(clippy::too_many_arguments)] async fn p2p_conn_handler( mut p2p_listener: GenericNetworkListener, session: CitadelSession, @@ -138,6 +143,7 @@ async fn p2p_conn_handler( hole_punched_addr: TargettedSocketAddr, ticket: Ticket, udp_mode: UdpMode, + session_security_settings: SessionSecuritySettings, ) -> Result<(), NetworkError> { let kernel_tx = session.kernel_tx.clone(); let session_cid = session.session_cid.clone(); @@ -162,6 +168,7 @@ async fn p2p_conn_handler( hole_punched_addr, ticket, udp_mode, + session_security_settings, )?; Ok(()) } @@ -186,20 +193,15 @@ fn handle_p2p_stream( mut p2p_stream: GenericNetworkStream, session_cid: DualRwLock>, session: CitadelSession, - kernel_tx: UnboundedSender, + kernel_tx: UnboundedSender>, from_listener: bool, v_conn: VirtualConnectionType, hole_punched_addr: TargettedSocketAddr, ticket: Ticket, udp_mode: UdpMode, + session_security_settings: SessionSecuritySettings, ) -> std::io::Result<()> { - // SECURITY: Since this branch only occurs IF the primary session is connected, then the primary user is - // logged-in. However, what if a malicious user decides to connect here? - // They won't be able to register through here, since registration requires that the state is NeedsRegister - // or SocketJustOpened. But, what if the primary sessions just started and a user tries registering through - // here? Well, just as explained, this branch requires a login in order to occur. Thus, it's impossible for - // a rogue user to attempt to register through here. All other packet types, even pre-connect and NAT traversal, require - // p2p endpoint crypto, so a rogue connector wouldn't be able to do anything + // SECURITY: Since this branch only occurs IF the primary session is connected, implying authentication has been performed let remote_peer = p2p_stream.peer_addr()?; let local_bind_addr = p2p_stream.local_addr()?; let quic_conn = p2p_stream @@ -212,7 +214,10 @@ fn handle_p2p_stream( let (p2p_primary_stream_tx, p2p_primary_stream_rx) = unbounded(); let p2p_primary_stream_tx = OutboundPrimaryStreamSender::from(p2p_primary_stream_tx); let p2p_primary_stream_rx = OutboundPrimaryStreamReceiver::from(p2p_primary_stream_rx); - //let (header_obfuscator, packet_opt) = HeaderObfuscator::new(from_listener); + let header_obfuscator = HeaderObfuscator::new( + from_listener, + session_security_settings.header_obfuscator_settings, + ); let peer_cid = v_conn.get_target_cid(); let (stopper_tx, stopper_rx) = channel(); @@ -224,9 +229,17 @@ fn handle_p2p_stream( p2p_primary_stream_tx.clone(), peer_cid, ); - let writer_future = CitadelSession::::outbound_stream(p2p_primary_stream_rx, sink); - let reader_future = - CitadelSession::execute_inbound_stream(stream, session.clone(), Some(p2p_handle)); + let writer_future = CitadelSession::::outbound_stream( + p2p_primary_stream_rx, + sink, + header_obfuscator.clone(), + ); + let reader_future = CitadelSession::execute_inbound_stream( + stream, + session.clone(), + Some(p2p_handle), + header_obfuscator, + ); let stopper_future = p2p_stopper(stopper_rx); let direct_p2p_remote = DirectP2PRemote::new(stopper_tx, p2p_primary_stream_tx, from_listener); @@ -250,7 +263,7 @@ fn handle_p2p_stream( ); } - std::mem::drop(state_container); + drop(state_container); let future = async move { let res = citadel_io::tokio::select! { @@ -281,22 +294,22 @@ fn handle_p2p_stream( Ok(()) } -pub struct P2PInboundHandle { +pub struct P2PInboundHandle { pub remote_peer: SocketAddr, pub local_bind_port: u16, // this has to be the CID of the local session, not the peer's CID pub session_cid: DualRwLock>, - pub kernel_tx: UnboundedSender, + pub kernel_tx: UnboundedSender>, pub to_primary_stream: OutboundPrimaryStreamSender, pub peer_cid: u64, } -impl P2PInboundHandle { +impl P2PInboundHandle { fn new( remote_peer: SocketAddr, local_bind_port: u16, session_cid: DualRwLock>, - kernel_tx: UnboundedSender, + kernel_tx: UnboundedSender>, to_primary_stream: OutboundPrimaryStreamSender, peer_cid: u64, ) -> Self { @@ -335,13 +348,14 @@ pub(crate) async fn attempt_simultaneous_hole_punch( session: CitadelSession, peer_nat_info: PeerNatInfo, session_cid: DualRwLock>, - kernel_tx: UnboundedSender, - channel_signal: NodeResult, + kernel_tx: UnboundedSender>, + channel_signal: NodeResult, sync_time: Instant, app: NetworkEndpoint, encrypted_config_container: HolePunchConfigContainer, client_config: Arc, udp_mode: UdpMode, + session_security_settings: SessionSecuritySettings, ) -> std::io::Result<()> { let is_initiator = app.is_initiator(); let kernel_tx = &kernel_tx; @@ -372,7 +386,7 @@ pub(crate) async fn attempt_simultaneous_hole_punch( client_config.clone(), ) .map_err(generic_error)?; - let p2p_stream = Node::::quic_p2p_connect_defaults( + let p2p_stream = CitadelNode::::quic_p2p_connect_defaults( quic_endpoint.endpoint, None, peer_nat_info.tls_domain, @@ -392,11 +406,12 @@ pub(crate) async fn attempt_simultaneous_hole_punch( addr, ticket, udp_mode, + session_security_settings, ) } else { log::trace!(target: "citadel", "Non-initiator will begin listening immediately"); drop(hole_punched_socket); // drop to prevent conflicts caused by SO_REUSE_ADDR - setup_listener_non_initiator(local_addr, remote_connect_addr, session.clone(), v_conn, addr, ticket, udp_mode) + setup_listener_non_initiator(local_addr, remote_connect_addr, session.clone(), v_conn, addr, ticket, udp_mode, session_security_settings) .await .map_err(|err| generic_error(format!("Non-initiator was unable to secure connection despite hole-punching success: {err:?}"))) } diff --git a/citadel_proto/src/proto/remote.rs b/citadel_proto/src/proto/remote.rs index 88872fcd8..4dcadf111 100644 --- a/citadel_proto/src/proto/remote.rs +++ b/citadel_proto/src/proto/remote.rs @@ -62,6 +62,7 @@ use citadel_wire::hypernode_type::NodeType; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; +use uuid::Uuid; /// allows convenient communication with the server #[derive(Clone)] @@ -92,7 +93,7 @@ pub trait Remote: Clone + Send { async fn send_callback_subscription( &self, request: NodeRequest, - ) -> Result; + ) -> Result, NetworkError>; /// Returns the account manager instance. fn account_manager(&self) -> &AccountManager; @@ -118,7 +119,7 @@ impl Remote for NodeRemote { async fn send_callback_subscription( &self, request: NodeRequest, - ) -> Result { + ) -> Result, NetworkError> { NodeRemote::send_callback_subscription(self, request).await } @@ -141,7 +142,7 @@ impl NodeRemote { /// Creates a new [`NodeRemote`] instance. pub(crate) fn new( outbound_send_request_tx: BoundedSender<(NodeRequest, Ticket)>, - callback_handler: KernelAsyncCallbackHandler, + callback_handler: KernelAsyncCallbackHandler, account_manager: AccountManager, node_type: NodeType, ) -> Self { @@ -187,7 +188,7 @@ impl NodeRemote { &self, request: NodeRequest, ticket: Ticket, - ) -> Result { + ) -> Result, NetworkError> { let callback_key = CallbackKey { ticket, session_cid: request.session_cid(), @@ -209,7 +210,7 @@ impl NodeRemote { pub async fn send_callback_subscription( &self, request: NodeRequest, - ) -> Result { + ) -> Result, NetworkError> { let ticket = self.get_next_ticket(); self.send_callback_subscription_custom_ticket(request, ticket) .await @@ -275,6 +276,12 @@ impl From for Ticket { } } +impl Default for Ticket { + fn default() -> Self { + Self::from(Uuid::new_v4().as_u128()) + } +} + impl Display for Ticket { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) diff --git a/citadel_proto/src/proto/session.rs b/citadel_proto/src/proto/session.rs index 6c2e7d3f3..5d2e08f2e 100644 --- a/citadel_proto/src/proto/session.rs +++ b/citadel_proto/src/proto/session.rs @@ -50,7 +50,6 @@ use citadel_wire::hypernode_type::NodeType; use citadel_wire::udp_traversal::hole_punched_socket::TargettedSocketAddr; use netbeam::time_tracker::TimeTracker; -//use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender, channel, TrySendError}; use crate::auth::AuthenticationRequest; use crate::constants::{ FIREWALL_KEEP_ALIVE_UDP, GROUP_EXPIRE_TIME_MS, HDP_HEADER_BYTE_LEN, @@ -58,8 +57,7 @@ use crate::constants::{ LOGIN_EXPIRATION_TIME, REKEY_UPDATE_FREQUENCY_STANDARD, }; use crate::error::NetworkError; -use crate::kernel::RuntimeFuture; -use crate::prelude::{GroupBroadcast, PeerEvent, PeerResponse, PreSharedKey, SecureProtocolPacket}; +use crate::prelude::{GroupBroadcast, PeerEvent, PeerResponse, PreSharedKey}; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; //use futures_codec::Framed; use crate::proto::misc; @@ -77,9 +75,9 @@ use crate::proto::outbound_sender::{ use crate::proto::outbound_sender::{ OutboundPrimaryStreamReceiver, OutboundPrimaryStreamSender, OutboundUdpSender, }; -use crate::proto::packet::{packet_flags, HdpPacket}; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; -use crate::proto::packet_crafter::{self, GroupTransmitter, RatchetPacketCrafterContainer}; +use crate::proto::packet::{packet_flags, HdpPacket, HeaderObfuscator}; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; +use crate::proto::packet_crafter::{self, ObjectTransmitter}; use crate::proto::packet_processor::disconnect_packet::SUCCESS_DISCONNECT; use crate::proto::packet_processor::includes::{Duration, SocketAddr}; use crate::proto::packet_processor::raw_primary_packet::{check_proxy, ReceivePortType}; @@ -94,8 +92,9 @@ use crate::proto::session_queue_handler::{ RESERVED_CID_IDX, }; use crate::proto::state_container::{ - FileKey, GroupKey, OutboundFileTransfer, OutboundTransmitterContainer, StateContainer, - StateContainerInner, VirtualConnectionType, VirtualTargetType, + FileKey, GroupKey, OutboundFileTransfer, OutboundTransmitterContainer, + OutgoingPeerConnectionAttempt, StateContainer, StateContainerInner, VirtualConnectionType, + VirtualTargetType, }; use crate::proto::state_subcontainers::preconnect_state_container::UdpChannelSender; use crate::proto::state_subcontainers::rekey_container::calculate_update_frequency; @@ -103,8 +102,9 @@ use crate::proto::transfer_stats::TransferStats; use bytemuck::NoUninit; use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; use citadel_crypt::prelude::{ConstructorOpts, FixedSizedSource}; +use citadel_crypt::ratchets::ratchet_manager::RatchetMessage; use citadel_crypt::scramble::streaming_crypt_scrambler::{scramble_encrypt_source, ObjectSource}; -use citadel_types::crypto::SecurityLevel; +use citadel_types::crypto::{HeaderObfuscatorSettings, SecBuffer, SecurityLevel}; use citadel_types::proto::ConnectMode; use citadel_types::proto::SessionSecuritySettings; use citadel_types::proto::TransferType; @@ -114,10 +114,11 @@ use citadel_user::prelude::ConnectProtocol; use citadel_wire::exports::tokio_rustls::rustls; use citadel_wire::exports::Connection; use citadel_wire::nat_identification::NatType; +use serde::{Deserialize, Serialize}; use std::ops::Deref; use std::path::PathBuf; -use std::pin::Pin; use std::time::{SystemTime, UNIX_EPOCH}; +use zerocopy::AsBytes; //use crate::define_struct; // Defines the primary structure which wraps the inner device @@ -204,7 +205,7 @@ pub struct CitadelSessionInner { pub(super) kernel_ticket: DualCell, pub(super) remote_peer: SocketAddr, // Sends results directly to the kernel - pub(super) kernel_tx: UnboundedSender, + pub(super) kernel_tx: UnboundedSender>, pub(super) to_primary_stream: DualLateInit>, // Setting this will determine what algorithm is used during the DO_CONNECT stage pub(super) session_manager: CitadelSessionManager, @@ -216,7 +217,7 @@ pub struct CitadelSessionInner { pub(super) remote_node_type: Option, pub(super) local_bind_addr: SocketAddr, pub(super) do_static_hr_refresh_atexit: DualCell, - pub(super) dc_signal_sender: DualRwLock>>, + pub(super) dc_signal_sender: DualRwLock>>>, pub(super) is_server: bool, pub(super) stopper_tx: DualRwLock>, pub(super) queue_handle: DualLateInit>, @@ -231,6 +232,7 @@ pub struct CitadelSessionInner { pub(super) init_time: Instant, pub(super) file_transfer_compatible: DualLateInit, pub(super) session_password: PreSharedKey, + pub(super) header_obfuscator_settings: HeaderObfuscatorSettings, on_drop: UnboundedSender<()>, } @@ -268,7 +270,7 @@ pub(crate) struct SessionInitParams { pub citadel_remote: NodeRemote, pub local_bind_addr: SocketAddr, pub local_node_type: NodeType, - pub kernel_tx: UnboundedSender, + pub kernel_tx: UnboundedSender>, pub session_manager: CitadelSessionManager, pub account_manager: AccountManager, pub time_tracker: TimeTracker, @@ -281,6 +283,7 @@ pub(crate) struct SessionInitParams { pub stun_servers: Option>, pub init_time: Instant, pub session_password: PreSharedKey, + pub server_only_session_init_settings: Option, } pub(crate) struct ClientOnlySessionInitSettings { @@ -294,6 +297,13 @@ pub(crate) struct ClientOnlySessionInitSettings { pub connect_mode: Option, } +#[derive(Clone, Default)] +pub struct ServerOnlySessionInitSettings { + // Only useful for the header obfuscator settings + pub declared_header_obfuscation_setting: HeaderObfuscatorSettings, + pub declared_pre_shared_key: Option, +} + impl CitadelSession { pub(crate) fn new( session_init_params: SessionInitParams, @@ -301,6 +311,14 @@ impl CitadelSession { let (stopper_tx, _stopper_rx) = citadel_io::tokio::sync::broadcast::channel(10); let client_only_settings = &session_init_params.client_only_settings; let is_server = client_only_settings.is_none(); + assert_ne!( + client_only_settings.is_some(), + session_init_params + .server_only_session_init_settings + .is_some(), + "Must have either a client or a server" + ); + let (cnac, state, session_cid) = if let Some(client_init_settings) = &session_init_params.client_only_settings { match &client_init_settings.init_mode { @@ -328,6 +346,17 @@ impl CitadelSession { (None, SessionState::SocketJustOpened, None) }; + let header_obfuscator_settings = + if let Some(client_settings) = client_only_settings.as_ref() { + client_settings.security_settings.header_obfuscator_settings + } else { + let server_settings = session_init_params + .server_only_session_init_settings + .as_ref() + .expect("Should be safe to unwrap"); + server_settings.declared_header_obfuscation_setting + }; + let state = DualCell::from(state); let timestamp = session_init_params.time_tracker.get_global_time_ns(); let hypernode_peer_layer = session_init_params.hypernode_peer_layer; @@ -363,6 +392,7 @@ impl CitadelSession { let session_password = session_init_params.session_password; let mut inner = CitadelSessionInner { + header_obfuscator_settings, hypernode_peer_layer, connect_mode: DualRwLock::from(connect_mode), primary_stream_quic_conn: DualRwLock::from(None), @@ -449,8 +479,7 @@ impl CitadelSession { *inner_mut!(this.primary_stream_quic_conn) = Some(quic_conn); } - //let (obfuscator, packet_opt) = HeaderObfuscator::new(this.is_server); - //let sess_id = this_ref.kernel_ticket; + let obfuscator = HeaderObfuscator::new(this.is_server, this.header_obfuscator_settings); this.to_primary_stream .set_once(Some(primary_outbound_tx.clone())); @@ -463,8 +492,10 @@ impl CitadelSession { let stopper = inner!(this.stopper_tx).subscribe(); // Ensure the tx forwards to the writer - let writer_future = Self::outbound_stream(primary_outbound_rx, writer); - let reader_future = Self::execute_inbound_stream(reader, this_inbound, None); + let writer_future = + Self::outbound_stream(primary_outbound_rx, writer, obfuscator.clone()); + let reader_future = + Self::execute_inbound_stream(reader, this_inbound, None, obfuscator); //let timer_future = Self::execute_timer(this.clone()); let queue_worker_future = Self::execute_queue_worker(this_queue_worker); let stopper_future = Self::stopper(stopper); @@ -536,17 +567,6 @@ impl CitadelSession { Ok(()) } - /// Executes each session in parallel (or concurrent if using a LocalSet) - pub async fn session_future_receiver( - mut p2p_session_rx: UnboundedReceiver>>, - ) -> Result<(), NetworkError> { - while let Some(session) = p2p_session_rx.recv().await { - spawn!(session); - } - - Ok(()) - } - /// Before going through the usual flow, check to see if we need to initiate either a stage0 REGISTER or CONNECT packet #[cfg_attr( feature = "localhost-testing", @@ -658,6 +678,7 @@ impl CitadelSession { let connect_mode = (*inner!(session.connect_mode)) .ok_or(NetworkError::InternalError("Connect mode not loaded"))?; let mut state_container = inner_mut_state!(session_ref.state_container); + state_container.store_session_password(C2S_IDENTITY_CID, session.session_password.clone()); let udp_mode = state_container.udp_mode; let timestamp = session_ref.time_tracker.get_global_time_ns(); @@ -770,7 +791,7 @@ impl CitadelSession { state_container.udp_primary_outbound_tx = Some(udp_sender.clone()); log::trace!(target: "citadel", "C2S UDP subroutine inserting UDP channel ... (is_server={})", is_server); if let Some(channel) = state_container.insert_udp_channel( - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, v_target, ticket, udp_sender, @@ -887,21 +908,27 @@ impl CitadelSession { )] pub async fn outbound_stream( primary_outbound_rx: OutboundPrimaryStreamReceiver, - writer: CleanShutdownSink, + mut writer: CleanShutdownSink, + mut header_obfuscator: HeaderObfuscator, ) -> Result<(), NetworkError> { + if let Some(first_packet) = header_obfuscator.first_packet.take() { + log::trace!(target: "citadel", "[Header Obfuscator] Sending first key packet {:?} of len {}", &first_packet.as_bytes()[..HDP_HEADER_BYTE_LEN.min(first_packet.len())], first_packet.len()); + writer.send(first_packet.freeze()).await?; + }; + primary_outbound_rx .0 - .map(|r| { + .map(|packet| { #[cfg_attr( feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, fields(packet_length = r.len() )) )] - fn process_outbound_packet(r: BytesMut) -> Bytes { - r.freeze() + fn process_outbound_packet(r: BytesMut, header_obfuscator: &HeaderObfuscator) -> Bytes { + header_obfuscator.prepare_outbound(r) } - Ok(process_outbound_packet(r)) + Ok(process_outbound_packet(packet, &header_obfuscator)) }) .forward(writer) .map_err(|err| NetworkError::Generic(err.to_string())) @@ -916,9 +943,11 @@ impl CitadelSession { pub async fn execute_inbound_stream( mut reader: CleanShutdownStream, this_main: CitadelSession, - p2p_handle: Option, + p2p_handle: Option>, + header_obfuscator: HeaderObfuscator, ) -> Result<(), NetworkError> { let this_main = &this_main; + let header_obfuscator = &header_obfuscator; log::trace!(target: "citadel", "HdpSession async inbound-stream subroutine executed"); let ( ref remote_peer, @@ -960,7 +989,7 @@ impl CitadelSession { fn evaluate_result( result: Result, primary_stream: &OutboundPrimaryStreamSender, - kernel_tx: &UnboundedSender, + kernel_tx: &UnboundedSender>, session: &CitadelSession, cid_opt: Option, ) -> std::io::Result<()> { @@ -1062,8 +1091,22 @@ impl CitadelSession { }; let res = reader - .try_for_each_concurrent(None, |packet| async move { - let session_cid = session_cid.get(); + .try_for_each_concurrent(None, |mut packet| async move { + log::trace!( + "RECV Raw packet (header only | is_server: {is_server}): {:?} | Len: {}", + &packet.as_bytes()[..HDP_HEADER_BYTE_LEN.min(packet.len())], + packet.len() + ); + if !header_obfuscator + .on_packet_received(&mut packet) + .map_err(|err| { + std::io::Error::new(std::io::ErrorKind::Other, err.into_string()) + })? + { + return Ok(()); + } + + let session_cid: Option = session_cid.get(); let result = packet_processor::raw_primary_packet::process_raw_packet( session_cid, this_main, @@ -1088,7 +1131,7 @@ impl CitadelSession { pub(crate) fn send_to_primary_stream_closure( to_primary_stream: &OutboundPrimaryStreamSender, - kernel_tx: &UnboundedSender, + kernel_tx: &UnboundedSender>, msg: BytesMut, ticket: Option, cid_opt: Option, @@ -1154,23 +1197,21 @@ impl CitadelSession { let ticket = kernel_ticket; if state_container.state.is_connected() { - let timestamp = time_tracker.get_global_time_ns(); - let security_level = state_container.session_security_settings.as_ref().map(|r| r.security_level).unwrap(); let p2p_sessions = state_container.active_virtual_connections.iter().filter_map(|vconn| { - if vconn.1.endpoint_container.as_ref()?.endpoint_crypto.local_is_initiator() && vconn.1.is_active.load(Ordering::SeqCst) && vconn.1.last_delivered_message_timestamp.get().map(|r| r.elapsed() > Duration::from_millis(15000)).unwrap_or(true) { + if vconn.1.endpoint_container.as_ref()?.ratchet_manager.local_is_initiator() && vconn.1.is_active.load(Ordering::SeqCst) && vconn.1.last_delivered_message_timestamp.get().map(|r| r.elapsed() > Duration::from_millis(15000)).unwrap_or(true) { Some(vconn.1.connection_type) } else { None } }).collect::>(); - let virtual_target = VirtualTargetType::LocalGroupServer { session_cid: C2S_ENCRYPTION_ONLY }; - if state_container.initiate_entropy_bank_update(timestamp, virtual_target, Some(ticket)).is_ok() { + let virtual_target = VirtualTargetType::LocalGroupServer { session_cid: C2S_IDENTITY_CID }; + if state_container.initiate_rekey(virtual_target, Some(ticket)).is_ok() { // now, call for each p2p session for vconn in p2p_sessions { - if let Err(err) = state_container.initiate_entropy_bank_update(timestamp, vconn, None) { + if let Err(err) = state_container.initiate_rekey(vconn, None) { log::warn!(target: "citadel", "Unable to initiate entropy_bank update for {:?}: {:?}", vconn, err); } } @@ -1244,26 +1285,24 @@ impl CitadelSession { security_level: SecurityLevel, ) -> Result<(), NetworkError> { self.ensure_connected(&ticket)?; - let mut state_container = inner_mut_state!(self.state_container); + let state_container = inner_state!(self.state_container); let ts = self.time_tracker.get_global_time_ns(); match v_conn { VirtualConnectionType::LocalGroupServer { session_cid: _session_cid, } => { - let crypt_container = &mut state_container - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; + let ratchet_manager = &state_container + .get_endpoint_container(C2S_IDENTITY_CID)? + .ratchet_manager; - let latest_hr = crypt_container.get_ratchet(None).unwrap(); + let latest_hr = ratchet_manager.get_ratchet(None).unwrap(); let packet = packet_crafter::file::craft_revfs_pull( - latest_hr, + &latest_hr, security_level, ticket, ts, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, virtual_path, delete_on_pull, ); @@ -1273,14 +1312,13 @@ impl CitadelSession { session_cid: _, peer_cid: target_cid, } => { - let endpoint_container = - state_container.get_peer_endpoint_container_mut(target_cid)?; + let endpoint_container = state_container.get_endpoint_container(target_cid)?; let latest_hr = endpoint_container - .endpoint_crypto + .ratchet_manager .get_ratchet(None) .unwrap(); let packet = packet_crafter::file::craft_revfs_pull( - latest_hr, + &latest_hr, security_level, ticket, ts, @@ -1318,19 +1356,17 @@ impl CitadelSession { VirtualConnectionType::LocalGroupServer { session_cid: _session_cid, } => { - let crypt_container = &mut state_container - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; + let ratchet_manager = &state_container + .get_endpoint_container(C2S_IDENTITY_CID)? + .ratchet_manager; - let latest_hr = crypt_container.get_ratchet(None).unwrap(); + let latest_hr = ratchet_manager.get_ratchet(None).unwrap(); let packet = packet_crafter::file::craft_revfs_delete( - latest_hr, + &latest_hr, security_level, ticket, ts, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, virtual_path, ); self.send_to_primary_stream(Some(ticket), packet) @@ -1339,14 +1375,13 @@ impl CitadelSession { session_cid: _, peer_cid: target_cid, } => { - let endpoint_container = - state_container.get_peer_endpoint_container_mut(target_cid)?; + let endpoint_container = state_container.get_endpoint_container_mut(target_cid)?; let latest_hr = endpoint_container - .endpoint_crypto + .ratchet_manager .get_ratchet(None) .unwrap(); let packet = packet_crafter::file::craft_revfs_delete( - latest_hr, + &latest_hr, security_level, ticket, ts, @@ -1453,17 +1488,16 @@ impl CitadelSession { return Err(NetworkError::msg("File transfer is not enabled for this session. Both nodes must use a filesystem backend")); } - let crypt_container = &mut state_container - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; + let crypt_container = state_container + .get_endpoint_container(C2S_IDENTITY_CID)? + .ratchet_manager + .session_crypto_state(); let object_id = virtual_object_metadata .as_ref() .map(|r| r.object_id) .unwrap_or_else(|| crypt_container.get_next_object_id()); let group_id_start = crypt_container.get_and_increment_group_id(); - let latest_hr = crypt_container.get_ratchet(None).cloned().unwrap(); + let latest_hr = crypt_container.get_ratchet(None).unwrap(); let static_aux_ratchet = crypt_container .toolset() .read() @@ -1509,7 +1543,9 @@ impl CitadelSession { // if 1 group, we don't need to reserve any more group IDs. If 2, then we reserve just one. 3, then 2 let amt_to_reserve = groups_needed.saturating_sub(1); - crypt_container.incrementing_group_id += amt_to_reserve as u64; + crypt_container + .incrementing_group_id + .fetch_add(amt_to_reserve as _, Ordering::Relaxed); let file_header = packet_crafter::file::craft_file_header_packet( &latest_hr, group_id_start, @@ -1538,19 +1574,17 @@ impl CitadelSession { log::trace!(target: "citadel", "Sending HyperLAN peer ({}) <-> HyperLAN Peer ({})", session_cid, target_cid); // here, we don't use the base session's PQC. Instead, we use the c2s vconn's pqc to ensure the peer can't access the contents // of the file - let crypt_container_c2s = &state_container - .c2s_channel_container - .as_ref() - .unwrap() - .peer_session_crypto; - let static_aux_ratchet = crypt_container_c2s + let ratchet_manager_c2s = &state_container + .get_endpoint_container(C2S_IDENTITY_CID)? + .ratchet_manager; + let static_aux_ratchet = ratchet_manager_c2s + .session_crypto_state() .toolset() .read() .get_static_auxiliary_ratchet() .clone(); - let endpoint_container = - state_container.get_peer_endpoint_container_mut(target_cid)?; + let endpoint_container = state_container.get_endpoint_container(target_cid)?; if !endpoint_container.file_transfer_compatible { return Err(NetworkError::msg("File transfer is not enabled for this p2p session. Both nodes must use a filesystem backend")); @@ -1559,21 +1593,25 @@ impl CitadelSession { let object_id = virtual_object_metadata .as_ref() .map(|r| r.object_id) - .unwrap_or_else(|| endpoint_container.endpoint_crypto.get_next_object_id()); + .unwrap_or_else(|| { + endpoint_container + .ratchet_manager + .session_crypto_state() + .get_next_object_id() + }); // reserve group ids let start_group_id = endpoint_container - .endpoint_crypto + .ratchet_manager + .session_crypto_state() .get_and_increment_group_id(); let latest_usable_ratchet = endpoint_container - .endpoint_crypto + .ratchet_manager .get_ratchet(None) .unwrap(); - let preferred_primary_stream = endpoint_container - .get_direct_p2p_primary_stream() - .cloned() - .unwrap_or_else(|| this.to_primary_stream.clone().unwrap()); + let preferred_primary_stream = + state_container.get_preferred_stream(target_cid).clone(); let (file_size, groups_needed, _max_bytes_per_group) = scramble_encrypt_source( source, @@ -1611,7 +1649,7 @@ impl CitadelSession { }; let file_header = packet_crafter::file::craft_file_header_packet( - latest_usable_ratchet, + &latest_usable_ratchet, start_group_id, ticket, security_level, @@ -1623,7 +1661,11 @@ impl CitadelSession { // if 1 group, we don't need to reserve any more group IDs. If 2, then we reserve just one. 3, then 2 let amt_to_reserve = groups_needed.saturating_sub(1); - endpoint_container.endpoint_crypto.incrementing_group_id += amt_to_reserve as u64; + endpoint_container + .ratchet_manager + .session_crypto_state() + .incrementing_group_id + .fetch_add(amt_to_reserve as _, Ordering::Relaxed); ( preferred_primary_stream, @@ -1719,45 +1761,25 @@ impl CitadelSession { } let mut state_container = inner_mut_state!(sess.state_container); - - let proper_latest_stacked_ratchet = match virtual_target { - VirtualConnectionType::LocalGroupServer { session_cid: _ } => { - state_container - .c2s_channel_container - .as_ref() - .unwrap() - .peer_session_crypto - .get_ratchet(None) - } - VirtualConnectionType::LocalGroupPeer { - session_cid: _, - peer_cid, - } => match state_container.get_peer_session_crypto(peer_cid) { - Some(peer_sess_crypt) => peer_sess_crypt.get_ratchet(None), - - None => { - log::warn!(target: "citadel", "Since transmitting the file, the peer session ended"); - return; - } - }, - - _ => { - log::error!(target: "citadel", "HyperWAN Functionality not implemented"); + let latest_proper_ratchet = match state_container + .get_endpoint_container(virtual_target.get_target_cid()) + { + Ok(r) => r.ratchet_manager.get_ratchet(None), + Err(err) => { + log::error!(target: "citadel", "Unable to get endpoint container: {}", err.to_string()); return; } }; - if proper_latest_stacked_ratchet.is_none() { - log::error!(target: "citadel", "Unable to unwrap StackedRatchet (X-05)"); + let Some(ratchet) = latest_proper_ratchet else { + log::error!(target: "citadel", "Unable to unwrap Ratchet for {virtual_target}"); return; - } - - let stacked_ratchet = proper_latest_stacked_ratchet.unwrap(); + }; - let mut transmitter = GroupTransmitter::new_from_group_sender( + let mut transmitter = ObjectTransmitter::new_from_group_sender( to_primary_stream.clone(), sender, - RatchetPacketCrafterContainer::new(stacked_ratchet.clone(), None), + ratchet.clone(), object_id, ticket, security_level, @@ -1807,7 +1829,7 @@ impl CitadelSession { // as long as a wave ACK has been received, proceed with the timeout check // The reason why is because this group may be loaded, but the previous one isn't done if transmitter.has_begun { - let transmitter = &transmitter.burst_transmitter.group_transmitter; + let transmitter = transmitter.burst_transmitter.group_transmitter.as_ref().expect("transmitter should exist"); if transmitter.has_expired(GROUP_EXPIRE_TIME_MS) { if state_container.meta_expiry_state.expired() { log::error!(target: "citadel", "Outbound group {} has expired; dropping entire transfer", group_id); @@ -1883,7 +1905,8 @@ impl CitadelSession { // TODO: Make a generic version to allow requests the ability to bypass the session manager pub(crate) fn spawn_message_sender_function( this: CitadelSession, - mut rx: citadel_io::tokio::sync::mpsc::Receiver, + virtual_connection_type: VirtualConnectionType, + mut rx_session_requests: crate::proto::outbound_sender::Receiver, ) { let task = async move { let this = &this; @@ -1898,36 +1921,90 @@ impl CitadelSession { }; let receiver = async move { - while let Some(request) = rx.recv().await { - let mut state_container = inner_mut_state!(this.state_container); - - match request { - SessionRequest::SendMessage { + // The messages the messenger receives will automatically be sent to the user + // via the ProtocolMessengerRx handle + // However, when the user sends messages through the sink for the provided ProtocolMessengerTx, + // that sink sends items here. Thus, we need to receive those messages and process them outbound. + // Note: based on the virtual connection type, we must dynamically determine the preferred_primary_stream + // to forward these to. + fn send_ratchet_message( + state_container: &StateContainerInner, + ratchet_message: RatchetMessage, + v_conn: VirtualConnectionType, + ) -> Result<(), (NetworkError, Option)> { + let mut attributed_ticket = None; + + let (ticket, security_level) = match &ratchet_message { + RatchetMessage::JustMessage(UserMessage { ticket, - packet, - target, security_level, - } => { - if let Err(err) = state_container.process_outbound_message( - ticket, - packet, - target, - security_level, - false, + .. + }) => { + attributed_ticket = Some(*ticket); + (*ticket, *security_level) + } + _other => (Ticket::default(), SecurityLevel::default()), + }; + + let preferred_stream = + state_container.get_preferred_stream(v_conn.get_target_cid()); + let endpoint_container = state_container + .get_virtual_connection_crypto(v_conn.get_target_cid()) + .ok_or_else(|| { + ( + NetworkError::Generic( + "Unable to get virtual connection crypto".to_string(), + ), + attributed_ticket, + ) + })?; + + let ratchet = endpoint_container + .get_ratchet(None) + .expect("ratchet should exist"); + + let object_id = endpoint_container.get_next_object_id(); + let group_id = endpoint_container.get_and_increment_group_id(); + let time_tracker = state_container.time_tracker; + + ObjectTransmitter::transmit_message( + preferred_stream.clone(), + object_id, + ratchet, + ratchet_message, + security_level, + group_id, + ticket, + time_tracker, + v_conn, + ) + .map_err(|err| (err, attributed_ticket))?; + + Ok(()) + } + + while let Some(request) = rx_session_requests.recv().await { + let state_container = inner_state!(this.state_container); + match request { + SessionRequest::SendMessage(other) => { + if let Err((err, ticket_opt)) = send_ratchet_message( + &state_container, + other, + virtual_connection_type, ) { to_kernel_tx .unbounded_send(NodeResult::InternalServerError( InternalServerError { - ticket_opt: Some(ticket), + ticket_opt, cid_opt: this.session_cid.get(), - message: err.into_string(), + message: err.to_string(), }, )) .map_err(|err| NetworkError::Generic(err.to_string()))? } } - SessionRequest::Group { ticket, broadcast } => { + SessionRequest::Group(Group { ticket, broadcast }) => { if let Err(err) = state_container .process_outbound_broadcast_command(ticket, &broadcast) { @@ -1964,12 +2041,12 @@ impl CitadelSession { peer_command: PeerSignal, security_level: SecurityLevel, ) -> Result<(), NetworkError> { - log::trace!(target: "citadel", "Dispatching peer command {:?} ...", peer_command); + log::trace!(target: "citadel", "Dispatching peer command {peer_command:?} ..."); let this = self; let state = this.state.get(); if state != SessionState::Connected { - log::warn!(target: "citadel", "Session is not connected (s={:?}); will still attempt send peer command {:?}", state, peer_command) + log::warn!(target: "citadel", "Session is not connected (s={state:?}); will still attempt send peer command {peer_command:?}") } let timestamp = this.time_tracker.get_global_time_ns(); @@ -2017,34 +2094,40 @@ impl CitadelSession { } PeerSignal::PostConnect { - peer_conn_type: a, - ticket_opt: b, + peer_conn_type, + ticket_opt, invitee_response, - session_security_settings: d, - udp_mode: e, + session_security_settings, + udp_mode, session_password, } => { let session_password = session_password.unwrap_or_default(); if state_container .outgoing_peer_connect_attempts - .contains_key(&a.get_original_target_cid()) + .contains_key(&peer_conn_type.get_original_target_cid()) { - log::warn!(target: "citadel", "{} is already attempting to connect to {}", a.get_original_session_cid(), a.get_original_target_cid()) + log::warn!(target: "citadel", "{} is already attempting to connect to {}", peer_conn_type.get_original_session_cid(), peer_conn_type.get_original_target_cid()) } - state_container - .store_session_password(a.get_original_target_cid(), session_password); + state_container.store_session_password( + peer_conn_type.get_original_target_cid(), + session_password, + ); // in case the ticket gets mapped during simultaneous_connect, store locally - let _ = state_container - .outgoing_peer_connect_attempts - .insert(a.get_original_target_cid(), ticket); + let _ = state_container.outgoing_peer_connect_attempts.insert( + peer_conn_type.get_original_target_cid(), + OutgoingPeerConnectionAttempt { + ticket, + session_security_settings, + }, + ); PeerSignal::PostConnect { - peer_conn_type: a, - ticket_opt: b, + peer_conn_type, + ticket_opt, invitee_response, - session_security_settings: d, - udp_mode: e, + session_security_settings, + udp_mode, session_password: None, } } @@ -2052,19 +2135,13 @@ impl CitadelSession { n => n, }; - let stacked_ratchet = state_container - .c2s_channel_container - .as_ref() - .ok_or_else(|| { - NetworkError::msg(format!( - "C2S container not loaded; cannot send peer command {signal_processed:?}" - )) - })? - .peer_session_crypto + let ratchet = state_container + .get_endpoint_container(C2S_IDENTITY_CID)? + .ratchet_manager .get_ratchet(None) .unwrap(); let packet = super::packet_crafter::peer_cmd::craft_peer_signal( - &stacked_ratchet, + &ratchet, signal_processed, ticket, timestamp, @@ -2089,7 +2166,7 @@ impl CitadelSession { while let Some(res) = stream.next().await { match res { Ok((packet, remote_peer)) => { - log::trace!(target: "citadel", "packet received on waveport {} has {} bytes (src: {:?})", local_port, packet.len(), &remote_peer); + log::trace!(target: "citadel", "Packet received on port {} has {} bytes (src: {:?})", local_port, packet.len(), &remote_peer); let packet = HdpPacket::new_recv(packet, remote_peer, local_port); this.process_inbound_packet_udp(packet, &peer_session_accessor)?; } @@ -2101,7 +2178,7 @@ impl CitadelSession { } } - log::trace!(target: "citadel", "Ending waveport listener on {}", local_port); + log::trace!(target: "citadel", "Ending UDP Port listener on {local_port}"); Ok(()) } @@ -2268,7 +2345,7 @@ impl CitadelSessionInner { /// This will panic if cannot be sent #[inline] - pub fn send_to_kernel(&self, msg: NodeResult) -> Result<(), SendError> { + pub fn send_to_kernel(&self, msg: NodeResult) -> Result<(), SendError>> { self.kernel_tx.unbounded_send(msg) } @@ -2394,17 +2471,25 @@ impl Drop for CitadelSession { } } +#[derive(Debug, Serialize, Deserialize)] +#[doc(hidden)] +pub struct UserMessage { + pub ticket: Ticket, + pub packet: SecBuffer, + pub target: VirtualTargetType, + pub security_level: SecurityLevel, +} + +#[derive(Debug)] +#[doc(hidden)] +pub struct Group { + pub ticket: Ticket, + pub broadcast: GroupBroadcast, +} + #[derive(Debug)] #[doc(hidden)] pub enum SessionRequest { - SendMessage { - ticket: Ticket, - packet: SecureProtocolPacket, - target: VirtualTargetType, - security_level: SecurityLevel, - }, - Group { - ticket: Ticket, - broadcast: GroupBroadcast, - }, + SendMessage(RatchetMessage), + Group(Group), } diff --git a/citadel_proto/src/proto/session_manager.rs b/citadel_proto/src/proto/session_manager.rs index c2ccc034d..dedec7387 100644 --- a/citadel_proto/src/proto/session_manager.rs +++ b/citadel_proto/src/proto/session_manager.rs @@ -37,7 +37,6 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::path::PathBuf; -use std::pin::Pin; use std::sync::atomic::Ordering; use bytes::BytesMut; @@ -51,18 +50,17 @@ use citadel_wire::nat_identification::NatType; use netbeam::time_tracker::TimeTracker; use crate::auth::AuthenticationRequest; -use crate::constants::{DO_CONNECT_EXPIRE_TIME_MS, KEEP_ALIVE_TIMEOUT_NS, UDP_MODE}; +use crate::constants::{DO_CONNECT_EXPIRE_TIME_MS, KEEP_ALIVE_TIMEOUT_NS}; use crate::error::NetworkError; -use crate::kernel::RuntimeFuture; -use crate::macros::SyncContextRequirements; +use crate::macros::{FutureRequirements, SyncContextRequirements}; use crate::prelude::{Disconnect, PreSharedKey}; use crate::proto::endpoint_crypto_accessor::EndpointCryptoAccessor; use crate::proto::misc::net::GenericNetworkStream; use crate::proto::misc::underlying_proto::ServerUnderlyingProtocol; -use crate::proto::node::Node; +use crate::proto::node::CitadelNode; use crate::proto::node_result::NodeResult; use crate::proto::outbound_sender::{unbounded, UnboundedReceiver, UnboundedSender}; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; use crate::proto::packet_processor::includes::{Duration, Instant}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; use crate::proto::packet_processor::PrimaryProcessorResult; @@ -72,7 +70,8 @@ use crate::proto::peer::peer_layer::{ }; use crate::proto::remote::{NodeRemote, Ticket}; use crate::proto::session::{ - CitadelSession, ClientOnlySessionInitSettings, HdpSessionInitMode, SessionInitParams, + CitadelSession, ClientOnlySessionInitSettings, HdpSessionInitMode, + ServerOnlySessionInitSettings, SessionInitParams, }; use crate::proto::state_container::{VirtualConnectionType, VirtualTargetType}; use citadel_crypt::scramble::streaming_crypt_scrambler::ObjectSource; @@ -102,7 +101,7 @@ pub struct HdpSessionManagerInner { /// in the state of NeedsRegister. Once they leave that state, they are eventually polled /// by the [CitadelSessionManager] and thereafter placed inside an appropriate session pub provisional_connections: HashMap, CitadelSession)>, - kernel_tx: UnboundedSender, + kernel_tx: UnboundedSender>, time_tracker: TimeTracker, clean_shutdown_tracker_tx: UnboundedSender<()>, clean_shutdown_tracker: Option>, @@ -114,7 +113,7 @@ impl CitadelSessionManager { /// Creates a new [SessionManager] which handles individual connections pub fn new( local_node_type: NodeType, - kernel_tx: UnboundedSender, + kernel_tx: UnboundedSender>, account_manager: AccountManager, time_tracker: TimeTracker, client_config: Arc, @@ -143,7 +142,7 @@ impl CitadelSessionManager { Self::from(inner) } - /// Loads the server remote, and gets the time tracker for the calling [Node] + /// Loads the server remote, and gets the time tracker for the calling [CitadelNode] /// Used during the init stage pub(crate) fn load_server_remote_get_tt(&self, server_remote: NodeRemote) -> TimeTracker { let mut this = inner_mut!(self); @@ -157,7 +156,7 @@ impl CitadelSessionManager { this.sessions.contains_key(&cid) } - /// Called by the higher-level [Node] async writer loop + /// Called by the higher-level [CitadelNode] async writer loop /// `nid_local` is only needed in case a provisional id is needed. /// This is initiated by the local HyperNode's request to connect to an external server /// `proposed_credentials`: Must be Some if session_cid is None! @@ -175,7 +174,7 @@ impl CitadelSessionManager { security_settings: SessionSecuritySettings, default_client_config: &Arc, session_password: PreSharedKey, - ) -> Result>, NetworkError> { + ) -> Result>, NetworkError> { let (session_manager, new_session, peer_addr, primary_stream) = { let session_manager_clone = self.clone(); @@ -285,10 +284,12 @@ impl CitadelSessionManager { ConnectProtocol::Quic(listener_underlying_proto.maybe_get_identity()); // create conn to peer - let primary_stream = - Node::::create_session_transport_init(peer_addr, default_client_config) - .await - .map_err(|err| NetworkError::SocketError(err.to_string()))?; + let primary_stream = CitadelNode::::create_session_transport_init( + peer_addr, + default_client_config, + ) + .await + .map_err(|err| NetworkError::SocketError(err.to_string()))?; let local_bind_addr = primary_stream .local_addr() .map_err(|err| NetworkError::Generic(err.to_string()))?; @@ -315,7 +316,7 @@ impl CitadelSessionManager { connect_mode, cnac, proposed_credentials, - udp_mode: udp_mode.unwrap_or(UDP_MODE), + udp_mode: udp_mode.unwrap_or_default(), keep_alive_timeout_ns: keep_alive_timeout_ns.unwrap_or(KEEP_ALIVE_TIMEOUT_NS), security_settings, peer_only_connect_proto: peer_only_connect_mode, @@ -341,6 +342,7 @@ impl CitadelSessionManager { stun_servers, init_time, session_password, + server_only_session_init_settings: None, }; let (stopper, new_session) = CitadelSession::new(session_init_params)?; @@ -427,7 +429,7 @@ impl CitadelSessionManager { let _ = cnac.refresh_static_ratchet(); } - if cnac.passwordless() { + if cnac.is_transient() { // delete let cid = cnac.get_cid(); let task = async move { pers.delete_cnac_by_cid(cid).await }; @@ -470,8 +472,8 @@ impl CitadelSessionManager { peer_conn_type, disconnect_response: Some(PeerResponse::Disconnected(format!("{peer_cid} disconnected from {session_cid} forcibly"))), }; - if let Err(_err) = sess_mgr.send_signal_to_peer_direct(peer_cid, |peer_stacked_ratchet| { - super::packet_crafter::peer_cmd::craft_peer_signal(peer_stacked_ratchet, signal, Ticket(0), timestamp, security_level) + if let Err(_err) = sess_mgr.send_signal_to_peer_direct(peer_cid, |peer_ratchet| { + super::packet_crafter::peer_cmd::craft_peer_signal(peer_ratchet, signal, Ticket(0), timestamp, security_level) }) { //log::error!(target: "citadel", "Unable to send shutdown signal to {}: {:?}", peer_cid, err); } @@ -518,8 +520,8 @@ impl CitadelSessionManager { local_nat_type: NatType, peer_addr: SocketAddr, primary_stream: GenericNetworkStream, - session_password: PreSharedKey, - ) -> Result>, NetworkError> { + server_only_session_init_settings: ServerOnlySessionInitSettings, + ) -> Result>, NetworkError> { let this_dc = self.clone(); let mut this = inner_mut!(self); let on_drop = this.clean_shutdown_tracker_tx.clone(); @@ -553,7 +555,11 @@ impl CitadelSessionManager { client_only_settings: None, stun_servers, init_time, - session_password, + session_password: server_only_session_init_settings + .declared_pre_shared_key + .clone() + .unwrap_or_default(), + server_only_session_init_settings: Some(server_only_session_init_settings), }; let (stopper, new_session) = CitadelSession::new(session_init_params)?; @@ -568,7 +574,7 @@ impl CitadelSessionManager { primary_stream, ); - Ok(Box::pin(session)) + Ok(session) } /// dispatches an outbound command @@ -587,7 +593,7 @@ impl CitadelSessionManager { } } - /// When the [Node] receives an outbound request, the request flows here. It returns where the packet must be sent to + /// When the [CitadelNode] receives an outbound request, the request flows here. It returns where the packet must be sent to #[allow(clippy::too_many_arguments)] pub fn process_outbound_file( &self, @@ -669,9 +675,8 @@ impl CitadelSessionManager { let this = inner!(self); if let Some(sess) = this.sessions.get(&session_cid) { let sess = &sess.1; - let timestamp = sess.time_tracker.get_global_time_ns(); let mut state_container = inner_mut_state!(sess.state_container); - state_container.initiate_entropy_bank_update(timestamp, virtual_target, Some(ticket)) + state_container.initiate_rekey(virtual_target, Some(ticket)) } else { Err(NetworkError::Generic(format!( "Unable to initiate entropy_bank update subroutine for {session_cid} (not an active session)" @@ -829,16 +834,16 @@ impl CitadelSessionManager { // notify all the peers for peer_cid in peers_to_notify { let this = inner!(self); - if let Err(err) = this.send_signal_to_peer_direct(peer_cid, |peer_stacked_ratchet| { + if let Err(err) = this.send_signal_to_peer_direct(peer_cid, |peer_ratchet| { let signal = GroupBroadcast::Invitation { sender: session_cid, key, }; super::packet_crafter::peer_cmd::craft_group_message_packet( - peer_stacked_ratchet, + peer_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ) @@ -865,19 +870,17 @@ impl CitadelSessionManager { let this = inner!(self); for peer_cid in group.concurrent_peers.keys() { if *peer_cid != cid_host { - if let Err(err) = - this.send_signal_to_peer_direct(*peer_cid, |peer_stacked_ratchet| { - let signal = GroupBroadcast::Disconnected { key }; - super::packet_crafter::peer_cmd::craft_group_message_packet( - peer_stacked_ratchet, - &signal, - ticket, - C2S_ENCRYPTION_ONLY, - timestamp, - security_level, - ) - }) - { + if let Err(err) = this.send_signal_to_peer_direct(*peer_cid, |peer_ratchet| { + let signal = GroupBroadcast::Disconnected { key }; + super::packet_crafter::peer_cmd::craft_group_message_packet( + peer_ratchet, + &signal, + ticket, + C2S_IDENTITY_CID, + timestamp, + security_level, + ) + }) { log::warn!(target: "citadel", "Unable to send d/c signal to peer {}: {}", peer_cid, err.to_string()); } } @@ -1232,12 +1235,12 @@ impl CitadelSessionManager { for (peer, is_registered) in peers_and_statuses { if is_registered { if this - .send_signal_to_peer_direct(peer, |peer_stacked_ratchet| { + .send_signal_to_peer_direct(peer, |peer_ratchet| { super::packet_crafter::peer_cmd::craft_group_message_packet( - peer_stacked_ratchet, + peer_ratchet, &signal, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ) diff --git a/citadel_proto/src/proto/state_container.rs b/citadel_proto/src/proto/state_container.rs index 62bc16715..982c8494c 100644 --- a/citadel_proto/src/proto/state_container.rs +++ b/citadel_proto/src/proto/state_container.rs @@ -37,15 +37,12 @@ //! - Group keys are securely managed //! - File transfers are encrypted end-to-end -use std::collections::{HashMap, VecDeque}; +use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; use std::ops::RangeInclusive; use std::sync::Arc; -use crate::proto::packet_processor::primary_group_packet::{ - attempt_kem_as_alice_finish, get_resp_target_cid_from_header, -}; -use citadel_crypt::sync_toggle::{CurrentToggleState, SyncToggle}; +use crate::proto::packet_processor::primary_group_packet::get_resp_target_cid_from_header; use serde::{Deserialize, Serialize}; use crate::proto::outbound_sender::{unbounded, UnboundedSender}; @@ -58,7 +55,7 @@ use citadel_user::client_account::ClientNetworkAccount; use netbeam::time_tracker::TimeTracker; use crate::constants::{ - GROUP_EXPIRE_TIME_MS, GROUP_TIMEOUT_MS, INDIVIDUAL_WAVE_TIMEOUT_MS, KEEP_ALIVE_INTERVAL_MS, + GROUP_TIMEOUT_MS, INDIVIDUAL_WAVE_TIMEOUT_MS, KEEP_ALIVE_INTERVAL_MS, MAX_OUTGOING_UNPROCESSED_REQUESTS, }; use crate::error::NetworkError; @@ -72,11 +69,9 @@ use crate::proto::node_result::{NodeResult, ObjectTransferHandle}; use crate::proto::outbound_sender::{OutboundPrimaryStreamSender, OutboundUdpSender}; use crate::proto::packet::packet_flags; use crate::proto::packet::HdpHeader; -use crate::proto::packet_crafter::peer_cmd::C2S_ENCRYPTION_ONLY; -use crate::proto::packet_crafter::{ - GroupTransmitter, RatchetPacketCrafterContainer, SecureProtocolPacket, -}; -use crate::proto::packet_processor::includes::{CitadelSession, Instant, SocketAddr}; +use crate::proto::packet_crafter::peer_cmd::C2S_IDENTITY_CID; +use crate::proto::packet_crafter::ObjectTransmitter; +use crate::proto::packet_processor::includes::{CitadelSession, Instant}; use crate::proto::packet_processor::peer::group_broadcast::GroupBroadcast; use crate::proto::packet_processor::PrimaryProcessorResult; use crate::proto::peer::channel::{PeerChannel, UdpChannel}; @@ -84,24 +79,24 @@ use crate::proto::peer::group_channel::{GroupBroadcastPayload, GroupChannel}; use crate::proto::peer::p2p_conn_handler::DirectP2PRemote; use crate::proto::peer::peer_layer::PeerConnectionType; use crate::proto::remote::{NodeRemote, Ticket}; -use crate::proto::session::SessionState; -use crate::proto::session_queue_handler::{QueueWorkerResult, SessionQueueWorkerHandle}; +use crate::proto::session::{SessionRequest, SessionState, UserMessage}; +use crate::proto::session_queue_handler::SessionQueueWorkerHandle; use crate::proto::state_subcontainers::connect_state_container::ConnectState; use crate::proto::state_subcontainers::deregister_state_container::DeRegisterState; use crate::proto::state_subcontainers::meta_expiry_container::MetaExpiryState; use crate::proto::state_subcontainers::peer_kem_state_container::PeerKemStateContainer; use crate::proto::state_subcontainers::preconnect_state_container::PreConnectState; use crate::proto::state_subcontainers::register_state_container::RegisterState; -use crate::proto::state_subcontainers::rekey_container::RatchetUpdateState; use crate::proto::transfer_stats::TransferStats; use crate::proto::{packet_crafter, send_with_error_logging}; +use crate::{ProtocolMessenger, ProtocolRatchetManager}; use bytes::Bytes; -use citadel_crypt::endpoint_crypto_container::{ - EndpointRatchetConstructor, KemTransferStatus, PeerSessionCrypto, -}; +use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; +use citadel_crypt::ratchets::ratchet_manager::RatchetMessage; use citadel_crypt::ratchets::Ratchet; +use citadel_io::tokio_stream::wrappers::UnboundedReceiverStream; +use citadel_io::{tokio, Mutex}; use citadel_types::crypto::SecBuffer; -use citadel_types::crypto::SecrecyMode; use citadel_types::crypto::SecurityLevel; use citadel_types::prelude::ObjectId; use citadel_types::proto::{ @@ -111,7 +106,6 @@ use citadel_types::proto::{ use citadel_user::backend::utils::*; use citadel_user::backend::PersistenceHandler; use citadel_user::serialization::SyncIO; -use either::Either; use std::sync::atomic::{AtomicBool, Ordering}; impl Debug for StateContainer { @@ -130,21 +124,9 @@ pub struct StateContainerInner { pub(super) register_state: RegisterState, /// No hashmap here, since connect is only for a single target pub(super) connect_state: ConnectState, - // TODO: move c2s ratchet updates into here - pub(super) ratchet_update_state: RatchetUpdateState, pub(super) deregister_state: DeRegisterState, pub(super) meta_expiry_state: MetaExpiryState, pub(super) network_stats: NetworkStats, - pub(super) enqueued_packets: HashMap< - u64, - VecDeque<( - Ticket, - SecureProtocolPacket, - VirtualTargetType, - SecurityLevel, - )>, - >, - pub(super) updates_in_progress: HashMap, pub(super) inbound_files: HashMap, pub(super) outbound_files: HashMap, pub(super) file_transfer_handles: HashMap>, @@ -153,15 +135,15 @@ pub struct StateContainerInner { pub(super) peer_kem_states: HashMap>, // u64 is peer id, ticket is the local original ticket (ticket may // transform if a simultaneous connect) - pub(super) outgoing_peer_connect_attempts: HashMap, + pub(super) outgoing_peer_connect_attempts: HashMap, pub(super) udp_primary_outbound_tx: Option, - pub(super) kernel_tx: UnboundedSender, + pub(super) kernel_tx: UnboundedSender>, pub(super) active_virtual_connections: HashMap>, - pub(super) c2s_channel_container: Option>, pub(crate) keep_alive_timeout_ns: i64, pub(crate) state: DualCell, // whenever a c2s or p2p channel is loaded, this is fired to signal any UDP loaders that it is safe to store the UDP conn in the corresponding v_conn pub(super) tcp_loaded_status: Option>, + // TODO: Ensure cleanup pub(super) hole_puncher_pipes: HashMap>, pub(super) cnac: Option>, @@ -171,7 +153,8 @@ pub struct StateContainerInner { pub(super) group_channels: HashMap>, pub(super) transfer_stats: TransferStats, pub(super) udp_mode: UdpMode, - pub(super) session_passwords: HashMap, + triggered_rekeys: Arc>>, + session_passwords: HashMap, is_server: bool, } @@ -183,12 +166,24 @@ pub(crate) struct GroupKey { object_id: ObjectId, } +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +pub(crate) struct ReKeyIndex { + target_cid: u64, + ticket: Ticket, +} + #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct FileKey { // wave payload get the object id inscribed pub object_id: ObjectId, } +#[derive(Copy, Clone, Debug)] +pub struct OutgoingPeerConnectionAttempt { + pub ticket: Ticket, + pub session_security_settings: SessionSecuritySettings, +} + /// when the GROUP_HEADER comes inbound with virtual file metadata, this should be created alongside /// an async task fired-up on the threadpool #[allow(dead_code)] @@ -248,35 +243,22 @@ pub struct VirtualConnection { impl VirtualConnection { /// If No version is supplied, uses the latest committed version - pub fn borrow_endpoint_stacked_ratchet(&self, version: Option) -> Option<&R> { + pub fn get_endpoint_ratchet(&self, version: Option) -> Option { let endpoint_container = self.endpoint_container.as_ref()?; - endpoint_container.endpoint_crypto.get_ratchet(version) + endpoint_container.ratchet_manager.get_ratchet(version) } } pub struct EndpointChannelContainer { - pub(crate) default_security_settings: SessionSecuritySettings, - // this is only loaded if STUN-like NAT-traversal works pub(crate) direct_p2p_remote: Option, - pub(crate) endpoint_crypto: PeerSessionCrypto, - to_default_channel: OrderedChannel, + pub(crate) ratchet_manager: ProtocolRatchetManager, + pub(crate) channel_signal: Option>, + to_ordered_local_channel: OrderedChannel>, // for UDP - pub(crate) to_unordered_channel: Option, - #[allow(dead_code)] - pub(crate) peer_socket_addr: SocketAddr, + pub(crate) to_unordered_local_channel: Option, pub(crate) file_transfer_compatible: bool, } -pub struct C2SChannelContainer { - to_channel: OrderedChannel, - // for UDP - pub(crate) to_unordered_channel: Option, - is_active: Arc, - to_primary_stream: OutboundPrimaryStreamSender, - pub(crate) channel_signal: Option, - pub(crate) peer_session_crypto: PeerSessionCrypto, -} - pub(crate) struct UnorderedChannelContainer { to_channel: UnboundedSender, stopper_tx: citadel_io::tokio::sync::oneshot::Sender<()>, @@ -291,6 +273,9 @@ impl EndpointChannelContainer { impl Drop for VirtualConnection { fn drop(&mut self) { self.is_active.store(false, Ordering::SeqCst); + if let Some(endpoint_container) = self.endpoint_container.as_mut() { + let _ = endpoint_container.ratchet_manager.shutdown(); + } } } @@ -330,8 +315,8 @@ impl VirtualConnectionType { pub fn get_target_cid(&self) -> u64 { match self { VirtualConnectionType::LocalGroupServer { session_cid: _cid } => { - // by rule of the network, the target CID is zero if a hyperlan peer -> hyperlan server conn - 0 + // by rule of the network, the target CID is zero if a C2S connection + C2S_IDENTITY_CID } VirtualConnectionType::LocalGroupPeer { @@ -494,8 +479,7 @@ pub(super) struct NetworkStats { } pub(crate) struct OutboundTransmitterContainer { - ratchet_constructor: Option, - pub(crate) burst_transmitter: GroupTransmitter, + pub(crate) burst_transmitter: ObjectTransmitter, // in the case of file transfers, it is desirable to wake-up the async task // that enqueues the next group object_notifier: Option>, @@ -512,18 +496,16 @@ pub(crate) struct OutboundTransmitterContainer { impl OutboundTransmitterContainer { pub fn new( object_notifier: Option>, - mut burst_transmitter: GroupTransmitter, + burst_transmitter: ObjectTransmitter, group_plaintext_length: usize, parent_object_total_groups: usize, relative_group_id: u32, ticket: Ticket, ) -> Self { - let ratchet_constructor = burst_transmitter.ratchet_container.base_constructor.take(); let transmission_start_time = Instant::now(); let has_begun = false; Self { - ratchet_constructor, has_begun, relative_group_id, ticket, @@ -585,7 +567,7 @@ impl StateContainerInner { /// Creates a new container #[allow(clippy::too_many_arguments)] pub fn create( - kernel_tx: UnboundedSender, + kernel_tx: UnboundedSender>, hdp_server_remote: NodeRemote, keep_alive_timeout_ns: i64, state: DualCell, @@ -607,19 +589,15 @@ impl StateContainerInner { session_security_settings, time_tracker, cnac, - updates_in_progress: HashMap::new(), hole_puncher_pipes: HashMap::new(), tcp_loaded_status: None, - enqueued_packets: HashMap::new(), state, - c2s_channel_container: None, keep_alive_timeout_ns, node_remote: hdp_server_remote, meta_expiry_state: Default::default(), pre_connect_state: Default::default(), udp_primary_outbound_tx: None, deregister_state: Default::default(), - ratchet_update_state: Default::default(), active_virtual_connections: Default::default(), network_stats: Default::default(), kernel_tx, @@ -631,11 +609,12 @@ impl StateContainerInner { inbound_files: HashMap::new(), outbound_files: HashMap::new(), session_passwords: HashMap::new(), + triggered_rekeys: Arc::new(Mutex::new(HashMap::new())), }; inner.into() } - // Note: c2s connections should not be stored here. They are stored in the session.rs file + // Note: c2s connection passwords are also stored as "session_password" in the [`CitadelSession`] pub fn store_session_password(&mut self, peer_cid: u64, session_password: PreSharedKey) { self.session_passwords.insert(peer_cid, session_password); } @@ -644,7 +623,7 @@ impl StateContainerInner { self.session_passwords.get(&peer_cid) } - // TODO: use this + // TODO: use this in period cleanup tasks #[allow(dead_code)] pub fn remove_session_password(&mut self, peer_cid: u64) { self.session_passwords.remove(&peer_cid); @@ -669,9 +648,11 @@ impl StateContainerInner { ) } - get_inner(self, peer_cid) - .or_else(|| Some(&self.c2s_channel_container.as_ref()?.to_primary_stream)) - .unwrap() + // On fallback + get_inner(self, peer_cid).unwrap_or_else(|| { + get_inner(self, C2S_IDENTITY_CID) + .expect("The C2S virtual connection should always exist") + }) } /// This assumes the data has reached its destination endpoint, and must be forwarded to the channel @@ -680,39 +661,20 @@ impl StateContainerInner { &mut self, target_cid: u64, group_id: u64, - data: SecBuffer, - ) -> bool { - if target_cid == 0 { - if let Some(c2s_container) = self.c2s_channel_container.as_mut() { - return c2s_container - .to_channel - .on_packet_received(group_id, data) - .is_ok(); - } - } else if let Some(vconn) = self.active_virtual_connections.get_mut(&target_cid) { - if let Some(channel) = vconn.endpoint_container.as_mut() { - return channel - .to_default_channel - .on_packet_received(group_id, data) - .is_ok(); - } - } - - false + data: RatchetMessage, + ) -> Result<(), NetworkError> { + let endpoint_container = self.get_endpoint_container_mut(target_cid)?; + endpoint_container + .to_ordered_local_channel + .on_packet_received(group_id, data) } /// This assumes the data has reached its destination endpoint, and must be forwarded to the channel /// (thus bypassing the unordered kernel) pub fn forward_data_to_unordered_channel(&self, target_cid: u64, data: SecBuffer) -> bool { - if target_cid == 0 { - if let Some(c2s_container) = self.c2s_channel_container.as_ref() { - if let Some(unordered_channel) = c2s_container.to_unordered_channel.as_ref() { - return unordered_channel.to_channel.unbounded_send(data).is_ok(); - } - } - } else if let Some(vconn) = self.active_virtual_connections.get(&target_cid) { + if let Some(vconn) = self.active_virtual_connections.get(&target_cid) { if let Some(channel) = vconn.endpoint_container.as_ref() { - if let Some(unordered_channel) = channel.to_unordered_channel.as_ref() { + if let Some(unordered_channel) = channel.to_unordered_local_channel.as_ref() { return unordered_channel.to_channel.unbounded_send(data).is_ok(); } } @@ -731,30 +693,8 @@ impl StateContainerInner { ticket: Ticket, to_udp_stream: OutboundUdpSender, stopper_tx: citadel_io::tokio::sync::oneshot::Sender<()>, - ) -> Option { - if target_cid == 0 { - if let Some(c2s_container) = self.c2s_channel_container.as_mut() { - self.udp_primary_outbound_tx = Some(to_udp_stream.clone()); - let (to_channel, rx) = unbounded(); - let udp_channel = UdpChannel::new( - to_udp_stream, - rx, - target_cid, - v_conn, - ticket, - c2s_container.is_active.clone(), - self.node_remote.clone(), - ); - c2s_container.to_unordered_channel = Some(UnorderedChannelContainer { - to_channel, - stopper_tx, - }); - // data can now be forwarded - Some(udp_channel) - } else { - None - } - } else if let Some(p2p_container) = self.active_virtual_connections.get_mut(&target_cid) { + ) -> Option> { + if let Some(p2p_container) = self.active_virtual_connections.get_mut(&target_cid) { if let Some((sender, _)) = p2p_container.sender.as_mut() { *sender = Some(to_udp_stream.clone()); if let Some(p2p_endpoint_container) = p2p_container.endpoint_container.as_mut() { @@ -768,10 +708,11 @@ impl StateContainerInner { p2p_container.is_active.clone(), self.node_remote.clone(), ); - p2p_endpoint_container.to_unordered_channel = Some(UnorderedChannelContainer { - to_channel, - stopper_tx, - }); + p2p_endpoint_container.to_unordered_local_channel = + Some(UnorderedChannelContainer { + to_channel, + stopper_tx, + }); // data can now be forwarded Some(udp_channel) } else { @@ -786,16 +727,11 @@ impl StateContainerInner { } pub fn remove_udp_channel(&mut self, target_cid: u64) { - if target_cid == 0 { - if let Some(c2s_container) = self.c2s_channel_container.as_mut() { - if let Some(channel) = c2s_container.to_unordered_channel.take() { - let _ = channel.stopper_tx.send(()); - } - } - } else if let Some(p2p_container) = self.active_virtual_connections.get_mut(&target_cid) { + if let Some(p2p_container) = self.active_virtual_connections.get_mut(&target_cid) { if let Some((sender, _)) = p2p_container.sender.as_mut() { if let Some(p2p_endpoint_container) = p2p_container.endpoint_container.as_mut() { - if let Some(channel) = p2p_endpoint_container.to_unordered_channel.take() { + if let Some(channel) = p2p_endpoint_container.to_unordered_local_channel.take() + { let _ = channel.stopper_tx.send(()); } *sender = None; @@ -840,52 +776,146 @@ impl StateContainerInner { #[allow(unused_results)] #[allow(clippy::too_many_arguments)] - pub fn insert_new_peer_virtual_connection_as_endpoint( + pub fn create_virtual_connection( &mut self, - peer_socket_addr: SocketAddr, default_security_settings: SessionSecuritySettings, channel_ticket: Ticket, target_cid: u64, - connection_type: VirtualConnectionType, + virtual_connection_type: VirtualConnectionType, endpoint_crypto: PeerSessionCrypto, sess: &CitadelSession, file_transfer_compatible: bool, - ) -> PeerChannel { - let (channel_tx, channel_rx) = unbounded(); - let (tx, rx) = crate::proto::outbound_sender::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); + ) -> PeerChannel { + let (tx_ratchet_manager_to_outbound, mut rx_from_ratchet_manager_to_outbound) = + unbounded::>(); + let (tx_to_outbound, rx_for_outbound) = + crate::proto::outbound_sender::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); + let (rekey_tx, mut rekey_rx) = tokio::sync::mpsc::unbounded_channel::(); + // Take messages from the ratchet manager , forward it to the dedicated outbound sender + let task_outbound = async move { + while let Some(ratchet_layer_message) = rx_from_ratchet_manager_to_outbound.recv().await + { + if let Err(err) = tx_to_outbound + .send(SessionRequest::SendMessage(ratchet_layer_message)) + .await + { + citadel_logging::error!(target: "citadel", "Failed to send secure protocol packet for {virtual_connection_type}: {err:?}"); + break; + } + } + + citadel_logging::warn!(target: "citadel", "Outbound ratchet task for {virtual_connection_type} ended"); + }; + + let kernel_tx = self.kernel_tx.clone(); + let session_cid = virtual_connection_type.get_session_cid(); + let triggered_rekeys = self.triggered_rekeys.clone(); + // On each rekey finished, take the received ratchet, R, and send it through the kernel_tx + let task_rekey_finished_listener = async move { + while let Some(rekey_finished) = rekey_rx.recv().await { + let mut lock = triggered_rekeys.lock(); + if let Some(entry) = lock.iter().find(|r| r.0.target_cid == target_cid) { + let ticket = *entry.1; + let to_remove = *entry.0; + lock.remove(&to_remove); + let result = NodeResult::ReKeyResult(ReKeyResult { + ticket, + status: ReKeyReturnType::Success { + version: rekey_finished.version(), + }, + session_cid, + }); + if let Err(err) = kernel_tx.unbounded_send(result) { + citadel_logging::error!(target: "citadel", "Failed to send rekey result for {virtual_connection_type}: {err:?}"); + break; + } + } + } + }; + + let password_cid_index = match virtual_connection_type { + VirtualConnectionType::LocalGroupPeer { .. } => target_cid, // TODO make sure this is right + VirtualConnectionType::LocalGroupServer { .. } => C2S_IDENTITY_CID, + _ => { + panic!("HyperWAN functionality not yet enabled"); + } + }; + + let psks = self + .get_session_password(password_cid_index) + .cloned() + .expect("The PSK was not found!"); + + let (tx_to_ratchet_manager_inbound, rx_for_ratchet_manager) = unbounded(); + + let ratchet_manager = ProtocolRatchetManager::new( + Box::new(tx_ratchet_manager_to_outbound), + Box::new(UnboundedReceiverStream::new(rx_for_ratchet_manager)), + endpoint_crypto, + psks.as_ref(), + ); + let is_active = Arc::new(AtomicBool::new(true)); - self.updates_in_progress - .insert(target_cid, endpoint_crypto.update_in_progress.clone()); - + let protocol_messenger = ProtocolMessenger::new( + ratchet_manager.clone(), + default_security_settings.secrecy_mode, + Some(rekey_tx), + is_active.clone(), + ); + + // This will automatically take inbound messages, order them, and forward them to the ratchet manager for processing + // where the ratchet manager will automatically forward the processed messages to the protocol_messenger above + let to_channel = OrderedChannel::new(tx_to_ratchet_manager_inbound); + + // We don't need an inbound task since: + // [*] Inbound messages get passed like usual to the ordered channel (1) + // [*] The ordered channel passes the message to the ratchet manager (2) + // [*] the ratchet manager passes to the protocol messenger (3) + // [*] the protocol messenger gets polled for messages (4) + + let is_server = sess.is_server; + + let combined_task = async move { + tokio::select! { + _ = task_rekey_finished_listener => {} + _ = task_outbound => {} + }; + + citadel_logging::warn!(target: "citadel", "Combined task for {virtual_connection_type} ended (is_server: {is_server})"); + }; + + spawn!(combined_task); + let peer_channel = PeerChannel::new( self.node_remote.clone(), target_cid, - connection_type, + virtual_connection_type, channel_ticket, default_security_settings.security_level, is_active.clone(), - channel_rx, - tx, + protocol_messenger, + ); + + CitadelSession::spawn_message_sender_function( + sess.clone(), + virtual_connection_type, + rx_for_outbound, ); - let to_channel = OrderedChannel::new(channel_tx); - CitadelSession::spawn_message_sender_function(sess.clone(), rx); let endpoint_container = Some(EndpointChannelContainer { - default_security_settings, direct_p2p_remote: None, - endpoint_crypto, - to_default_channel: to_channel, - to_unordered_channel: None, - peer_socket_addr, + ratchet_manager, + channel_signal: None, + to_ordered_local_channel: to_channel, + to_unordered_local_channel: None, file_transfer_compatible, }); let vconn = VirtualConnection { last_delivered_message_timestamp: DualRwLock::from(None), - connection_type, + connection_type: virtual_connection_type, is_active, - // this is None for endpoints, as there's no need for this sender: None, endpoint_container, }; @@ -900,46 +930,43 @@ impl StateContainerInner { pub fn init_new_c2s_virtual_connection( &mut self, cnac: &ClientNetworkAccount, - security_level: SecurityLevel, channel_ticket: Ticket, session_cid: u64, session: &CitadelSession, - ) -> PeerChannel { - let (channel_tx, channel_rx) = unbounded(); - let (tx, rx) = crate::proto::outbound_sender::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); - let is_active = Arc::new(AtomicBool::new(true)); - let peer_channel = PeerChannel::new( - self.node_remote.clone(), - session_cid, - VirtualConnectionType::LocalGroupServer { session_cid }, + ) -> PeerChannel { + let security_settings = self + .session_security_settings + .expect("Should be set at beginning of session or on first SYN packet"); + // Reuse the latest one. During SYN/SYN_ACK process, toolsets should be reset inside the endpoint_crypto + let endpoint_crypto = cnac.get_session_crypto().clone(); + + let channel = self.create_virtual_connection( + security_settings, channel_ticket, - security_level, - is_active.clone(), - channel_rx, - tx, + C2S_IDENTITY_CID, + VirtualConnectionType::LocalGroupServer { session_cid }, + endpoint_crypto, + session, + true, ); - CitadelSession::spawn_message_sender_function(session.clone(), rx); - let c2s = C2SChannelContainer { - to_channel: OrderedChannel::new(channel_tx), - to_unordered_channel: None, - is_active, - to_primary_stream: session.to_primary_stream.clone().unwrap(), - channel_signal: None, - peer_session_crypto: cnac.get_session_crypto().new_session(), + let p2p_remote = DirectP2PRemote { + stopper: None, + p2p_primary_stream: session + .to_primary_stream + .clone() + .expect("Should be set at beginning of session or on first SYN packet"), + from_listener: false, }; - let updates_in_progress = c2s.peer_session_crypto.update_in_progress.clone(); - - self.c2s_channel_container = Some(c2s); - - self.updates_in_progress.insert(0, updates_in_progress); + self.insert_direct_p2p_connection(p2p_remote, C2S_IDENTITY_CID) + .expect("C2S insertion should not fail"); if let Some(udp_alerter) = self.tcp_loaded_status.take() { let _ = udp_alerter.send(()); } - peer_channel + channel } pub fn setup_tcp_alert_if_udp_c2s(&mut self) -> citadel_io::tokio::sync::oneshot::Receiver<()> { @@ -975,29 +1002,23 @@ impl StateContainerInner { log::trace!(target: "citadel", "Vconn {} -> {} established", connection_type.get_session_cid(), target_cid); } - pub fn get_peer_session_crypto(&self, peer_cid: u64) -> Option<&PeerSessionCrypto> { + pub fn get_virtual_connection_crypto(&self, peer_cid: u64) -> Option<&PeerSessionCrypto> { Some( - &self - .active_virtual_connections + self.active_virtual_connections .get(&peer_cid)? .endpoint_container .as_ref()? - .endpoint_crypto, + .ratchet_manager + .session_crypto_state(), ) } - pub fn get_peer_endpoint_container_mut( + pub fn get_virtual_connection_mut( &mut self, target_cid: u64, - ) -> Result<&mut EndpointChannelContainer, NetworkError> { + ) -> Result<&mut VirtualConnection, NetworkError> { if let Some(vconn) = self.active_virtual_connections.get_mut(&target_cid) { - if let Some(endpoint_container) = vconn.endpoint_container.as_mut() { - Ok(endpoint_container) - } else { - Err(NetworkError::msg(format!( - "Unable to access endpoint container to peer {target_cid}" - ))) - } + Ok(vconn) } else { Err(NetworkError::msg(format!( "Unable to find virtual connection to peer {target_cid}" @@ -1005,8 +1026,45 @@ impl StateContainerInner { } } - pub fn get_c2s_crypto(&self) -> Option<&PeerSessionCrypto> { - Some(&self.c2s_channel_container.as_ref()?.peer_session_crypto) + pub fn get_virtual_connection( + &self, + target_cid: u64, + ) -> Result<&VirtualConnection, NetworkError> { + if let Some(vconn) = self.active_virtual_connections.get(&target_cid) { + Ok(vconn) + } else { + Err(NetworkError::msg(format!( + "Unable to find virtual connection to peer {target_cid}" + ))) + } + } + + pub fn get_endpoint_container_mut( + &mut self, + target_cid: u64, + ) -> Result<&mut EndpointChannelContainer, NetworkError> { + let v_conn = self.get_virtual_connection_mut(target_cid)?; + if let Some(endpoint_container) = v_conn.endpoint_container.as_mut() { + Ok(endpoint_container) + } else { + Err(NetworkError::msg(format!( + "Unable to access endpoint container to peer {target_cid}" + ))) + } + } + + pub fn get_endpoint_container( + &self, + target_cid: u64, + ) -> Result<&EndpointChannelContainer, NetworkError> { + let v_conn = self.get_virtual_connection(target_cid)?; + if let Some(endpoint_container) = v_conn.endpoint_container.as_ref() { + Ok(endpoint_container) + } else { + Err(NetworkError::msg(format!( + "Unable to access endpoint container to peer {target_cid}" + ))) + } } /// When a keep alive is received, this function gets called. Prior to getting called, @@ -1136,7 +1194,7 @@ impl StateContainerInner { metadata_orig: VirtualObjectMetadata, pers: &PersistenceHandler, state_container: StateContainer, - stacked_ratchet: R, + ratchet: R, _target_cid: u64, v_target_flipped: VirtualTargetType, preferred_primary_stream: OutboundPrimaryStreamSender, @@ -1221,7 +1279,7 @@ impl StateContainerInner { // first, send a rebound signal immediately to the sender // to ensure the sender knows if the user accepted or not let file_header_ack = packet_crafter::file::craft_file_header_ack_packet( - &stacked_ratchet, + &ratchet, accepted, object_id, target_cid, @@ -1258,7 +1316,7 @@ impl StateContainerInner { Ok(header) => { // write the header let wave_ack = packet_crafter::group::craft_wave_ack( - &stacked_ratchet, + &ratchet, object_id, get_resp_target_cid_from_header(&header), header.group.get(), @@ -1307,7 +1365,7 @@ impl StateContainerInner { Err(err) => { log::error!(target: "citadel", "Start_recv_rx failed: {:?}", err); let err_packet = packet_crafter::file::craft_file_header_ack_packet( - &stacked_ratchet, + &ratchet, false, object_id, target_cid, @@ -1420,40 +1478,14 @@ impl StateContainerInner { #[allow(clippy::too_many_arguments)] pub fn on_group_header_ack_received( &mut self, - session: &CitadelSession, - base_session_secrecy_mode: SecrecyMode, peer_cid: u64, - target_cid: u64, group_id: u64, object_id: ObjectId, next_window: Option>, - transfer: KemTransferStatus, fast_msg: bool, ) -> bool { let key = GroupKey::new(peer_cid, group_id, object_id); - let constructor = if let Some(outbound_container) = self.outbound_transmitters.get_mut(&key) - { - outbound_container.ratchet_constructor.take() - } else { - log::warn!(target: "citadel", "Key for outbound transmitter absent"); - return false; - }; - - if attempt_kem_as_alice_finish( - session, - base_session_secrecy_mode, - peer_cid, - target_cid, - transfer, - self, - constructor, - ) - .is_err() - { - return true; - } - if fast_msg { let _ = self.outbound_transmitters.remove(&key); // we don't proceed past here b/c there's no need to send more data @@ -1587,11 +1619,7 @@ impl StateContainerInner { if let Some(local_encryption_level) = file_container.local_encryption_level { log::trace!(target: "citadel", "Detected REVFS. Locally decrypting object {object_id} with level {local_encryption_level:?} | Ratchet used: {} w/version {}", hr.get_cid(), hr.version()); // which static hr do we need? Since we are receiving this chunk, always our local account's - let static_aux_hr = self - .cnac - .as_ref() - .unwrap() - .get_static_auxiliary_stacked_ratchet(); + let static_aux_hr = self.cnac.as_ref().unwrap().get_static_auxiliary_ratchet(); chunk = static_aux_hr .local_decrypt(chunk, local_encryption_level) @@ -1708,7 +1736,11 @@ impl StateContainerInner { if let Some(transmitter_container) = self.outbound_transmitters.get_mut(&key) { // we set has_begun here instead of the transmit_tcp, simply because we want the first wave to ACK transmitter_container.has_begun = true; - let transmitter = &mut transmitter_container.burst_transmitter.group_transmitter; + let transmitter = transmitter_container + .burst_transmitter + .group_transmitter + .as_mut() + .expect("Transmitter not found"); let relative_group_id = transmitter_container.relative_group_id; if transmitter.on_wave_tail_ack_received(wave_id) { // Group is finished. Delete it @@ -1797,408 +1829,14 @@ impl StateContainerInner { } } - fn get_secrecy_mode(&self, target_cid: u64) -> Option { - if target_cid != C2S_ENCRYPTION_ONLY { - Some( - self.active_virtual_connections - .get(&target_cid)? - .endpoint_container - .as_ref()? - .default_security_settings - .secrecy_mode, - ) - } else { - self.session_security_settings - .as_ref() - .map(|r| r.secrecy_mode) - } - } - - /// Returns true if a packet was sent, false otherwise. This should only be called when a packet is received - pub(crate) fn poll_next_enqueued(&mut self, target_cid: u64) -> Result { - log::trace!(target: "citadel", "Polling next for {}", target_cid); - let secrecy_mode = self - .get_secrecy_mode(target_cid) - .ok_or(NetworkError::InternalError("Secrecy mode not loaded"))?; - match secrecy_mode { - SecrecyMode::BestEffort => {} - - SecrecyMode::Perfect => { - // fetch_nand(false - let update_in_progress = self - .updates_in_progress - .get(&target_cid) - .map(|r| r.toggle_on_if_untoggled() == CurrentToggleState::AlreadyToggled) - .ok_or(NetworkError::InternalError( - "Update state not loaded in hashmap!", - ))?; - - // We have to make sure when this is called, it also sets update_in_progress to true to place a lock. We will also need to reinforce this via a force_mode inside the get_next_constructor fn in the crypt container - // it's possible in high-stress loads, a new inbound packet triggers update_in_progress to true right after checking below. The fetch_nand w/false helps us achieve this - if update_in_progress { - log::trace!(target: "citadel", "Cannot send packet at this time since update_in_progress"); // in this case, update will happen upon reception of TRUNCATE packet - return Ok(false); - } - - let queue = self.enqueued_packets.entry(target_cid).or_default(); - log::trace!(target: "citadel", "Queue has: {} items", queue.len()); - // since we have a mutable lock on the session, no other attempts will happen. We can safely pop the front of the queue and rest assured that it won't be denied a send this time - if let Some((ticket, packet, virtual_target, security_level)) = queue.pop_front() { - //std::mem::drop(enqueued); - return self - .process_outbound_message( - ticket, - packet, - virtual_target, - security_level, - true, - ) - .map(|_| true); - } else { - log::trace!(target: "citadel", "NO packets enqueued for target {}", target_cid); - } - } - } - - Ok(false) - } - - fn enqueue_packet( - &mut self, - target_cid: u64, - ticket: Ticket, - packet: SecureProtocolPacket, - target: VirtualTargetType, - security_level: SecurityLevel, - ) { - self.enqueued_packets - .entry(target_cid) - .or_default() - .push_back((ticket, packet, target, security_level)) - } - - fn has_enqueued(&self, target_cid: u64) -> bool { - self.enqueued_packets - .get(&target_cid) - .map(|r| r.front().is_some()) - .unwrap_or(false) - } - #[allow(unused_results)] - pub(crate) fn process_outbound_message( + pub(crate) fn initiate_rekey( &mut self, - ticket: Ticket, - packet: SecureProtocolPacket, - virtual_target: VirtualTargetType, - security_level: SecurityLevel, - called_from_poll: bool, - ) -> Result<(), NetworkError> { - let this = self; - - if !this.state.is_connected() { - Err(NetworkError::Generic(format!( - "Attempted to send data (ticket: {ticket}) outbound, but the session is not connected" - ))) - } else { - // first, make sure that there aren't already packets in the queue (unless we were called from the poll, in which case, we are getting the latest version) - let secrecy_mode = this - .get_secrecy_mode(virtual_target.get_target_cid()) - .ok_or(NetworkError::InternalError("Secrecy mode not loaded"))?; - - let time_tracker = this.time_tracker; - - if secrecy_mode == SecrecyMode::Perfect && !called_from_poll { - //let mut enqueued = inner_mut!(this.enqueued_packets); - if this.has_enqueued(virtual_target.get_target_cid()) - || this - .updates_in_progress - .get(&virtual_target.get_target_cid()) - .map(|r| r.state() == CurrentToggleState::AlreadyToggled) - .ok_or({ - NetworkError::InternalError("Update in progress not loaded for client") - })? - { - // If there are packets enqueued, it doesn't matter if an update is in progress or not. Queue this packet - //log::trace!(target: "citadel", "[ABX] enqueuing packet for {:?}", virtual_target); - this.enqueue_packet( - virtual_target.get_target_cid(), - ticket, - packet, - virtual_target, - security_level, - ); - return Ok(()); - } - } - - // object singleton == 0 implies that the data does not belong to a file - const OBJECT_SINGLETON: ObjectId = ObjectId::zero(); - // Drop this to ensure that it doesn't block other async closures from accessing the inner device - // std::mem::drop(this); - let (mut transmitter, group_id, target_cid) = match virtual_target { - VirtualTargetType::LocalGroupServer { session_cid } => { - // if we are sending this just to the HyperLAN server (in the case of file uploads), - // then, we use this session's pqc, the cnac's latest entropy_bank, and 0 for target_cid - let crypt_container = &mut this - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; - let latest_stacked_ratchet = - crypt_container.get_ratchet(None).cloned().unwrap(); - latest_stacked_ratchet.verify_level(Some(security_level)).map_err(|_err| NetworkError::Generic(format!("Invalid security level. The maximum security level for this session is {:?}", latest_stacked_ratchet.get_default_security_level())))?; - let constructor = crypt_container.get_next_constructor(); - - let result = match secrecy_mode { - SecrecyMode::BestEffort => { - let group_id = crypt_container.get_and_increment_group_id(); - Either::Left((constructor, latest_stacked_ratchet, group_id, packet)) - } - - SecrecyMode::Perfect => { - if constructor.is_some() { - // we can perform a kex - let group_id = crypt_container.get_and_increment_group_id(); - Either::Left(( - constructor, - latest_stacked_ratchet, - group_id, - packet, - )) - } else { - // kex later - Either::Right(packet) - } - } - }; - - match result { - Either::Left(( - alice_constructor, - latest_stacked_ratchet, - group_id, - packet, - )) => { - let to_primary_stream = this.get_primary_stream().cloned().unwrap(); - ( - GroupTransmitter::new_message( - to_primary_stream, - OBJECT_SINGLETON, - RatchetPacketCrafterContainer::new( - latest_stacked_ratchet, - alice_constructor, - ), - packet, - security_level, - group_id, - ticket, - time_tracker, - ) - .ok_or({ - NetworkError::InternalError( - "Unable to create the outbound transmitter", - ) - })?, - group_id, - session_cid, - ) - } - - Either::Right(packet) => { - // store inside hashmap - log::trace!(target: "citadel", "[ATC] Enqueuing c2s packet"); - this.enqueue_packet( - C2S_ENCRYPTION_ONLY, - ticket, - packet, - virtual_target, - security_level, - ); - return Ok(()); - } - } - } - - VirtualConnectionType::LocalGroupPeer { - session_cid, - peer_cid: target_cid, - } => { - log::trace!(target: "citadel", "Maybe sending HyperLAN peer ({}) <-> HyperLAN Peer ({})", session_cid, target_cid); - // here, we don't use the base session's PQC. Instead, we use the vconn's pqc and Toolset - let default_primary_stream = this.get_primary_stream().cloned().unwrap(); - - if let Some(vconn) = this.active_virtual_connections.get_mut(&target_cid) { - if let Some(endpoint_container) = vconn.endpoint_container.as_mut() { - //let group_id = endpoint_container.endpoint_crypto.get_and_increment_group_id(); - let to_primary_stream_preferred = endpoint_container.get_direct_p2p_primary_stream().cloned().unwrap_or_else(|| { - log::trace!(target: "citadel", "Reverting to primary stream since p2p conn not loaded"); - if cfg!(feature = "localhost-testing-assert-no-proxy") { - log::error!(target: "citadel", "*** Feature flag asserted no proxying, yet, message requires proxy ***"); - std::process::exit(1); - } - - default_primary_stream - }); - //let to_primary_stream_preferred = this.to_primary_stream.clone().unwrap(); - let latest_usable_ratchet = endpoint_container - .endpoint_crypto - .get_ratchet(None) - .unwrap() - .clone(); - latest_usable_ratchet.verify_level(Some(security_level)).map_err(|_err| NetworkError::Generic(format!("Invalid security level. The maximum security level for this session is {:?}", latest_usable_ratchet.get_default_security_level())))?; - let constructor = - endpoint_container.endpoint_crypto.get_next_constructor(); - - match secrecy_mode { - SecrecyMode::BestEffort => { - let group_id = endpoint_container - .endpoint_crypto - .get_and_increment_group_id(); - ( - GroupTransmitter::new_message( - to_primary_stream_preferred, - OBJECT_SINGLETON, - RatchetPacketCrafterContainer::new( - latest_usable_ratchet, - constructor, - ), - packet, - security_level, - group_id, - ticket, - time_tracker, - ) - .ok_or({ - NetworkError::InternalError( - "Unable to create the outbound transmitter", - ) - })?, - group_id, - target_cid, - ) - } - - SecrecyMode::Perfect => { - // Note: we can't just add/send here. What if there are packets in the queue? We thus must poll before calling the below function - if constructor.is_some() { - let group_id = endpoint_container - .endpoint_crypto - .get_and_increment_group_id(); - log::trace!(target: "citadel", "[Perfect] will send group {}", group_id); - ( - GroupTransmitter::new_message( - to_primary_stream_preferred, - OBJECT_SINGLETON, - RatchetPacketCrafterContainer::new( - latest_usable_ratchet, - constructor, - ), - packet, - security_level, - group_id, - ticket, - time_tracker, - ) - .ok_or( - { - NetworkError::InternalError( - "Unable to create the outbound transmitter", - ) - }, - )?, - group_id, - target_cid, - ) - } else { - //assert!(!called_from_poll); - // Being called from poll should only happen when a packet needs to be sent, and is ready to be sent. Further, being called from the poll adds a lock ensuring it gets sent - if called_from_poll { - std::process::exit(1); // for dev purposes - } - - //std::mem::drop(state_container); - log::trace!(target: "citadel", "[Perfect] will enqueue packet"); - //let mut enqueued_packets = inner_mut!(this.enqueued_packets); - this.enqueue_packet( - target_cid, - ticket, - packet, - virtual_target, - security_level, - ); - return Ok(()); - } - } - } - } else { - return Err(NetworkError::InternalError( - "Endpoint container not found", - )); - } - } else { - log::error!(target: "citadel", "Unable to find active vconn for the channel"); - return Ok(()); - } - } - - _ => { - return Err(NetworkError::InvalidRequest( - "HyperWAN functionality not yet implemented", - )); - } - }; - - // We manually send the header. The tails get sent automatically - log::trace!(target: "citadel", "[message] Sending GROUP HEADER through primary stream for group {} as {}", group_id, if this.is_server { "Server" } else { "Client" }); - let group_len = transmitter.get_total_plaintext_bytes(); - transmitter.transmit_group_header(virtual_target)?; - let object_id = transmitter.object_id; - //this.transfer_stats += TransferStats::new(timestamp, group_len as isize); - - let outbound_container = - OutboundTransmitterContainer::new(None, transmitter, group_len, 1, 0, ticket); - // The payload packets won't be sent until a GROUP_HEADER_ACK is received - // NOTE: Ever since using GroupKeys, we use either the session_cid (for client -> server conns) or target_cids (for peer conns) - let key = GroupKey::new(target_cid, group_id, object_id); - //inner_mut!(this.state_container).outbound_transmitters.insert(key, outbound_container); - this.outbound_transmitters.insert(key, outbound_container); - - //std::mem::drop(state_container); - - this.queue_handle.insert_ordinary(group_id as usize, target_cid, GROUP_EXPIRE_TIME_MS, move |state_container| { - if let Some(transmitter) = state_container.outbound_transmitters.get(&key) { - let transmitter = &transmitter.burst_transmitter.group_transmitter; - if transmitter.has_expired(GROUP_EXPIRE_TIME_MS) { - if state_container.meta_expiry_state.expired() { - log::warn!(target: "citadel", "Outbound group {} has expired; dropping from map", group_id); - QueueWorkerResult::Complete - } else { - log::trace!(target: "citadel", "Other outbound groups being processed; patiently awaiting group {}", group_id); - QueueWorkerResult::Incomplete - } - } else { - // it hasn't expired yet, and is still transmitting - QueueWorkerResult::Incomplete - } - } else { - // it finished - QueueWorkerResult::Complete - } - }); - - Ok(()) - } - } - - #[allow(unused_results)] - pub(crate) fn initiate_entropy_bank_update( - &mut self, - timestamp: i64, virtual_target: VirtualTargetType, ticket: Option, ) -> Result<(), NetworkError> { - fn return_already_in_progress( - kernel_tx: &UnboundedSender, + fn return_already_in_progress( + kernel_tx: &UnboundedSender>, ticket: Ticket, session_cid: u64, ) -> Result<(), NetworkError> { @@ -2211,149 +1849,61 @@ impl StateContainerInner { .map_err(|err| NetworkError::Generic(err.to_string())) } + let ticket = ticket.unwrap_or_default(); + if !self.state.is_connected() { return Err(NetworkError::InvalidRequest( "Cannot initiate rekey since the session is not connected", )); } - let session_security_settings = self.session_security_settings.unwrap(); - let security_level = session_security_settings.security_level; - let default_primary_stream = &(self - .get_primary_stream() - .cloned() - .ok_or(NetworkError::InternalError("Primary stream not loaded"))?); - - match virtual_target { + let (session_cid, target_cid) = match virtual_target { VirtualConnectionType::LocalGroupServer { session_cid } => { - let crypt_container = &mut self - .c2s_channel_container - .as_mut() - .unwrap() - .peer_session_crypto; - - match crypt_container.get_next_constructor() { - Some(alice_constructor) => { - let ratchet = crypt_container.get_ratchet(None).unwrap(); - let stage0_packet = packet_crafter::do_entropy_bank_update::craft_stage0( - ratchet, - alice_constructor - .stage0_alice() - .ok_or(NetworkError::InternalError("Alice Construction failed"))?, - timestamp, - C2S_ENCRYPTION_ONLY, - security_level, - ); - self.ratchet_update_state.alice_stacked_ratchet = Some(alice_constructor); - if let Some(ticket) = ticket { - // this request requires tracking - let _ = self - .ratchet_update_state - .current_local_requests - .insert(virtual_target, ticket); - } - - let to_primary_stream = self.get_primary_stream().unwrap(); - let kernel_tx = &self.kernel_tx; - CitadelSession::::send_to_primary_stream_closure( - to_primary_stream, - kernel_tx, - stage0_packet, - ticket, - Some(session_cid), - ) - } - - None => { - log::trace!(target: "citadel", "Won't perform update b/c concurrent c2s update occurring"); - if let Some(ticket) = ticket { - return_already_in_progress( - &self.kernel_tx, - ticket, - virtual_target.get_session_cid(), - ) - } else { - Ok(()) - } - } - } + (session_cid, C2S_IDENTITY_CID) } VirtualConnectionType::LocalGroupPeer { - session_cid: _, peer_cid, - } => { - const MISSING: NetworkError = NetworkError::InvalidRequest("Peer not connected"); - let endpoint_container = &mut self - .active_virtual_connections - .get_mut(&peer_cid) - .ok_or(MISSING)? - .endpoint_container - .as_mut() - .ok_or(MISSING)?; - let crypt = &mut endpoint_container.endpoint_crypto; - let alice_constructor = crypt.get_next_constructor(); - let latest_stacked_ratchet = crypt - .get_ratchet(None) - .cloned() - .ok_or(NetworkError::InternalError("Ratchet not loaded"))?; - - match alice_constructor { - Some(alice_constructor) => { - let to_primary_stream_preferred = endpoint_container - .get_direct_p2p_primary_stream() - .unwrap_or(default_primary_stream); - let stage0_packet = - packet_crafter::do_entropy_bank_update::craft_stage0( - &latest_stacked_ratchet, - alice_constructor.stage0_alice().ok_or( - NetworkError::InternalError("Alice constructor (2) failed"), - )?, - timestamp, - peer_cid, - security_level, - ); - - to_primary_stream_preferred - .unbounded_send(stage0_packet) - .map_err(|err| NetworkError::Generic(err.to_string()))?; - - if self - .ratchet_update_state - .p2p_updates - .insert(peer_cid, alice_constructor) - .is_some() - { - log::error!(target: "citadel", "Overwrote pre-existing peer kem. Report to developers"); - } + session_cid, + } => (session_cid, peer_cid), - if let Some(ticket) = ticket { - let _ = self - .ratchet_update_state - .current_local_requests - .insert(virtual_target, ticket); - } + _ => { + return Err(NetworkError::InvalidRequest( + "External group functionality not yet implemented", + )) + } + }; - Ok(()) - } + let v_conn = self.get_endpoint_container(target_cid)?; + if v_conn.ratchet_manager.is_rekeying() { + return return_already_in_progress(&self.kernel_tx, ticket, session_cid); + } - None => { - log::trace!(target: "citadel", "Won't perform update b/c concurrent update occurring"); - if let Some(ticket) = ticket { - return_already_in_progress( - &self.kernel_tx, - ticket, - virtual_target.get_session_cid(), - ) - } else { - Ok(()) - } - } + // Insert into map + let index = ReKeyIndex { ticket, target_cid }; + + if self.triggered_rekeys.lock().insert(index, ticket).is_some() { + return return_already_in_progress(&self.kernel_tx, ticket, session_cid); + } + + let to_kernel = self.kernel_tx.clone(); + + let ratchet_manager = v_conn.ratchet_manager.clone(); + let task = async move { + if let Err(err) = ratchet_manager.trigger_rekey().await { + if let Err(err) = to_kernel.unbounded_send(NodeResult::ReKeyResult(ReKeyResult { + ticket, + status: ReKeyReturnType::Failure { err }, + session_cid, + })) { + log::error!(target: "citadel", "Unable to send ReKeyResult to kernel: {err}"); } } + }; - _ => Err(NetworkError::InternalError("HyperWAN Not implemented")), - } + spawn!(task); + + Ok(()) } pub(crate) fn process_outbound_broadcast_command( @@ -2366,8 +1916,8 @@ impl StateContainerInner { return Ok(()); } - let stacked_ratchet = self - .get_c2s_crypto() + let ratchet = self + .get_virtual_connection_crypto(C2S_IDENTITY_CID) .ok_or(NetworkError::InternalError("C2s not loaded"))? .get_ratchet(None) .unwrap(); @@ -2390,10 +1940,10 @@ impl StateContainerInner { | GroupBroadcast::ListGroupsFor { .. } | GroupBroadcast::LeaveRoom { .. } => { packet_crafter::peer_cmd::craft_group_message_packet( - stacked_ratchet, + &ratchet, command, ticket, - C2S_ENCRYPTION_ONLY, + C2S_IDENTITY_CID, timestamp, security_level, ) @@ -2436,7 +1986,9 @@ impl StateContainerInner { let (to_session_tx, to_session_rx) = crate::proto::outbound_sender::channel(MAX_OUTGOING_UNPROCESSED_REQUESTS); - CitadelSession::spawn_message_sender_function(session.clone(), to_session_rx); + let v_conn_type = VirtualConnectionType::LocalGroupServer { session_cid }; + + CitadelSession::spawn_message_sender_function(session.clone(), v_conn_type, to_session_rx); Ok(GroupChannel::new( to_session_tx, @@ -2448,8 +2000,10 @@ impl StateContainerInner { } fn get_primary_stream(&self) -> Option<&OutboundPrimaryStreamSender> { - self.c2s_channel_container - .as_ref() - .map(|r| &r.to_primary_stream) + self.get_virtual_connection(C2S_IDENTITY_CID) + .ok()? + .endpoint_container + .as_ref()? + .get_direct_p2p_primary_stream() } } diff --git a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs index 1491f45b0..d7e2c43bb 100644 --- a/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/peer_kem_state_container.rs @@ -39,7 +39,7 @@ pub struct PeerKemStateContainer { pub(crate) constructor: Option, pub(crate) local_is_initiator: bool, pub(crate) session_security_settings: SessionSecuritySettings, - pub(crate) udp_channel_sender: UdpChannelSender, + pub(crate) udp_channel_sender: UdpChannelSender, pub(crate) session_password: PreSharedKey, } diff --git a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs index ba38650af..c37a81670 100644 --- a/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/preconnect_state_container.rs @@ -46,7 +46,7 @@ pub struct PreConnectState { pub(crate) constructor: Option, pub(crate) ticket: Option, pub(crate) last_packet_time: Option, - pub(crate) udp_channel_oneshot_tx: UdpChannelSender, + pub(crate) udp_channel_oneshot_tx: UdpChannelSender, pub(crate) success: bool, pub(crate) generated_ratchet: Option, } @@ -72,18 +72,18 @@ impl Default for PreConnectState { } } -pub struct UdpChannelSender { - pub tx: Option>, - pub rx: Option>, +pub struct UdpChannelSender { + pub tx: Option>>, + pub rx: Option>>, } -impl UdpChannelSender { +impl UdpChannelSender { pub(crate) fn empty() -> Self { Self { tx: None, rx: None } } } -impl Default for UdpChannelSender { +impl Default for UdpChannelSender { fn default() -> Self { let (tx, rx) = channel(); Self { diff --git a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs index a20597563..ea3605ba5 100644 --- a/citadel_proto/src/proto/state_subcontainers/register_state_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/register_state_container.rs @@ -39,7 +39,7 @@ use citadel_crypt::ratchets::Ratchet; pub struct RegisterState { pub(crate) last_stage: u8, pub(crate) constructor: Option, - pub(crate) created_stacked_ratchet: Option, + pub(crate) created_ratchet: Option, pub(crate) last_packet_time: Option, pub(crate) passwordless: Option, } @@ -49,7 +49,7 @@ impl Default for RegisterState { Self { last_stage: 0, constructor: None, - created_stacked_ratchet: None, + created_ratchet: None, last_packet_time: None, passwordless: None, } diff --git a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs index 2be879daf..84d93b9df 100644 --- a/citadel_proto/src/proto/state_subcontainers/rekey_container.rs +++ b/citadel_proto/src/proto/state_subcontainers/rekey_container.rs @@ -8,7 +8,7 @@ //! - Supports peer-to-peer key updates //! - Handles local rekey requests and notifications //! - Provides configurable security levels -//! - Implements adaptive update frequency based on security needs +//! - Calculates update frequency based on security level //! //! ## Important Notes //! - Security levels range from 0 (low) to 4 (extreme) @@ -18,7 +18,7 @@ //! - Completion status is reported for local requests //! //! ## Related Components -//! - `stacked_ratchet`: Core cryptographic ratchet implementation +//! - `ratchet`: Core cryptographic ratchet implementation //! - `session`: Uses rekey state for session security //! - `peer`: Manages peer-to-peer rekey operations //! - `kernel`: Receives rekey completion notifications @@ -29,51 +29,7 @@ use crate::constants::{ REKEY_UPDATE_FREQUENCY_EXTREME, REKEY_UPDATE_FREQUENCY_HIGH, REKEY_UPDATE_FREQUENCY_REINFORCED, REKEY_UPDATE_FREQUENCY_STANDARD, REKEY_UPDATE_FREQUENCY_ULTRA, }; -use crate::error::NetworkError; -use crate::prelude::{NodeResult, ReKeyResult, ReKeyReturnType, Ticket, VirtualTargetType}; -use crate::proto::outbound_sender::UnboundedSender; use crate::proto::transfer_stats::TransferStats; -use citadel_crypt::ratchets::Ratchet; -use std::collections::HashMap; - -pub struct RatchetUpdateState { - pub alice_stacked_ratchet: Option, - pub p2p_updates: HashMap, - // if this is present (in the case of manual mode), an alert will be sent - // to the kernel once the re-key has finished - pub current_local_requests: HashMap, -} - -impl Default for RatchetUpdateState { - fn default() -> Self { - Self { - alice_stacked_ratchet: None, - p2p_updates: HashMap::new(), - current_local_requests: HashMap::new(), - } - } -} - -impl RatchetUpdateState { - pub(crate) fn on_complete( - &mut self, - v_conn_type: VirtualTargetType, - to_kernel_tx: &UnboundedSender, - status: ReKeyReturnType, - ) -> Result<(), NetworkError> { - if let Some(ticket) = self.current_local_requests.remove(&v_conn_type) { - to_kernel_tx - .unbounded_send(NodeResult::ReKeyResult(ReKeyResult { - ticket, - status, - session_cid: v_conn_type.get_session_cid(), - })) - .map_err(|err| NetworkError::Generic(err.to_string())) - } else { - Ok(()) - } - } -} /// Calculates the frequency, in nanoseconds per update pub fn calculate_update_frequency(security_level: u8, _transfer_stats: &TransferStats) -> Duration { diff --git a/citadel_proto/src/proto/validation.rs b/citadel_proto/src/proto/validation.rs index 83bb68398..f831e07cc 100644 --- a/citadel_proto/src/proto/validation.rs +++ b/citadel_proto/src/proto/validation.rs @@ -63,18 +63,16 @@ pub(crate) mod do_connect { } pub(crate) mod group { - use byteorder::{BigEndian, ReadBytesExt}; use std::ops::RangeInclusive; - use bytes::{Buf, Bytes, BytesMut}; + use bytes::{Bytes, BytesMut}; use citadel_crypt::scramble::crypt_splitter::GroupReceiverConfig; - use crate::proto::packet_crafter::SecureProtocolPacket; + use crate::proto::session::UserMessage; use crate::proto::state_container::VirtualTargetType; - use citadel_crypt::endpoint_crypto_container::{EndpointRatchetConstructor, KemTransferStatus}; + use citadel_crypt::ratchets::ratchet_manager::RatchetMessage; use citadel_crypt::ratchets::Ratchet; - use citadel_types::crypto::SecBuffer; use citadel_types::crypto::SecurityLevel; use citadel_types::proto::ObjectId; use citadel_user::serialization::SyncIO; @@ -82,12 +80,12 @@ pub(crate) mod group { /// First-pass validation. Ensures header integrity through AAD-services in AES-GCM pub(crate) fn validate<'a, 'b: 'a, R: Ratchet>( - stacked_ratchet: &R, + ratchet: &R, security_level: SecurityLevel, header: &'b [u8], mut payload: BytesMut, ) -> Option { - stacked_ratchet + ratchet .validate_message_packet_in_place_split(Some(security_level), header, &mut payload) .ok()?; Some(payload.freeze()) @@ -96,53 +94,29 @@ pub(crate) mod group { #[derive(Serialize, Deserialize)] pub(crate) enum GroupHeader { Standard(GroupReceiverConfig, VirtualTargetType), - //FastMessage(SecBuffer, VirtualTargetType, #[serde(borrow)] Option>) + Ratchet(RatchetMessage, ObjectId), } pub(crate) fn validate_header(payload: &BytesMut) -> Option { let mut group_header = GroupHeader::deserialize_from_vector(payload).ok()?; - match &mut group_header { - GroupHeader::Standard(group_receiver_config, _) => { - if group_receiver_config.plaintext_length as usize - > citadel_user::prelude::MAX_BYTES_PER_GROUP - { - log::error!(target: "citadel", "The provided GroupReceiverConfiguration contains an oversized allocation request. Dropping ..."); - return None; - } + if let GroupHeader::Standard(group_receiver_config, _) = &mut group_header { + if group_receiver_config.plaintext_length as usize + > citadel_user::prelude::MAX_BYTES_PER_GROUP + { + log::error!(target: "citadel", "The provided GroupReceiverConfiguration contains an oversized allocation request. Dropping ..."); + return None; } } Some(group_header) } - #[allow(clippy::type_complexity)] - pub(crate) fn validate_message( - payload_orig: &mut BytesMut, - ) -> Option<( - SecBuffer, - Option<>::AliceToBobWireTransfer>, - ObjectId, - )> { - // Safely check that there are 8 bytes in length, then, split at the end - 8 - if payload_orig.len() < std::mem::size_of::() { - return None; - } - let mut payload = - payload_orig.split_to(payload_orig.len() - std::mem::size_of::()); - let object_id = payload_orig.reader().read_u128::().ok()?.into(); - let message = SecureProtocolPacket::extract_message(&mut payload).ok()?; - let deser = SyncIO::deserialize_from_vector(&payload[..]).ok()?; - Some((message.into(), deser, object_id)) - } - #[derive(Serialize, Deserialize)] #[allow(variant_size_differences)] - pub enum GroupHeaderAck { + pub enum GroupHeaderAck { ReadyToReceive { fast_msg: bool, initial_window: Option>, - #[serde(bound = "")] - transfer: KemTransferStatus, object_id: ObjectId, }, NotReady { @@ -152,7 +126,7 @@ pub(crate) mod group { } /// Returns None if the packet is invalid. Returns Some(is_ready_to_accept) if the packet is valid - pub(crate) fn validate_header_ack(payload: &[u8]) -> Option> { + pub(crate) fn validate_header_ack(payload: &[u8]) -> Option { GroupHeaderAck::deserialize_from_vector(payload).ok() } @@ -192,13 +166,12 @@ pub(crate) mod do_register { /// Returns the decrypted username, password, and full name pub(crate) fn validate_stage2( - stacked_ratchet: &R, + ratchet: &R, header: &Ref<&[u8], HdpHeader>, payload: BytesMut, peer_addr: SocketAddr, ) -> Option<(DoRegisterStage2Packet, ConnectionInfo)> { - let (_, plaintext_bytes) = - super::aead::validate_custom(stacked_ratchet, &header.bytes(), payload)?; + let (_, plaintext_bytes) = super::aead::validate_custom(ratchet, &header.bytes(), payload)?; let packet = DoRegisterStage2Packet::deserialize_from_vector(&plaintext_bytes[..]).ok()?; //let proposed_credentials = ProposedCredentials::new_from_hashed(full_name, username, SecVec::new(password.to_vec()), nonce); @@ -208,12 +181,12 @@ pub(crate) mod do_register { /// Returns the decrypted Toolset text, as well as the welcome message pub(crate) fn validate_success( - stacked_ratchet: &R, + ratchet: &R, header: &Ref<&[u8], HdpHeader>, payload: BytesMut, remote_addr: SocketAddr, ) -> Option<(Vec, ConnectionInfo)> { - let (_, payload) = super::aead::validate_custom(stacked_ratchet, &header.bytes(), payload)?; + let (_, payload) = super::aead::validate_custom(ratchet, &header.bytes(), payload)?; let adjacent_addr = ConnectionInfo { addr: remote_addr }; Some((payload.to_vec(), adjacent_addr)) } @@ -228,33 +201,6 @@ pub(crate) mod do_register { } } -pub(crate) mod do_stacked_ratchet_update { - use crate::proto::packet_crafter::do_entropy_bank_update::{ - Stage1UpdatePacket, TruncateAckPacket, TruncatePacket, - }; - use citadel_crypt::endpoint_crypto_container::EndpointRatchetConstructor; - use citadel_crypt::ratchets::Ratchet; - use citadel_user::serialization::SyncIO; - - pub(crate) fn validate_stage0( - payload: &[u8], - ) -> Option<>::AliceToBobWireTransfer> { - SyncIO::deserialize_from_vector(payload).ok() - } - - pub(crate) fn validate_stage1(payload: &[u8]) -> Option> { - Stage1UpdatePacket::deserialize_from_vector(payload as &[u8]).ok() - } - - pub(crate) fn validate_truncate(payload: &[u8]) -> Option { - TruncatePacket::deserialize_from_vector(payload).ok() - } - - pub(crate) fn validate_truncate_ack(payload: &[u8]) -> Option { - TruncateAckPacket::deserialize_from_vector(payload).ok() - } -} - pub(crate) mod pre_connect { use citadel_crypt::endpoint_crypto_container::{ AssociatedSecurityLevel, EndpointRatchetConstructor, @@ -351,17 +297,14 @@ pub(crate) mod pre_connect { let transfer = bob_constructor .stage0_bob() .ok_or(NetworkError::InternalError("Unable to execute stage0_bob"))?; - let new_stacked_ratchet = bob_constructor.finish().ok_or(NetworkError::InternalError( + let new_ratchet = bob_constructor.finish().ok_or(NetworkError::InternalError( "Unable to finish bob constructor", ))?; - let _ = new_stacked_ratchet + let _ = new_ratchet .verify_level(transfer.security_level().into()) .map_err(|err| NetworkError::Generic(err.into_string()))?; // below, we need to ensure the hyper ratchet stays constant throughout transformations - let toolset = Toolset::from(( - static_auxiliary_ratchet.clone(), - new_stacked_ratchet.clone(), - )); + let toolset = Toolset::from((static_auxiliary_ratchet.clone(), new_ratchet.clone())); cnac.on_session_init(toolset); Ok(( @@ -372,7 +315,7 @@ pub(crate) mod pre_connect { udp_mode, kat, nat_type, - new_stacked_ratchet, + new_ratchet, )) } @@ -384,7 +327,7 @@ pub(crate) mod pre_connect { mut alice_constructor: R::Constructor, packet: HdpPacket, ) -> Option<(R, NatType)> { - let static_auxiliary_ratchet = cnac.get_static_auxiliary_stacked_ratchet(); + let static_auxiliary_ratchet = cnac.get_static_auxiliary_ratchet(); let (header, payload, _, _) = packet.decompose(); let (_, payload) = super::aead::validate_custom(&static_auxiliary_ratchet, &header, payload)?; @@ -398,18 +341,18 @@ pub(crate) mod pre_connect { return None; } - let new_stacked_ratchet = alice_constructor.finish()?; - let _ = new_stacked_ratchet.verify_level(lvl.into()).ok()?; - let toolset = Toolset::from((static_auxiliary_ratchet, new_stacked_ratchet.clone())); + let new_ratchet = alice_constructor.finish()?; + let _ = new_ratchet.verify_level(lvl.into()).ok()?; + let toolset = Toolset::from((static_auxiliary_ratchet, new_ratchet.clone())); cnac.on_session_init(toolset); - Some((new_stacked_ratchet, packet.nat_type)) + Some((new_ratchet, packet.nat_type)) } // Returns the adjacent node type, wave ports, and external IP. Serverside, we do not update the CNAC's toolset until this point // because we want to make sure the client passes the challenge - pub fn validate_stage0(stacked_ratchet: &R, packet: HdpPacket) -> Option { + pub fn validate_stage0(ratchet: &R, packet: HdpPacket) -> Option { let (header, payload, _, _) = packet.decompose(); - let (_header, payload) = super::aead::validate_custom(stacked_ratchet, &header, payload)?; + let (_header, payload) = super::aead::validate_custom(ratchet, &header, payload)?; let packet = PreConnectStage0::deserialize_from_vector(&payload).ok()?; Some(packet.node_type) } @@ -504,18 +447,18 @@ pub(crate) mod aead { /// First-pass validation. Ensures header integrity through AAD-services in AES-GCM pub(crate) fn validate_custom<'a, 'b: 'a, H: AsRef<[u8]> + 'b, R: Ratchet>( - stacked_ratchet: &R, + ratchet: &R, header: &'b H, mut payload: BytesMut, ) -> Option<(Ref<&'a [u8], HdpHeader>, BytesMut)> { let header_bytes = header.as_ref(); let header = Ref::new(header_bytes)? as Ref<&[u8], HdpHeader>; - if let Err(err) = stacked_ratchet.validate_message_packet_in_place_split( + if let Err(err) = ratchet.validate_message_packet_in_place_split( Some(header.security_level.into()), header_bytes, &mut payload, ) { - log::error!(target: "citadel", "AES-GCM stage failed: {:?}. Supplied Ratchet Version: {} | Expected Ratchet Version: {} | Header CID: {} | Target CID: {}\nPacket: {:?}\nPayload len: {}", err, stacked_ratchet.version(), header.entropy_bank_version.get(), header.session_cid.get(), header.target_cid.get(), &header, payload.len()); + log::error!(target: "citadel", "AES-GCM stage failed: {:?}. Supplied Ratchet Version: {} | Expected Ratchet Version: {} | Header CID: {} | Target CID: {}\nPacket: {:?}\nPayload len: {}", err, ratchet.version(), header.entropy_bank_version.get(), header.session_cid.get(), header.target_cid.get(), &header, payload.len()); return None; } diff --git a/citadel_proto/tests/connections.rs b/citadel_proto/tests/connections.rs index 05519212a..34358dcfd 100644 --- a/citadel_proto/tests/connections.rs +++ b/citadel_proto/tests/connections.rs @@ -59,7 +59,14 @@ pub mod tests { #[case("127.0.0.1:0")] #[case("[::1]:0")] #[timeout(Duration::from_secs(60))] - #[citadel_io::tokio::test(flavor = "multi_thread")] + #[cfg_attr( + feature = "multi-threaded", + citadel_io::tokio::test(flavor = "multi_thread") + )] + #[cfg_attr( + not(feature = "multi-threaded"), + citadel_io::tokio::test(flavor = "current_thread") + )] async fn test_tcp_or_tls( #[case] addr: SocketAddr, protocols: &Vec, @@ -72,11 +79,18 @@ pub mod tests { return Ok(()); } + if !cfg!(feature = "multi-threaded") { + log::warn!(target: "citadel", "Skipping test since only works on multi-threaded mode"); + return Ok(()); + } + for proto in protocols { log::trace!(target: "citadel", "Testing proto {:?} @ {:?}", &proto, addr); - let res = - Node::::server_create_primary_listen_socket(proto.clone(), addr); + let res = CitadelNode::::server_create_primary_listen_socket( + proto.clone(), + addr, + ); if let Err(err) = res.as_ref() { log::error!(target: "citadel", "Error creating primary socket: {:?}", err); @@ -94,7 +108,7 @@ pub mod tests { let client = async move { let (stream, _) = - Node::::c2s_connect_defaults(None, addr, client_config) + CitadelNode::::c2s_connect_defaults(None, addr, client_config) .await .unwrap(); on_client_received_stream(stream).await @@ -115,7 +129,14 @@ pub mod tests { #[case("127.0.0.1:0")] #[case("[::1]:0")] #[timeout(Duration::from_secs(60))] - #[citadel_io::tokio::test(flavor = "current_thread")] + #[cfg_attr( + feature = "multi-threaded", + citadel_io::tokio::test(flavor = "multi_thread") + )] + #[cfg_attr( + not(feature = "multi-threaded"), + citadel_io::tokio::test(flavor = "current_thread") + )] async fn test_many_proto_conns( #[case] addr: SocketAddr, protocols: &Vec, @@ -123,8 +144,13 @@ pub mod tests { ) -> std::io::Result<()> { citadel_logging::setup_log(); + if !cfg!(feature = "multi-threaded") { + log::trace!(target: "citadel", "Skipping test since only works on multi-threaded mode"); + return Ok(()); + } + if !is_ipv6_enabled() && addr.is_ipv6() { - log::trace!(target: "citadel", "Skipping ipv6 test since ipv6 is not enabled locally"); + log::warn!(target: "citadel", "Skipping ipv6 test since ipv6 is not enabled locally"); return Ok(()); } @@ -133,8 +159,10 @@ pub mod tests { log::trace!(target: "citadel", "Testing proto {:?}", &proto); let cnt = &AtomicUsize::new(0); - let res = - Node::::server_create_primary_listen_socket(proto.clone(), addr); + let res = CitadelNode::::server_create_primary_listen_socket( + proto.clone(), + addr, + ); if let Err(err) = res.as_ref() { log::error!(target: "citadel", "Error creating primary socket w/mode {proto:?}: {err:?}"); @@ -162,9 +190,12 @@ pub mod tests { for _ in 0..count { client.push(async move { - let (stream, _) = - Node::::c2s_connect_defaults(None, addr, client_config) - .await?; + let (stream, _) = CitadelNode::::c2s_connect_defaults( + None, + addr, + client_config, + ) + .await?; on_client_received_stream(stream).await?; let _ = cnt.fetch_add(1, Ordering::SeqCst); Ok(()) @@ -175,6 +206,7 @@ pub mod tests { // if server ends, bad. If client ends, maybe good let res = citadel_io::tokio::select! { res0 = server => { + log::error!(target: "citadel", "Server ended! {res0:?}"); res0 }, res1 = client => { @@ -204,6 +236,7 @@ pub mod tests { sink.send(BytesMut::from(&[100u8] as &[u8]).freeze()) .await?; log::trace!(target: "citadel", "[Server] Sent packet"); + tokio::time::sleep(Duration::from_millis(100)).await; Ok(()) } diff --git a/citadel_sdk/examples/client.rs b/citadel_sdk/examples/client.rs index 2cb11fa30..2a9713016 100644 --- a/citadel_sdk/examples/client.rs +++ b/citadel_sdk/examples/client.rs @@ -26,7 +26,7 @@ async fn main() { let client = citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel::new( server_connection_settings, - |mut connection, remote| async move { + |mut connection| async move { let chan = connection.udp_channel_rx.take(); citadel_io::tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions( UdpMode::Enabled, @@ -35,7 +35,7 @@ async fn main() { .await .map_err(|err| NetworkError::Generic(err.to_string()))?; finished.store(true, Ordering::SeqCst); - remote.shutdown_kernel().await?; + connection.shutdown_kernel().await?; Ok(()) }, ); diff --git a/citadel_sdk/examples/server.rs b/citadel_sdk/examples/server.rs index b83a09805..9c2fb0397 100644 --- a/citadel_sdk/examples/server.rs +++ b/citadel_sdk/examples/server.rs @@ -20,7 +20,7 @@ async fn main() { } else { Box::new( citadel_sdk::prefabs::server::client_connect_listener::ClientConnectListenerKernel::new( - |mut conn, _c2s_remote| async move { + |mut conn| async move { let chan = conn.udp_channel_rx.take(); citadel_io::tokio::task::spawn(citadel_sdk::test_common::udp_mode_assertions( UdpMode::Enabled, diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs index e86b763b0..5fa8cbb05 100644 --- a/citadel_sdk/src/builder/node_builder.rs +++ b/citadel_sdk/src/builder/node_builder.rs @@ -40,6 +40,7 @@ use citadel_proto::prelude::*; use citadel_proto::kernel::KernelExecutorArguments; use citadel_proto::macros::{ContextRequirements, LocalContextRequirements}; use citadel_proto::re_imports::RustlsClientConfig; +use citadel_types::crypto::HeaderObfuscatorSettings; use futures::Future; use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; @@ -60,7 +61,7 @@ pub struct NodeBuilder { client_tls_config: Option, kernel_executor_settings: Option, stun_servers: Option>, - server_session_password: Option, + local_only_server_settings: Option, _ratchet: PhantomData, } @@ -82,7 +83,7 @@ impl Default for NodeBuilder { client_tls_config: None, kernel_executor_settings: None, stun_servers: None, - server_session_password: None, + local_only_server_settings: None, _ratchet: Default::default(), } } @@ -166,7 +167,11 @@ impl NodeBuilder { .map_err(|err| anyhow::Error::msg(err.into_string()))? }; - let server_only_session_password = self.server_session_password.take(); + if matches!(underlying_proto, ServerUnderlyingProtocol::Tcp(..)) { + citadel_logging::warn!(target: "citadel", "⚠️ WARNING ⚠️ TCP is discouraged for production use until The Citadel Protocol has been reviewed. Use TLS automatically by not changing the underlying protocol"); + } + + let server_only_session_init_settings = self.local_only_server_settings.take(); Ok(NodeFuture { _pd: Default::default(), @@ -192,7 +197,7 @@ impl NodeBuilder { client_config, kernel_executor_settings, stun_servers, - server_only_session_password, + server_only_session_init_settings, }; log::trace!(target: "citadel", "[NodeBuilder] Creating KernelExecutor ..."); @@ -205,12 +210,10 @@ impl NodeBuilder { /// Defines the node type. By default, Peer is used. If a server is desired, a bind address is expected /// ``` - /// use std::net::SocketAddr; - /// use std::str::FromStr; /// use citadel_sdk::prelude::DefaultNodeBuilder; /// use citadel_proto::prelude::NodeType; /// - /// DefaultNodeBuilder::default().with_node_type(NodeType::Server(SocketAddr::from_str("0.0.0.0:25021").unwrap())); + /// DefaultNodeBuilder::default().with_node_type(NodeType::server("0.0.0.0:25021").unwrap()); /// ``` pub fn with_node_type(&mut self, node_type: NodeType) -> &mut Self { self.hypernode_type = Some(node_type); @@ -347,7 +350,21 @@ impl NodeBuilder { /// is specified, the client must have the matching pre-shared key in order to /// register and connect with the server. pub fn with_server_password>(&mut self, password: T) -> &mut Self { - self.server_session_password = Some(password.into()); + let mut server_only_settings = self.local_only_server_settings.clone().unwrap_or_default(); + server_only_settings.declared_pre_shared_key = Some(password.into()); + self.local_only_server_settings = Some(server_only_settings); + self + } + + /// Sets the header obfuscator settings for the server + pub fn with_server_declared_header_obfuscation>( + &mut self, + header_obfuscator_settings: T, + ) -> &mut Self { + let mut server_only_settings = self.local_only_server_settings.clone().unwrap_or_default(); + server_only_settings.declared_header_obfuscation_setting = + header_obfuscator_settings.into(); + self.local_only_server_settings = Some(server_only_settings); self } diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs index 5f2b36d68..841e8bb4c 100644 --- a/citadel_sdk/src/fs.rs +++ b/citadel_sdk/src/fs.rs @@ -186,12 +186,12 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |_channel, remote| async move { + |connection| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf"); // write to file to the RE-VFS crate::fs::write_with_security_level( - &remote, + &connection.remote, source_dir.clone(), security_level, &virtual_path, @@ -199,7 +199,7 @@ mod tests { .await?; log::info!(target: "citadel", "***CLIENT FILE TRANSFER SUCCESS***"); // now, pull it - let save_dir = crate::fs::read(&remote, virtual_path).await?; + let save_dir = crate::fs::read(&connection.remote, virtual_path).await?; // now, compare bytes log::info!(target: "citadel", "***CLIENT REVFS PULL SUCCESS"); let original_bytes = citadel_io::tokio::fs::read(&source_dir).await.unwrap(); @@ -207,7 +207,7 @@ mod tests { assert_eq!(original_bytes, revfs_pulled_bytes); log::info!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS"); client_success.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); @@ -259,12 +259,12 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |_channel, remote| async move { + |connection| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf"); // write to file to the RE-VFS crate::fs::write_with_security_level( - &remote, + &connection.remote, source_dir.clone(), security_level, &virtual_path, @@ -272,7 +272,7 @@ mod tests { .await?; log::trace!(target: "citadel", "***CLIENT FILE TRANSFER SUCCESS***"); // now, pull it - let save_dir = crate::fs::take(&remote, &virtual_path).await?; + let save_dir = crate::fs::take(&connection.remote, &virtual_path).await?; // now, compare bytes log::trace!(target: "citadel", "***CLIENT REVFS PULL SUCCESS"); let original_bytes = citadel_io::tokio::fs::read(&source_dir).await.unwrap(); @@ -280,9 +280,11 @@ mod tests { assert_eq!(original_bytes, revfs_pulled_bytes); log::trace!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS"); // prove we can no longer read from this virtual file - assert!(crate::fs::read(&remote, &virtual_path).await.is_err()); + assert!(crate::fs::read(&connection.remote, &virtual_path) + .await + .is_err()); client_success.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); @@ -334,12 +336,12 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |_channel, remote| async move { + |connection| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf"); // write to file to the RE-VFS crate::fs::write_with_security_level( - &remote, + &connection.remote, source_dir.clone(), security_level, &virtual_path, @@ -347,18 +349,20 @@ mod tests { .await?; log::trace!(target: "citadel", "***CLIENT FILE TRANSFER SUCCESS***"); // now, pull it - let save_dir = crate::fs::read(&remote, &virtual_path).await?; + let save_dir = crate::fs::read(&connection.remote, &virtual_path).await?; // now, compare bytes log::trace!(target: "citadel", "***CLIENT REVFS PULL SUCCESS"); let original_bytes = citadel_io::tokio::fs::read(&source_dir).await.unwrap(); let revfs_pulled_bytes = citadel_io::tokio::fs::read(&save_dir).await.unwrap(); assert_eq!(original_bytes, revfs_pulled_bytes); log::trace!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS"); - crate::fs::delete(&remote, &virtual_path).await?; + crate::fs::delete(&connection.remote, &virtual_path).await?; // prove we can no longer read from this virtual file since it was just deleted - assert!(crate::fs::read(&remote, &virtual_path).await.is_err()); + assert!(crate::fs::read(&connection.remote, &virtual_path) + .await + .is_err()); client_success.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); diff --git a/citadel_sdk/src/macros.rs b/citadel_sdk/src/macros.rs index 92fb3de91..702ab4831 100644 --- a/citadel_sdk/src/macros.rs +++ b/citadel_sdk/src/macros.rs @@ -50,7 +50,7 @@ macro_rules! impl_remote { &self, request: NodeRequest, ) -> Result< - citadel_proto::kernel::kernel_communicator::KernelStreamSubscription, + citadel_proto::kernel::kernel_communicator::KernelStreamSubscription, NetworkError, > { self.inner.send_callback_subscription(request).await diff --git a/citadel_sdk/src/prefabs/client/broadcast.rs b/citadel_sdk/src/prefabs/client/broadcast.rs index edb216d48..5e088ead2 100644 --- a/citadel_sdk/src/prefabs/client/broadcast.rs +++ b/citadel_sdk/src/prefabs/client/broadcast.rs @@ -60,7 +60,6 @@ //! - [`PrefabFunctions`]: Base prefab functionality //! -use crate::prefabs::ClientServerRemote; use crate::prelude::*; use crate::test_common::wait_for_peers; use citadel_io::tokio::sync::Mutex; @@ -127,7 +126,7 @@ pub enum GroupInitRequestType { impl<'a, F, Fut, R: Ratchet> PrefabFunctions<'a, GroupInitRequestType, R> for BroadcastKernel<'a, F, Fut, R> where - F: FnOnce(GroupChannel, ClientServerRemote) -> Fut + Send + 'a, + F: FnOnce(GroupChannel, CitadelClientServerConnection) -> Fut + Send + 'a, Fut: Future> + Send + 'a, { type UserLevelInputFunction = F; @@ -143,8 +142,7 @@ where tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err(Debug)) )] async fn on_c2s_channel_received( - connect_success: ConnectionSuccess, - remote: ClientServerRemote, + connect_success: CitadelClientServerConnection, arg: GroupInitRequestType, fx: Self::UserLevelInputFunction, shared: Arc, @@ -167,7 +165,7 @@ where for peer in &invite_list { let peer = peer - .search_peer(session_cid, remote.inner.account_manager()) + .search_peer(session_cid, connect_success.account_manager()) .await? .ok_or_else(|| { NetworkError::msg(format!( @@ -199,20 +197,19 @@ where // ensure local is registered to owner let owner_orig = owner; let owner_find = owner_orig - .search_peer(session_cid, remote.inner.account_manager()) + .search_peer(session_cid, connect_success.account_manager()) .await?; let owner = if let Some(owner) = owner_find { Some(owner) } else if do_peer_register { - let handle = remote - .inner + let handle = connect_success .propose_target(local_user.clone(), owner_orig.clone()) .await?; let _ = handle.register_to_peer().await?; // wait_for_peers().await; owner_orig - .search_peer(session_cid, remote.inner.account_manager()) + .search_peer(session_cid, connect_success.account_manager()) .await? } else { None @@ -232,8 +229,9 @@ where // Exponential backoff, waiting for owner to create group let mut retries = 0; - let group_owner_handle = - remote.propose_target(local_user.clone(), owner.cid).await?; + let group_owner_handle = connect_success + .propose_target(local_user.clone(), owner.cid) + .await?; loop { let owned_groups = group_owner_handle.list_owned_groups().await?; if owned_groups.contains(&expected_message_group_key) { @@ -267,14 +265,14 @@ where }); let subscription = &Mutex::new(Some( - remote.inner.send_callback_subscription(request).await?, + connect_success.send_callback_subscription(request).await?, )); log::trace!(target: "citadel", "Peer {session_cid} is attempting to join group"); let acceptor_task = if creator_only_accept_inbound_registers { shared.route_registers.store(true, Ordering::Relaxed); let mut reg_rx = shared.register_rx.lock().take().unwrap(); - let remote = remote.inner.clone(); + let remote = connect_success.remote_ref().clone(); Box::pin(async move { let mut subscription = subscription.lock().await.take().unwrap(); // Merge the reg_rx stream and the subscription stream @@ -364,9 +362,10 @@ where // Drop the lock to allow the acceptor task to gain access to the subscription drop(lock); return if is_owner { - citadel_io::tokio::try_join!(fx(channel, remote), acceptor_task).map(|_| ()) + citadel_io::tokio::try_join!(fx(channel, connect_success), acceptor_task) + .map(|_| ()) } else { - fx(channel, remote).await.map(|_| ()) + fx(channel, connect_success).await.map(|_| ()) }; } @@ -411,7 +410,7 @@ impl NetKernel for BroadcastKernel<'_, F, Fut, R> { self.inner_kernel.on_start().await } - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { if let NodeResult::PeerEvent(PeerEvent { event: ps @ PeerSignal::PostRegister { .. }, ticket: _, @@ -493,11 +492,11 @@ mod tests { let client_kernel = BroadcastKernel::new( server_connection_settings, request, - move |channel, remote| async move { + move |channel, connection| async move { wait_for_peers().await; - log::trace!(target: "citadel", "***GROUP PEER {}={}={} CONNECT SUCCESS***", idx, uuid, remote.conn_type.get_session_cid()); + log::trace!(target: "citadel", "***GROUP PEER {}={}={} CONNECT SUCCESS***", idx, uuid, connection.conn_type.get_session_cid()); - let owned_groups = remote.list_owned_groups().await.unwrap(); + let owned_groups = connection.list_owned_groups().await.unwrap(); if idx == 0 { assert_eq!(owned_groups.len(), 1); @@ -505,12 +504,12 @@ mod tests { assert_eq!(owned_groups.len(), 0); } - log::trace!(target: "citadel", "Peer {idx}={} is COMPLETE!", remote.conn_type.get_session_cid()); + log::trace!(target: "citadel", "Peer {idx}={} is COMPLETE!", connection.conn_type.get_session_cid()); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; drop(channel); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); diff --git a/citadel_sdk/src/prefabs/client/mod.rs b/citadel_sdk/src/prefabs/client/mod.rs index d2ba3aa5c..a934bbbb9 100644 --- a/citadel_sdk/src/prefabs/client/mod.rs +++ b/citadel_sdk/src/prefabs/client/mod.rs @@ -41,7 +41,6 @@ //! use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; -use crate::prefabs::ClientServerRemote; use crate::prelude::*; use std::marker::PhantomData; use std::net::{SocketAddr, ToSocketAddrs}; @@ -63,8 +62,7 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a, R: Ratchet>: Sized + 'a { fn get_shared_bundle(&self) -> Self::SharedBundle; async fn on_c2s_channel_received( - connect_success: ConnectionSuccess, - remote: ClientServerRemote, + connect_success: CitadelClientServerConnection, arg: Arg, fx: Self::UserLevelInputFunction, shared: Self::SharedBundle, @@ -81,14 +79,8 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a, R: Ratchet>: Sized + 'a { let (tx, rx) = citadel_io::tokio::sync::oneshot::channel(); let server_conn_kernel = SingleClientServerConnectionKernel::<_, _, R>::new( server_connection_settings, - |connect_success, remote| { - on_channel_received_fn::<_, Self, R>( - connect_success, - remote, - rx, - arg, - on_channel_received, - ) + |connect_success| { + on_channel_received_fn::<_, Self, R>(connect_success, rx, arg, on_channel_received) }, ); @@ -99,8 +91,7 @@ pub trait PrefabFunctions<'a, Arg: Send + 'a, R: Ratchet>: Sized + 'a { } async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg, R>, R: Ratchet>( - connect_success: ConnectionSuccess, - remote: ClientServerRemote, + connect_success: CitadelClientServerConnection, rx_bundle: citadel_io::tokio::sync::oneshot::Receiver, arg: Arg, on_channel_received: T::UserLevelInputFunction, @@ -108,7 +99,7 @@ async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg, let shared = rx_bundle .await .map_err(|err| NetworkError::Generic(err.to_string()))?; - T::on_c2s_channel_received(connect_success, remote, arg, on_channel_received, shared).await + T::on_c2s_channel_received(connect_success, arg, on_channel_received, shared).await } /// Used to instantiate a client to server connection diff --git a/citadel_sdk/src/prefabs/client/peer_connection.rs b/citadel_sdk/src/prefabs/client/peer_connection.rs index 11948299b..6fb22cca7 100644 --- a/citadel_sdk/src/prefabs/client/peer_connection.rs +++ b/citadel_sdk/src/prefabs/client/peer_connection.rs @@ -60,7 +60,6 @@ //! - [`SessionSecuritySettings`]: Connection security //! -use crate::prefabs::ClientServerRemote; use crate::prelude::results::PeerConnectSuccess; use crate::prelude::*; use crate::test_common::wait_for_peers; @@ -155,7 +154,7 @@ impl NetKernel for PeerConnectionKernel<'_, F, Fut, R> { } #[allow(clippy::collapsible_else_if)] - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { match message { NodeResult::ObjectTransferHandle(ObjectTransferHandle { ticket: _, @@ -406,7 +405,10 @@ impl From for PeerConnectionSetupAggregator { impl<'a, F, Fut, T: Into + Send + 'a, R: Ratchet> PrefabFunctions<'a, T, R> for PeerConnectionKernel<'a, F, Fut, R> where - F: FnOnce(Receiver, NetworkError>>, ClientServerRemote) -> Fut + F: FnOnce( + Receiver, NetworkError>>, + CitadelClientServerConnection, + ) -> Fut + Send + 'a, Fut: Future> + Send + 'a, @@ -424,8 +426,7 @@ where tracing::instrument(level = "trace", target = "citadel", skip_all, ret, err(Debug)) )] async fn on_c2s_channel_received( - connect_success: ConnectionSuccess, - cls_remote: ClientServerRemote, + connect_success: CitadelClientServerConnection, peers_to_connect: T, f: Self::UserLevelInputFunction, shared: Shared, @@ -441,12 +442,12 @@ where // TODO: optimize this into a single concurrent operation peers_already_registered.push( peer.id - .search_peer(session_cid, cls_remote.inner.account_manager()) + .search_peer(session_cid, connect_success.account_manager()) .await?, ) } - let remote = cls_remote.inner.clone(); + let remote = connect_success.clone(); let (ref tx, rx) = citadel_io::tokio::sync::mpsc::channel(peers_to_connect.len()); let requests = FuturesUnordered::new(); @@ -531,7 +532,7 @@ where // TODO: What should be done if a peer conn fails? No room for error here let collection_task = async move { requests.try_collect::<()>().await }; - citadel_io::tokio::try_join!(collection_task, f(rx, cls_remote)).map(|_| ()) + citadel_io::tokio::try_join!(collection_task, f(rx, connect_success)).map(|_| ()) } fn construct(kernel: Box + 'a>) -> Self { @@ -625,9 +626,9 @@ mod tests { let client_kernel = PeerConnectionKernel::new( server_connection_settings, agg.clone(), - move |results, mut remote| async move { + move |results, connection| async move { log::info!(target: "citadel", "***PEER {username} CONNECTED ***"); - let session_cid = remote.conn_type.get_session_cid(); + let session_cid = connection.conn_type.get_session_cid(); let check = move |conn: PeerConnectSuccess<_>| async move { let session_cid = conn.channel.get_session_cid(); let _mutual_peers = conn @@ -653,16 +654,15 @@ mod tests { // By now, all the network peers have been registered to. // Test that getting the peers (not necessarily mutual) // show up - let network_peers = remote.get_peers(None).await.unwrap(); + let network_peers = connection.get_peers(None).await.unwrap(); for user in agg.inner { let peer_cid = user.id.get_cid(); assert!(network_peers.iter().any(|r| r.cid == peer_cid)) } // test to make sure the mutuals are valid - let session_cid = remote.conn_type.get_session_cid(); - let mutual_peers = remote - .inner + let session_cid = connection.conn_type.get_session_cid(); + let mutual_peers = connection .get_local_group_mutual_peers(session_cid) .await .unwrap(); @@ -673,7 +673,7 @@ mod tests { log::info!(target: "citadel", "***PEER {username} finished all checks***"); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); @@ -689,12 +689,15 @@ mod tests { } #[rstest] - #[case(2)] - #[case(3)] + #[case(2, HeaderObfuscatorSettings::default())] + #[case(2, HeaderObfuscatorSettings::Enabled)] + #[case(2, HeaderObfuscatorSettings::EnabledWithKey(12345))] + #[case(3, HeaderObfuscatorSettings::default())] #[timeout(Duration::from_secs(90))] #[tokio::test(flavor = "multi_thread")] async fn peer_to_peer_connect_transient( #[case] peer_count: usize, + #[case] header_obfuscator_settings: HeaderObfuscatorSettings, ) -> Result<(), Box> { assert!(peer_count > 1); citadel_logging::setup_log(); @@ -723,11 +726,13 @@ mod tests { let mut agg = PeerConnectionSetupAggregator::default(); for peer in peers { + let mut security_settings = SessionSecuritySettings::default(); + security_settings.header_obfuscator_settings = header_obfuscator_settings; agg = agg .with_peer_custom(peer) .with_udp_mode(udp_mode) .ensure_registered() - .with_session_security_settings(SessionSecuritySettings::default()) + .with_session_security_settings(security_settings) .add(); } @@ -1021,7 +1026,8 @@ mod tests { conn }; - let _ = handle_peer_connect_successes( + + let results = handle_peer_connect_successes( results, session_cid, peer_count, @@ -1030,14 +1036,14 @@ mod tests { ) .await; - log::info!(target: "citadel", "***PEER {uuid} finished all checks***"); + log::info!(target: "citadel", "***PEER {uuid} finished all check (count: {})s***", results.len()); let _ = client_success.fetch_add(1, Ordering::Relaxed); wait_for_peers().await; remote.shutdown_kernel().await }, ); - let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel)?; client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -1104,6 +1110,7 @@ mod tests { agg, move |results, remote| async move { log::info!(target: "citadel", "***PEER {uuid} CONNECTED***"); + wait_for_peers().await; let session_cid = remote.conn_type.get_session_cid(); let check = move |conn: PeerConnectSuccess<_>| async move { @@ -1129,7 +1136,7 @@ mod tests { }, ); - let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); + let client = DefaultNodeBuilder::default().build(client_kernel)?; client_kernels.push(async move { client.await.map(|_| ()) }); } @@ -1264,7 +1271,7 @@ mod tests { checks: F, ) -> Vec> where - F: for<'a> Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, + F: Fn(PeerConnectSuccess) -> Fut + Send + Clone + 'static, Fut: Future> + Send, { let (finished_tx, finished_rx) = tokio::sync::oneshot::channel(); @@ -1279,6 +1286,8 @@ mod tests { } } + log::info!(target: "citadel", "~~~*** Peer {session_cid} has {} connections to other peers ***~~~", conns.len()); + for conn in conns { let conn = conn.expect("Error receiving peer connection"); handle_peer_connect_success( @@ -1324,8 +1333,9 @@ mod tests { Fut: Future> + Send, { let task = async move { + let chan = conn.udp_channel_rx.take(); crate::test_common::p2p_assertions(session_cid, &conn).await; - crate::test_common::udp_mode_assertions(udp_mode, conn.udp_channel_rx.take()).await; + crate::test_common::udp_mode_assertions(udp_mode, chan).await; let conn = checks(conn).await; done_tx .send(conn) diff --git a/citadel_sdk/src/prefabs/client/single_connection.rs b/citadel_sdk/src/prefabs/client/single_connection.rs index 68cf819e7..d4cd24f6c 100644 --- a/citadel_sdk/src/prefabs/client/single_connection.rs +++ b/citadel_sdk/src/prefabs/client/single_connection.rs @@ -47,13 +47,13 @@ //! - [`NetKernel`]: Base trait for network kernels //! - [`ServerConnectionSettings`]: Connection configuration //! - [`ClientServerRemote`]: Remote connection handler -//! - [`ConnectionSuccess`]: Connection establishment data +//! - [`CitadelClientServerConnection`]: Connection establishment data //! use crate::prefabs::client::peer_connection::FileTransferHandleRx; use crate::prefabs::client::ServerConnectionSettings; use crate::prefabs::ClientServerRemote; -use crate::remote_ext::ConnectionSuccess; +use crate::remote_ext::CitadelClientServerConnection; use crate::remote_ext::ProtocolRemoteExt; use citadel_io::Mutex; use citadel_proto::prelude::*; @@ -71,7 +71,7 @@ pub struct SingleClientServerConnectionKernel { auth_info: Mutex>, session_security_settings: SessionSecuritySettings, unprocessed_signal_filter_tx: - Mutex>>, + Mutex>>>, remote: Option>, server_password: Option, rx_incoming_object_transfer_handle: Mutex>, @@ -101,7 +101,7 @@ pub(crate) enum ConnectionType { impl SingleClientServerConnectionKernel where - F: FnOnce(ConnectionSuccess, ClientServerRemote) -> Fut + Send, + F: FnOnce(CitadelClientServerConnection) -> Fut + Send, Fut: Future> + Send, { fn generate_object_transfer_handle() -> ( @@ -171,7 +171,7 @@ where #[async_trait] impl NetKernel for SingleClientServerConnectionKernel where - F: FnOnce(ConnectionSuccess, ClientServerRemote) -> Fut + Send, + F: FnOnce(CitadelClientServerConnection) -> Fut + Send, Fut: Future> + Send, { fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { @@ -231,7 +231,7 @@ where } }; - let connect_success = remote + let mut connect_success = remote .connect( auth, Default::default(), @@ -241,6 +241,7 @@ where self.server_password.clone(), ) .await?; + let conn_type = VirtualTargetType::LocalGroupServer { session_cid: connect_success.cid, }; @@ -254,21 +255,18 @@ where let (reroute_tx, reroute_rx) = citadel_io::tokio::sync::mpsc::unbounded_channel(); *self.unprocessed_signal_filter_tx.lock() = Some(reroute_tx); + connect_success.remote = ClientServerRemote::new( + conn_type, + remote, + session_security_settings, + Some(reroute_rx), + Some(handle), + ); - handler( - connect_success, - ClientServerRemote::new( - conn_type, - remote, - session_security_settings, - Some(reroute_rx), - Some(handle), - ), - ) - .await + handler(connect_success).await } - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { match message { NodeResult::ObjectTransferHandle(handle) => { if let Err(err) = self.tx_incoming_object_transfer_handle.send(handle.handle) { @@ -298,7 +296,6 @@ where mod tests { use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel; use crate::prefabs::client::DefaultServerConnectionSettingsBuilder; - use crate::prefabs::ClientServerRemote; use crate::prelude::*; use crate::test_common::{server_info_reactive, wait_for_peers, TestBarrier}; use citadel_io::tokio; @@ -310,11 +307,11 @@ mod tests { feature = "localhost-testing", tracing::instrument(level = "trace", target = "citadel", skip_all, err(Debug)) )] - async fn on_server_received_conn( + async fn on_server_received_conn( udp_mode: UdpMode, - conn: ConnectionSuccess, + conn: &mut CitadelClientServerConnection, ) -> Result<(), NetworkError> { - crate::test_common::udp_mode_assertions(udp_mode, conn.udp_channel_rx).await; + crate::test_common::udp_mode_assertions(udp_mode, conn.udp_channel_rx.take()).await; Ok(()) } @@ -324,15 +321,15 @@ mod tests { )] async fn default_server_harness( udp_mode: UdpMode, - conn: ConnectionSuccess, - remote: ClientServerRemote, + mut connection: CitadelClientServerConnection, server_success: &AtomicBool, ) -> Result<(), NetworkError> { wait_for_peers().await; - on_server_received_conn(udp_mode, conn).await?; + on_server_received_conn(udp_mode, &mut connection).await?; server_success.store(true, Ordering::SeqCst); + log::warn!(target: "citadel", "Server awaiting peer ..."); wait_for_peers().await; - remote.shutdown_kernel().await + connection.shutdown_kernel().await } #[rstest] @@ -357,8 +354,8 @@ mod tests { let server_success = &AtomicBool::new(false); let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( - move |conn, remote| async move { - default_server_harness(udp_mode, conn, remote, server_success).await + move |connection| async move { + default_server_harness(udp_mode, connection, server_success).await }, |builder| { let _ = builder.with_underlying_protocol(underlying_protocol); @@ -375,17 +372,16 @@ mod tests { .build() .unwrap(); - let client_kernel = SingleClientServerConnectionKernel::new( - client_settings, - |channel, remote| async move { + let client_kernel = + SingleClientServerConnectionKernel::new(client_settings, |mut connection| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); + let chan = connection.udp_channel_rx.take(); wait_for_peers().await; - crate::test_common::udp_mode_assertions(udp_mode, channel.udp_channel_rx).await; + crate::test_common::udp_mode_assertions(udp_mode, chan).await; client_success.store(true, Ordering::Relaxed); wait_for_peers().await; - remote.shutdown_kernel().await - }, - ); + connection.shutdown_kernel().await + }); let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); @@ -398,13 +394,24 @@ mod tests { } #[rstest] - #[case(UdpMode::Enabled, None)] - #[case(UdpMode::Enabled, Some("test-password"))] + #[case(UdpMode::Enabled, None, HeaderObfuscatorSettings::Disabled)] + #[case(UdpMode::Enabled, None, HeaderObfuscatorSettings::Enabled)] + #[case( + UdpMode::Enabled, + None, + HeaderObfuscatorSettings::EnabledWithKey(12345) + )] + #[case( + UdpMode::Enabled, + Some("test-password"), + HeaderObfuscatorSettings::Disabled + )] #[timeout(std::time::Duration::from_secs(90))] #[citadel_io::tokio::test(flavor = "multi_thread")] async fn test_single_connection_transient( #[case] udp_mode: UdpMode, #[case] server_password: Option<&'static str>, + #[case] header_obfuscator_settings: HeaderObfuscatorSettings, ) { citadel_logging::setup_log(); TestBarrier::setup(2); @@ -412,12 +419,14 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( - |conn, remote| async move { - default_server_harness(udp_mode, conn, remote, server_success).await + |connection| async move { + default_server_harness(udp_mode, connection, server_success).await }, |opts| { if let Some(password) = server_password { - let _ = opts.with_server_password(password); + let _ = opts + .with_server_password(password) + .with_server_declared_header_obfuscation(header_obfuscator_settings); } }, ); @@ -426,7 +435,13 @@ mod tests { let mut server_connection_settings = DefaultServerConnectionSettingsBuilder::transient_with_id(server_addr, uuid) - .with_udp_mode(udp_mode); + .with_udp_mode(udp_mode) + .with_session_security_settings(SessionSecuritySettings { + security_level: Default::default(), + secrecy_mode: Default::default(), + crypto_params: Default::default(), + header_obfuscator_settings, + }); if let Some(server_password) = server_password { server_connection_settings = @@ -437,14 +452,15 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |channel, remote| async move { + |mut connection| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); + let chan = connection.udp_channel_rx.take(); wait_for_peers().await; - crate::test_common::udp_mode_assertions(udp_mode, channel.udp_channel_rx).await; - remote.disconnect().await?; + crate::test_common::udp_mode_assertions(udp_mode, chan).await; + connection.disconnect().await?; client_success.store(true, Ordering::Relaxed); wait_for_peers().await; - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); @@ -470,7 +486,7 @@ mod tests { TestBarrier::setup(2); let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( - |_conn, _remote| async move { panic!("Server should not have connected") }, + |_conn| async move { panic!("Server should not have connected") }, |opts| { if let Some(password) = server_password { let _ = opts.with_server_password(password); @@ -489,7 +505,7 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |_channel, _remote| async move { panic!("Client should not have connected") }, + |_connection| async move { panic!("Client should not have connected") }, ); let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); @@ -521,8 +537,8 @@ mod tests { let server_success = &AtomicBool::new(false); let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( - |conn, remote| async move { - default_server_harness(udp_mode, conn, remote, server_success).await + |connection| async move { + default_server_harness(udp_mode, connection, server_success).await }, |_| (), ); @@ -537,14 +553,15 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |channel, remote| async move { + |mut connection| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); + let chan = connection.udp_channel_rx.take(); wait_for_peers().await; - crate::test_common::udp_mode_assertions(udp_mode, channel.udp_channel_rx).await; - remote.deregister().await?; + crate::test_common::udp_mode_assertions(udp_mode, chan).await; + connection.deregister().await?; client_success.store(true, Ordering::Relaxed); wait_for_peers().await; - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); @@ -570,8 +587,8 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( - |conn, remote| async move { - default_server_harness(udp_mode, conn, remote, server_success).await + |connection| async move { + default_server_harness(udp_mode, connection, server_success).await }, |_| (), ); @@ -586,7 +603,7 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |_channel, remote| async move { + |connection| async move { log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); wait_for_peers().await; @@ -595,33 +612,39 @@ mod tests { let value: Vec = Vec::from("Hello, world!"); let value2: Vec = Vec::from("Hello, world!2"); - assert_eq!(remote.set(KEY, value.clone()).await?.as_deref(), None); - assert_eq!(remote.get(KEY).await?.as_deref(), Some(value.as_slice())); + assert_eq!(connection.set(KEY, value.clone()).await?.as_deref(), None); + assert_eq!( + connection.get(KEY).await?.as_deref(), + Some(value.as_slice()) + ); - assert_eq!(remote.set(KEY2, value2.clone()).await?.as_deref(), None); - assert_eq!(remote.get(KEY2).await?.as_deref(), Some(value2.as_slice())); + assert_eq!(connection.set(KEY2, value2.clone()).await?.as_deref(), None); + assert_eq!( + connection.get(KEY2).await?.as_deref(), + Some(value2.as_slice()) + ); - let map = remote.get_all().await?; + let map = connection.get_all().await?; assert_eq!(map.get(KEY), Some(&value)); assert_eq!(map.get(KEY2), Some(&value2)); assert_eq!( - remote.remove(KEY2).await?.as_deref(), + connection.remove(KEY2).await?.as_deref(), Some(value2.as_slice()) ); - assert_eq!(remote.remove(KEY2).await?.as_deref(), None); + assert_eq!(connection.remove(KEY2).await?.as_deref(), None); - let map = remote.remove_all().await?; + let map = connection.remove_all().await?; assert_eq!(map.get(KEY), Some(&value)); assert_eq!(map.get(KEY2), None); - assert_eq!(remote.get_all().await?.len(), 0); - assert_eq!(remote.remove_all().await?.len(), 0); + assert_eq!(connection.get_all().await?.len(), 0); + assert_eq!(connection.remove_all().await?.len(), 0); client_success.store(true, Ordering::Relaxed); wait_for_peers().await; - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); @@ -647,8 +670,8 @@ mod tests { let client_success = &AtomicBool::new(false); let server_success = &AtomicBool::new(false); let (server, server_addr) = server_info_reactive::<_, _, StackedRatchet>( - |conn, remote| async move { - default_server_harness(udp_mode, conn, remote, server_success).await + |connection| async move { + default_server_harness(udp_mode, connection, server_success).await }, |_| (), ); @@ -663,23 +686,24 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |_channel, remote| async move { - log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); + |mut connection| async move { + log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS***"); wait_for_peers().await; + let chan = connection.udp_channel_rx.take(); + crate::test_common::udp_mode_assertions(udp_mode, chan).await; for x in 1..10 { - assert_eq!(remote.rekey().await?, Some(x)); + assert_eq!(connection.remote.rekey().await?, Some(x)); } client_success.store(true, Ordering::Relaxed); wait_for_peers().await; - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); let client = DefaultNodeBuilder::default().build(client_kernel).unwrap(); - let joined = futures::future::try_join(server, client); let _ = joined.await.unwrap(); diff --git a/citadel_sdk/src/prefabs/mod.rs b/citadel_sdk/src/prefabs/mod.rs index e330dbcbb..5ac63262e 100644 --- a/citadel_sdk/src/prefabs/mod.rs +++ b/citadel_sdk/src/prefabs/mod.rs @@ -50,6 +50,7 @@ use citadel_io::tokio::sync::mpsc::UnboundedReceiver; use citadel_io::Mutex; use citadel_proto::prelude::*; use std::net::{SocketAddr, ToSocketAddrs}; +use std::ops::{Deref, DerefMut}; use std::sync::Arc; /// Kernels for clients @@ -67,21 +68,34 @@ use crate::remote_ext::ProtocolRemoteExt; #[derive(Clone)] pub struct ClientServerRemote { pub(crate) inner: NodeRemote, - pub(crate) unprocessed_signals_rx: Arc>>>, + pub(crate) unprocessed_signals_rx: Arc>>>>, pub(crate) file_transfer_handle_rx: Arc>>, conn_type: VirtualTargetType, session_security_settings: SessionSecuritySettings, } +impl Deref for ClientServerRemote { + type Target = NodeRemote; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for ClientServerRemote { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + impl_remote!(ClientServerRemote); impl ClientServerRemote { - /// constructs a new [`ClientServerRemote`] from a [`NodeRemote`] and a [`VirtualTargetType`] + /// constructs a new [`ClientServerRemote`] from a [`ClientServerRemote`] and a [`VirtualTargetType`] pub fn new( conn_type: VirtualTargetType, remote: NodeRemote, session_security_settings: SessionSecuritySettings, - unprocessed_signals_rx: Option>, + unprocessed_signals_rx: Option>>, file_transfer_handle_rx: Option, ) -> Self { // TODO: Add handles, only the server calls this @@ -96,7 +110,7 @@ impl ClientServerRemote { /// Can only be called once per remote. Allows receiving events pub fn get_unprocessed_signals_receiver( &self, - ) -> Option> { + ) -> Option>> { self.unprocessed_signals_rx.lock().take() } @@ -132,12 +146,12 @@ impl TargetLockedRemote for ClientServerRemote { impl ClientServerRemote { /// Gracefully closes the protocol and kernel executor - pub async fn shutdown_kernel(self) -> Result<(), NetworkError> { + pub async fn shutdown_kernel(&self) -> Result<(), NetworkError> { self.inner.shutdown().await } pub async fn get_peers( - &mut self, + &self, limit: Option, ) -> Result, NetworkError> { let session_cid = self.conn_type.get_session_cid(); diff --git a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs index 0970afbe2..41c6a2a77 100644 --- a/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs +++ b/citadel_sdk/src/prefabs/server/accept_file_transfer_kernel.rs @@ -55,7 +55,7 @@ impl NetKernel for AcceptFileTransferKernel { Ok(()) } - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { if let NodeResult::ObjectTransferHandle(mut handle) = message { let _ = handle .handle diff --git a/citadel_sdk/src/prefabs/server/client_connect_listener.rs b/citadel_sdk/src/prefabs/server/client_connect_listener.rs index 5a7dc8475..5ba51a83e 100644 --- a/citadel_sdk/src/prefabs/server/client_connect_listener.rs +++ b/citadel_sdk/src/prefabs/server/client_connect_listener.rs @@ -35,7 +35,7 @@ //! # Related Components //! - [`NetKernel`]: Base trait for network kernels //! - [`ClientServerRemote`]: Client-server communication -//! - [`ConnectionSuccess`]: Connection event data +//! - [`CitadelClientServerConnection`]: Connection event data //! - [`NodeResult`]: Network event handling //! @@ -55,7 +55,7 @@ pub struct ClientConnectListenerKernel { impl ClientConnectListenerKernel where - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync, + F: Fn(CitadelClientServerConnection) -> Fut + Send + Sync, Fut: Future> + Send + Sync, { pub fn new(on_channel_received: F) -> Self { @@ -70,7 +70,7 @@ where #[async_trait] impl NetKernel for ClientConnectListenerKernel where - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync, + F: Fn(CitadelClientServerConnection) -> Fut + Send + Sync, Fut: Future> + Send + Sync, { fn load_remote(&mut self, server_remote: NodeRemote) -> Result<(), NetworkError> { @@ -82,7 +82,7 @@ where Ok(()) } - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { match message { NodeResult::ConnectSuccess(ConnectSuccess { ticket: _, @@ -100,19 +100,17 @@ where conn_type, self.node_remote.clone().unwrap(), session_security_settings, - None, // TODO: Add real handles + None, None, ); - (self.on_channel_received)( - ConnectionSuccess { - channel, - udp_channel_rx, - services, - cid, - session_security_settings, - }, - client_server_remote, - ) + (self.on_channel_received)(CitadelClientServerConnection { + remote: client_server_remote.clone(), + channel: Some(channel), + udp_channel_rx, + services, + cid, + session_security_settings, + }) .await } diff --git a/citadel_sdk/src/prefabs/server/empty.rs b/citadel_sdk/src/prefabs/server/empty.rs index 78b9ac723..3a07c0b09 100644 --- a/citadel_sdk/src/prefabs/server/empty.rs +++ b/citadel_sdk/src/prefabs/server/empty.rs @@ -58,7 +58,7 @@ impl NetKernel for EmptyKernel { Ok(()) } - async fn on_node_event_received(&self, _message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, _message: NodeResult) -> Result<(), NetworkError> { Ok(()) } diff --git a/citadel_sdk/src/prefabs/server/internal_service.rs b/citadel_sdk/src/prefabs/server/internal_service.rs index 78feeeda1..b31135914 100644 --- a/citadel_sdk/src/prefabs/server/internal_service.rs +++ b/citadel_sdk/src/prefabs/server/internal_service.rs @@ -83,9 +83,8 @@ where _pd: Default::default(), inner_kernel: Box::new( super::client_connect_listener::ClientConnectListenerKernel::new( - move |connect_success, remote| async move { + move |connect_success| async move { crate::prefabs::shared::internal_service::internal_service( - remote, connect_success, on_create_webserver, ) @@ -107,7 +106,7 @@ impl NetKernel for InternalServiceKernel<'_, F, Fut, R> { self.inner_kernel.on_start().await } - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { self.inner_kernel.on_node_event_received(message).await } @@ -203,10 +202,9 @@ mod test { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |connect_success, remote| async move { + |connection| async move { crate::prefabs::shared::internal_service::internal_service( - remote, - connect_success, + connection, |mut internal_server_communicator| async move { test_write_and_read_one_packet( barrier, @@ -284,10 +282,9 @@ mod test { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |connect_success, remote| async move { + |connection| async move { crate::prefabs::shared::internal_service::internal_service( - remote, - connect_success, + connection, |internal_server_communicator| async move { barrier.wait().await; // wait for the server diff --git a/citadel_sdk/src/prefabs/shared/internal_service.rs b/citadel_sdk/src/prefabs/shared/internal_service.rs index 9409f9d5d..1c199911c 100644 --- a/citadel_sdk/src/prefabs/shared/internal_service.rs +++ b/citadel_sdk/src/prefabs/shared/internal_service.rs @@ -32,13 +32,13 @@ //! - Thread-safe message passing //! //! # Related Components -//! - [`ConnectionSuccess`]: Connection event data +//! - [`CitadelClientServerConnection`]: Connection event data //! - [`TargetLockedRemote`]: Remote target interface //! - [`NetworkError`]: Error handling //! - [`SecBuffer`]: Secure data handling //! -use crate::prelude::{ConnectionSuccess, TargetLockedRemote}; +use crate::prelude::{CitadelClientServerConnection, TargetLockedRemote}; use bytes::Bytes; use citadel_io::tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use citadel_proto::prelude::NetworkError; @@ -50,16 +50,15 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -pub async fn internal_service( - remote: R, - connect_success: ConnectionSuccess, +pub async fn internal_service( + connection: CitadelClientServerConnection, service: F, ) -> Result<(), NetworkError> where F: Send + Copy + Sync + FnOnce(InternalServerCommunicator) -> Fut, Fut: Send + Sync + Future>, - R: TargetLockedRemote, { + let remote = connection.remote.clone(); let (tx_to_service, rx_from_kernel) = citadel_io::tokio::sync::mpsc::unbounded_channel(); let (tx_to_kernel, mut rx_from_service) = citadel_io::tokio::sync::mpsc::unbounded_channel(); @@ -71,7 +70,7 @@ where let internal_server = service(internal_server_communicator); // each time a client connects, we will begin listening for messages. - let (sink, mut stream) = connect_success.channel.split(); + let (sink, mut stream) = connection.split(); // from_proto forwards packets from the proto to the http server let from_proto = async move { while let Some(packet) = stream.next().await { diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs index e0f22549f..e2570b127 100644 --- a/citadel_sdk/src/remote_ext.rs +++ b/citadel_sdk/src/remote_ext.rs @@ -51,7 +51,7 @@ //! - [`NodeRemote`]: Core remote interface //! - [`ClientServerRemote`]: Client-server communication //! - [`PeerRemote`]: Peer-to-peer communication -//! - [`ConnectionSuccess`]: Connection management +//! - [`CitadelClientServerConnection`]: Connection management //! - [`RegisterSuccess`]: Registration handling //! @@ -60,6 +60,7 @@ use crate::prelude::results::{PeerConnectSuccess, PeerRegisterStatus}; use crate::prelude::*; use crate::remote_ext::remote_specialization::PeerRemote; use crate::remote_ext::results::LocalGroupPeerFullInfo; +use std::ops::{Deref, DerefMut}; use futures::StreamExt; use std::path::PathBuf; @@ -165,17 +166,47 @@ pub(crate) mod user_ids { } /// Contains the elements required to communicate with the adjacent node -pub struct ConnectionSuccess { +pub struct CitadelClientServerConnection { /// An interface to send ordered, reliable, and encrypted messages - pub channel: PeerChannel, + pub(crate) channel: Option>, + pub remote: ClientServerRemote, /// Only available if UdpMode was enabled at the beginning of a session - pub udp_channel_rx: Option>, + pub udp_channel_rx: Option>>, /// Contains the Google auth minted at the central server (if the central server enabled it), as well as any other services enabled by the central server pub services: ServicesObject, pub cid: u64, pub session_security_settings: SessionSecuritySettings, } +impl CitadelClientServerConnection { + /// Splits the channel into a send and receive half. This will render + /// the other fields of this connection object innaccessible + /// + /// # Panics + /// - If the channel has already been taken + pub fn split(self) -> (PeerChannelSendHalf, PeerChannelRecvHalf) { + self.channel.expect("Channel already taken").split() + } + + pub fn take_channel(&mut self) -> Option> { + self.channel.take() + } +} + +impl Deref for CitadelClientServerConnection { + type Target = ClientServerRemote; + + fn deref(&self) -> &Self::Target { + &self.remote + } +} + +impl DerefMut for CitadelClientServerConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.remote + } +} + /// Contains the elements entailed by a successful registration pub struct RegisterSuccess { pub cid: u64, @@ -271,7 +302,7 @@ pub trait ProtocolRemoteExt: Remote { keep_alive_timeout: Option, session_security_settings: SessionSecuritySettings, server_password: Option, - ) -> Result { + ) -> Result, NetworkError> { let connect_request = NodeRequest::ConnectToHypernode(ConnectToHypernode { auth_request: auth, connect_mode, @@ -295,14 +326,21 @@ pub trait ProtocolRemoteExt: Remote { session_cid: cid, remote_addr: _, is_personal: _, - v_conn_type: _, + v_conn_type, services, welcome_message: _, channel, udp_rx_opt: udp_channel_rx, session_security_settings, - }) => Ok(ConnectionSuccess { - channel, + }) => Ok(CitadelClientServerConnection { + remote: ClientServerRemote::new( + v_conn_type, + self.remote_ref().clone(), + session_security_settings, + None, + None, + ), + channel: Some(channel), udp_channel_rx, services, cid, @@ -327,7 +365,7 @@ pub trait ProtocolRemoteExt: Remote { async fn connect_with_defaults( &self, auth: AuthenticationRequest, - ) -> Result { + ) -> Result, NetworkError> { self.connect( auth, Default::default(), @@ -536,7 +574,7 @@ pub trait ProtocolRemoteExt: Remote { } } -pub fn map_errors(result: NodeResult) -> Result { +pub fn map_errors(result: NodeResult) -> Result, NetworkError> { match result { NodeResult::ConnectFail(ConnectFail { ticket: _, @@ -1121,8 +1159,8 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote { return match result.status { ReKeyReturnType::Success { version } => Ok(Some(version)), ReKeyReturnType::AlreadyInProgress => Ok(None), - ReKeyReturnType::Failure => { - Err(NetworkError::InternalError("The rekey request failed")) + ReKeyReturnType::Failure { err } => { + Err(NetworkError::Generic(format!("Rekey failed: {err}"))) } }; } @@ -1216,8 +1254,8 @@ pub mod results { use std::fmt::Debug; pub struct PeerConnectSuccess { - pub channel: PeerChannel, - pub udp_channel_rx: Option>, + pub channel: PeerChannel, + pub udp_channel_rx: Option>>, pub remote: PeerRemote, /// Receives incoming file/object transfer requests. The handles must be /// .accepted() before the file/object transfer is allowed to proceed @@ -1269,6 +1307,7 @@ pub mod results { pub mod remote_specialization { use crate::prelude::*; + use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone)] pub struct PeerRemote { @@ -1278,6 +1317,19 @@ pub mod remote_specialization { pub(crate) session_security_settings: SessionSecuritySettings, } + impl Deref for PeerRemote { + type Target = NodeRemote; + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl DerefMut for PeerRemote { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } + } + impl TargetLockedRemote for PeerRemote { fn user(&self) -> &VirtualTargetType { &self.peer @@ -1326,7 +1378,7 @@ mod tests { Ok(()) } - async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { + async fn on_node_event_received(&self, message: NodeResult) -> Result<(), NetworkError> { log::trace!(target: "citadel", "SERVER received {:?}", message); if let NodeResult::ObjectTransferHandle(ObjectTransferHandle { mut handle, .. }) = map_errors(message)? @@ -1419,9 +1471,9 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - |_channel, remote| async move { + |connection| async move { log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***"); - remote + connection .send_file_with_custom_opts( "../resources/TheBridge.pdf", 32 * 1024, @@ -1431,7 +1483,7 @@ mod tests { .unwrap(); log::trace!(target: "citadel", "***CLIENT FILE TRANSFER SUCCESS***"); client_success.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); diff --git a/citadel_sdk/src/responses.rs b/citadel_sdk/src/responses.rs index 7d0b73cce..473549c35 100644 --- a/citadel_sdk/src/responses.rs +++ b/citadel_sdk/src/responses.rs @@ -147,7 +147,7 @@ pub async fn peer_connect( /// Given a group invite signal, this function sends a response to the server pub async fn group_invite( - invite_signal: NodeResult, + invite_signal: NodeResult, accept: bool, remote: &impl Remote, ) -> Result { diff --git a/citadel_sdk/src/test_common.rs b/citadel_sdk/src/test_common.rs index 5778d30ec..09f050ed5 100644 --- a/citadel_sdk/src/test_common.rs +++ b/citadel_sdk/src/test_common.rs @@ -90,7 +90,7 @@ pub fn server_info_reactive<'a, F, Fut, R: Ratchet>( opts: impl FnOnce(&mut NodeBuilder), ) -> (NodeFuture<'a, Box + 'a>>, SocketAddr) where - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync + 'a, + F: Fn(CitadelClientServerConnection) -> Fut + Send + Sync + 'a, Fut: Future> + Send + Sync + 'a, { server_test_node( @@ -103,7 +103,7 @@ where #[cfg(not(feature = "localhost-testing"))] pub fn server_info_reactive< 'a, - F: Fn(ConnectionSuccess, ClientServerRemote) -> Fut + Send + Sync + 'a, + F: Fn(CitadelClientServerConnection) -> Fut + Send + Sync + 'a, Fut: Future> + Send + Sync + 'a, R: Ratchet, >( @@ -221,9 +221,9 @@ lazy_static::lazy_static! { tracing::instrument(level = "trace", target = "citadel") )] #[allow(dead_code)] -pub async fn udp_mode_assertions( +pub async fn udp_mode_assertions( udp_mode: UdpMode, - udp_channel_rx_opt: Option>, + udp_channel_rx_opt: Option>>, ) { use futures::StreamExt; citadel_logging::info!(target: "citadel", "Inside UDP mode assertions ..."); diff --git a/citadel_sdk/tests/stress_tests.rs b/citadel_sdk/tests/stress_tests.rs index efc459b45..9d561cdae 100644 --- a/citadel_sdk/tests/stress_tests.rs +++ b/citadel_sdk/tests/stress_tests.rs @@ -87,11 +87,6 @@ mod tests { } impl MessageTransfer { - pub fn create(idx: u64) -> SecureProtocolPacket { - let rand = Self::create_rand(idx); - rand.into() - } - pub fn create_secbuffer(idx: u64) -> SecBuffer { let rand = Self::create_rand(idx); rand.into() @@ -109,14 +104,15 @@ mod tests { } } - async fn handle_send_receive_e2e( + async fn handle_send_receive_e2e( barrier: Arc, - channel: PeerChannel, + channel: PeerChannel, count: usize, ) -> Result<(), NetworkError> { let (tx, rx) = channel.split(); for idx in 0..count { - tx.send_message(MessageTransfer::create(idx as u64)).await?; + tx.send_message(MessageTransfer::create_secbuffer(idx as u64)) + .await?; } let mut cur_idx = 0usize; @@ -228,12 +224,17 @@ mod tests { let (server, server_addr) = citadel_sdk::test_common::server_info_reactive::<_, _, StackedRatchet>( - move |conn, remote| async move { + move |mut connection| async move { log::trace!(target: "citadel", "*** SERVER RECV CHANNEL ***"); - handle_send_receive_e2e(get_barrier(), conn.channel, message_count).await?; + handle_send_receive_e2e( + get_barrier(), + connection.take_channel().unwrap(), + message_count, + ) + .await?; log::trace!(target: "citadel", "***SERVER TEST SUCCESS***"); SERVER_SUCCESS.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, |_| {}, ); @@ -254,12 +255,17 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( server_connection_settings, - move |connection, remote| async move { + move |mut connection| async move { log::trace!(target: "citadel", "*** CLIENT RECV CHANNEL ***"); - handle_send_receive_e2e(get_barrier(), connection.channel, message_count).await?; + handle_send_receive_e2e( + get_barrier(), + connection.take_channel().unwrap(), + message_count, + ) + .await?; log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); CLIENT_SUCCESS.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); @@ -298,12 +304,17 @@ mod tests { let (server, server_addr) = citadel_sdk::test_common::server_info_reactive::<_, _, StackedRatchet>( - move |conn, remote| async move { + move |mut connection| async move { log::trace!(target: "citadel", "*** SERVER RECV CHANNEL ***"); - handle_send_receive_e2e(get_barrier(), conn.channel, message_count).await?; + handle_send_receive_e2e( + get_barrier(), + connection.take_channel().unwrap(), + message_count, + ) + .await?; log::trace!(target: "citadel", "***SERVER TEST SUCCESS***"); SERVER_SUCCESS.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, |node| { if let Some(password) = server_password { @@ -332,12 +343,17 @@ mod tests { let client_kernel = SingleClientServerConnectionKernel::new( connection_settings, - move |connection, remote| async move { + move |mut connection| async move { log::trace!(target: "citadel", "*** CLIENT RECV CHANNEL ***"); - handle_send_receive_e2e(get_barrier(), connection.channel, message_count).await?; + handle_send_receive_e2e( + get_barrier(), + connection.take_channel().unwrap(), + message_count, + ) + .await?; log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***"); CLIENT_SUCCESS.store(true, Ordering::Relaxed); - remote.shutdown_kernel().await + connection.shutdown_kernel().await }, ); diff --git a/citadel_types/src/crypto/mod.rs b/citadel_types/src/crypto/mod.rs index ad0c1f9d6..f3c451850 100644 --- a/citadel_types/src/crypto/mod.rs +++ b/citadel_types/src/crypto/mod.rs @@ -66,6 +66,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{Debug, Formatter}; use std::ops::{Add, Deref, DerefMut}; use strum::{EnumCount, ParseError}; +use uuid::Uuid; pub const LARGEST_NONCE_LEN: usize = KYBER_NONCE_LENGTH_BYTES; @@ -118,11 +119,12 @@ pub fn add_inner(lhs: L, rhs: R) -> CryptoPa ret } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)] pub enum SecrecyMode { /// Slowest, but ensures each packet gets encrypted with a unique symmetrical key Perfect, /// Fastest. Meant for high-throughput environments. Each message will attempt to get re-keyed, but if not possible, will use the most recent symmetrical key + #[default] BestEffort, } @@ -141,15 +143,9 @@ impl TryFrom for SecrecyMode { } } -impl Default for SecrecyMode { - fn default() -> Self { - Self::BestEffort - } -} - /// A memory-secure wrapper for shipping around Bytes pub struct SecBuffer { - pub inner: BytesMut, + inner: BytesMut, } impl SecBuffer { @@ -516,6 +512,39 @@ impl From for SecurityLevel { } } +#[derive(Serialize, Deserialize, Copy, Clone, Debug, Default)] +pub enum HeaderObfuscatorSettings { + /// Enables header obfuscation to help mitigate some deep packet inspection techniques using a pseudorandom key + Enabled, + #[default] + /// Disables header obfuscation (default) + Disabled, + /// Enables header obfuscation with a specific key. This value must be symmetric between both endpoints, otherwise the obfuscation will fail + EnabledWithKey(u128), +} + +impl From for HeaderObfuscatorSettings { + fn from(val: u128) -> Self { + HeaderObfuscatorSettings::EnabledWithKey(val) + } +} + +impl From for HeaderObfuscatorSettings { + fn from(value: bool) -> Self { + if value { + HeaderObfuscatorSettings::Enabled + } else { + HeaderObfuscatorSettings::Disabled + } + } +} + +impl From for HeaderObfuscatorSettings { + fn from(value: Uuid) -> Self { + HeaderObfuscatorSettings::EnabledWithKey(value.as_u128()) + } +} + #[cfg(test)] mod test { use crate::crypto::SecBuffer; diff --git a/citadel_types/src/proto/mod.rs b/citadel_types/src/proto/mod.rs index a8d3122f3..ac1f674d6 100644 --- a/citadel_types/src/proto/mod.rs +++ b/citadel_types/src/proto/mod.rs @@ -1,4 +1,5 @@ use crate::crypto::{CryptoParameters, SecrecyMode, SecurityLevel}; +use crate::prelude::HeaderObfuscatorSettings; use crate::utils; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Display, Formatter}; @@ -160,6 +161,7 @@ pub struct SessionSecuritySettings { pub security_level: SecurityLevel, pub secrecy_mode: SecrecyMode, pub crypto_params: CryptoParameters, + pub header_obfuscator_settings: HeaderObfuscatorSettings, } #[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Default)] diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs index 64ebc9da1..9c33af73a 100644 --- a/citadel_user/src/account_manager.rs +++ b/citadel_user/src/account_manager.rs @@ -36,9 +36,10 @@ //! use citadel_user::backend::BackendType; //! use citadel_user::account_manager::AccountManager; //! use citadel_crypt::ratchets::stacked::StackedRatchet; +//! use citadel_crypt::endpoint_crypto_container::PeerSessionCrypto; //! use citadel_user::auth::proposed_credentials::ProposedCredentials; //! -//! # fn get_ratchet() -> StackedRatchet { todo!() } +//! # fn gen_crypto_state() -> PeerSessionCrypto { todo!() } //! async fn example() -> Result<(), Box> { //! // Initialize account manager with in-memory backend //! let manager = AccountManager::::new( @@ -53,12 +54,12 @@ //! //! let creds = ProposedCredentials::transient("some-unique-id"); //! -//! let ratchet = get_ratchet(); +//! let crypto_state = gen_crypto_state(); //! //! let account = manager.register_impersonal_hyperlan_client_network_account( //! conn_info, //! creds, -//! ratchet +//! crypto_state //! ).await?; //! //! // Retrieve peer information diff --git a/citadel_user/src/client_account.rs b/citadel_user/src/client_account.rs index e84776061..0afe8f138 100644 --- a/citadel_user/src/client_account.rs +++ b/citadel_user/src/client_account.rs @@ -221,7 +221,7 @@ impl ClientNetworkAccount { pub fn store_rtdb_config(&self, cfg: crate::external_services::rtdb::RtdbClientConfig) { self.write().client_rtdb_config = Some(cfg); } - + pub fn auth_store(&self) -> &DeclaredAuthenticationMode { &self.inner.auth_store } @@ -308,7 +308,7 @@ impl ClientNetworkAccount { } /// This should ONLY be used for recovery mode - pub fn get_static_auxiliary_stacked_ratchet(&self) -> R { + pub fn get_static_auxiliary_ratchet(&self) -> R { self.get_session_crypto() .toolset() .read() @@ -540,7 +540,7 @@ impl ClientNetworkAccount { } /// Returns true if passwordless - pub fn passwordless(&self) -> bool { + pub fn is_transient(&self) -> bool { self.inner.is_transient } } diff --git a/citadel_user/tests/primary.rs b/citadel_user/tests/primary.rs index c7b80460b..339767054 100644 --- a/citadel_user/tests/primary.rs +++ b/citadel_user/tests/primary.rs @@ -49,9 +49,11 @@ mod tests { .get_persistence_handler() .get_cid_by_username(username); let (client_hr, server_hr) = gen(cid, 0, None); - let server_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, server_hr), false); - let client_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, client_hr), true); - + let server_session_crypto_state = + PeerSessionCrypto::new(Toolset::new(cid, server_hr), false); + let client_session_crypto_state = + PeerSessionCrypto::new(Toolset::new(cid, client_hr), true); + let server_vers = self .server_acc_mgr .register_impersonal_hyperlan_client_network_account( @@ -103,9 +105,11 @@ mod tests { .get_persistence_handler() .get_cid_by_username(username); let (client_hr, server_hr) = gen(cid, 0, None); - let server_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, server_hr), false); - let client_session_crypto_state = PeerSessionCrypto::new(Toolset::new(cid, client_hr), true); - + let server_session_crypto_state = + PeerSessionCrypto::new(Toolset::new(cid, server_hr), false); + let client_session_crypto_state = + PeerSessionCrypto::new(Toolset::new(cid, client_hr), true); + let _server_vers = self .server_acc_mgr .register_impersonal_hyperlan_client_network_account( diff --git a/context.ai.json b/context.ai.json index 01c131909..0bcf3d68f 100644 --- a/context.ai.json +++ b/context.ai.json @@ -1138,7 +1138,7 @@ ] }, { - "file": "./citadel_crypt/src/stacked_ratchet.rs", + "file": "./citadel_crypt/src/ratchet.rs", "context": [ "Implements perfect forward secrecy", "Manages independent key evolution", diff --git a/suggestions.ai.json b/suggestions.ai.json index e51b1482a..66f2fd25f 100644 --- a/suggestions.ai.json +++ b/suggestions.ai.json @@ -974,7 +974,7 @@ ] }, { - "file": "./citadel_crypt/src/stacked_ratchet.rs", + "file": "./citadel_crypt/src/ratchet.rs", "suggestions": [ "Consider adding benchmarks for key evolution operations", "Add more detailed error handling for edge cases", From f27d5e705475a7faa4837e151f9885825cd8181b Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sun, 12 Jan 2025 07:33:34 -0500 Subject: [PATCH 12/12] fix: cleanup sql backend errors --- citadel_sdk/src/test_common.rs | 2 ++ citadel_user/src/backend/redis_backend.rs | 2 +- citadel_user/src/backend/sql_backend.rs | 3 +-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/citadel_sdk/src/test_common.rs b/citadel_sdk/src/test_common.rs index 83fe17678..c5cd81dc9 100644 --- a/citadel_sdk/src/test_common.rs +++ b/citadel_sdk/src/test_common.rs @@ -229,6 +229,8 @@ pub async fn udp_mode_assertions( citadel_logging::info!(target: "citadel", "Inside UDP mode assertions ..."); match udp_mode { UdpMode::Enabled => { + // Wait to give time for the other side to finish connecting + citadel_io::tokio::time::sleep(Duration::from_millis(500)).await; citadel_logging::info!(target: "citadel", "Inside UDP mode assertions AB1 ..."); assert!(udp_channel_rx_opt.is_some()); citadel_logging::info!(target: "citadel", "Inside UDP mode assertions AB1.5 ..."); diff --git a/citadel_user/src/backend/redis_backend.rs b/citadel_user/src/backend/redis_backend.rs index 27a130b8c..c5eb2e1fa 100644 --- a/citadel_user/src/backend/redis_backend.rs +++ b/citadel_user/src/backend/redis_backend.rs @@ -34,7 +34,7 @@ use crate::backend::memory::no_backend_streaming; use crate::backend::BackendConnection; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; -use crate::prelude::{AccountState, HYPERLAN_IDX}; +use crate::prelude::HYPERLAN_IDX; use crate::serialization::SyncIO; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; diff --git a/citadel_user/src/backend/sql_backend.rs b/citadel_user/src/backend/sql_backend.rs index cccfb431d..1cebeaba7 100644 --- a/citadel_user/src/backend/sql_backend.rs +++ b/citadel_user/src/backend/sql_backend.rs @@ -34,14 +34,13 @@ use crate::backend::memory::no_backend_streaming; use crate::backend::{BackendConnection, BackendType}; use crate::client_account::ClientNetworkAccount; use crate::misc::{AccountError, CNACMetadata}; -use crate::prelude::{AccountState, HYPERLAN_IDX}; +use crate::prelude::HYPERLAN_IDX; use crate::serialization::SyncIO; use async_trait::async_trait; use citadel_crypt::ratchets::mono::MonoRatchet; use citadel_crypt::ratchets::stacked::StackedRatchet; use citadel_crypt::ratchets::Ratchet; use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; -use citadel_io::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use citadel_types::proto::{ObjectTransferStatus, VirtualObjectMetadata}; use citadel_types::user::MutualPeer; use itertools::Itertools;