citadel_sdk/lib.rs
1#![doc(html_logo_url = "avarok.png", html_favicon_url = "favicon.png")]
2//! Software development kit for creating high performance, extremely-secure, and post-quantum network applications. Supports p2p (NAT traversal + WebRTC) and standard client/server architectures for
3//! messaging and streaming. The underlying asynchronous runtime is [Tokio](https://tokio.rs).
4//!
5//! The Network protocol, SDK, and user libraries use 100% safe rust
6//!
7//! All peer-discovery and NAT traversal are built-in to the protocol, with the central server acting as a broker and authenticator. The central server is used for TURN-like routing when direct p2p NAT traversal fails between two nodes.
8//!
9//! Authentication to a central node is required before making peer-to-peer connections. There is both device-dependent auth as well as credentialed authentication backed by the argon2id hashing algorithm.
10//!
11//! Client/Peer information is by default synchronized to the local filesystem. If the *redis* and/or *sql* feature is enabled, a redis or SQL (MySQL, PostgreSQL, SQLite) server or cluster can be used instead.
12//!
13//! When messaging is used, perfect forward secrecy (PFS) is an optional mode on a per-session basis. Best-effort mode (BEM) is also available if the security of PFS is not needed, and instead, high throughput
14//! in messaging is required.
15//!
16//! Client-to-server connections can use TCP, TLS (default), or QUIC protocols for the underlying communication. Cryptographers recommend the use of hybrid protocols, and as such, TLS is the default to
17//! ensure implementations of post-quantum networks are at least as secure as traditional methods. Valid certificates can be specified when constructing the application, otherwise, self-signed certificates are used for the underlying protocol.
18//!
19//! Peer-to-peer connections only use QUIC. In order to establish a direct peer-to-peer connection, UDP NAT-traversal is required, and as such, the use of QUIC complements this requirement since QUIC uses UDP for ordered, reliable transport.
20//!
21//! Streaming is also available in this crate. When the use of webrtc is desired for an application, the *webrtc* feature can be enabled to allow interoperability between the [`UdpChannel`] and the [WebRTC.rs](https://webrtc.rs) ecosystem.
22//!
23//! # Feature Flags
24//! - `multi-threaded`: Uses a multi-threaded (Send) executor for the inner protocol
25//! - `redis`: Enables the use of Redis for the backend
26//! - `sql`: Enables the use of sql for the backend
27//! - `webrtc`: enables *limited* interoperability with webrtc via the [`UdpChannel`] (see: `UdpChannel::into_webrtc_compat`, available with the feature enabled)
28//! - `security-key`: security keys for post-quantum sign-in, over USB natively and through WebAuthn in a browser (see [`sign_in`])
29//!
30//!
31//! # Post-quantum key encapsulation mechanisms
32//! The user may also select a KEM family before a session to either a central server or peer begins (see: [SessionSecuritySettingsBuilder](crate::prelude::SessionSecuritySettingsBuilder)). Each KEM has variants that alter the degree of security
33//! - Kyber (default)
34//! - NTRU (Sntrup761)
35//!
36//! # Encryption Algorithms
37//! The user may also select a symmetric encryption algorithm before a session starts (see: [SessionSecuritySettingsBuilder](crate::prelude::SessionSecuritySettingsBuilder))
38//!
39//! - AES-256-GCM
40//! - Chacha20Poly-1305
41//! - Ascon-80pq
42//! - Kyber Hybrid Encryption (see below for explanation)
43//!
44//! Whereas AES-GCM and ChaCha are only quantum resistant (as opposed to post-quantum), a novel method of encryption may be used that
45//! combines the post-quantum asymmetric encryption algorithm Kyber coupled with AES. When Kyber Hybrid Encryption is used, several modifications to the protocol outlined in the whitepaper
46//! is applied. The first modification is the use of Falcon-1024 to sign each message to ensure non-repudiation. The second modification is more complex. Ciphertext is first encrypted by AES-GCM, then, randomly shifted using modular arithmetic
47//! in 32-byte blocks using a 32-byte long quasi one-time pad (OTP). The OTP is unique for each ciphertext, and, is appended at the end of the ciphertext in encrypted form (using Kyber1024 encryption). Even if the attacker uses Grover's algorithm to
48//! discover the AES key, the attacker would also have to break the lattice-based Kyber cryptography in order to properly order
49//! the ciphertext before using the AES key. Since every 32 bytes of input into the Kyber encryption scheme produces over a 1KB output ciphertext, and, each quasi-OTP is 32 bytes long,
50//! the size of each packet is increased at a minimum constant value, helping keep packet sizes minimal and security very high.
51//!
52//! # Network Architecture
53#![cfg_attr(
54 feature = "doc-images",
55 doc = ::embed_doc_image::embed_image!(
56 "network_direct_p2p",
57 "../resources/network_direct_p2p.png"
58 )
59)]
60//! ![Network Architecture w/ direct P2P][network_direct_p2p]
61//! Each network has a central node that peers may connect to. This central node helps facilitate P2P connections, and, can itself serve
62//! as a peer on a network if the program implementation on the central server so chooses.
63//!
64//! The peers Alice and Bob can only connect to each other after they use the central server to **register** to each other. Once registered, the two peers
65//! may begin attempting connecting to each other via NAT traversal. Each peer begins NAT traversal by attempting to determine what type of NAT
66//! they're each behind by communicating to 3 different STUN servers to find a predictable pattern in their internal/external socket mappings.
67//! If at least one has a predictable pattern, a direct P2P connection bypassing the central server may be facilitated.
68//!
69//! If, however, both Alice and Bob do not have predictable internal/external socket mappings (e.g., both are behind symmetric NATs), then, both will use
70//! their central server to relay their packets to each other using endpoint-to-endpoint encryption, preventing the central server from
71//! decrypting the packets.
72//!
73//! Applications never wait for NAT traversal: a peer connection is delivered, and usable, as soon as the keys are exchanged, with its
74//! traffic on the central server's relay. Hole punching runs in the background, and when a direct path is established the channel
75//! upgrades in place (and falls back to the relay if that path is later lost), with every message delivered once and in order across
76//! the switch. `PeerChannel::ensure_direct` waits for the direct path when an application needs one (e.g. for UDP), and
77//! `PeerChannel::path_changes` reports each change.
78//!
79#![cfg_attr(
80 feature = "doc-images",
81 doc = ::embed_doc_image::embed_image!(
82 "network_relay_p2p",
83 "../resources/network_relay_p2p.png"
84 )
85)]
86//! ![Network Architecture w/ relay P2P][network_relay_p2p]
87//!
88//!
89//! # Executor Architecture: The [`NetKernel`]
90#![cfg_attr(
91 feature = "doc-images",
92 doc = ::embed_doc_image::embed_image!(
93 "proto_kernel_iface",
94 "../resources/proto_kernel_iface.png"
95 )
96)]
97//! ![Protocol/Executor/NetKernel Architecture][proto_kernel_iface]
98//! Any node in the network may act as **both** a server and a client/peer (except for when [`NodeType::Peer`] or the default node type is specified). Since multiple parallel connections may exist, handling events is necessary. When the lower-level protocol produces events,
99//! they are sent to the [`NetKernel`]. The [`NetKernel`] is where your application logic must be written.
100//!
101//! ## Initialization Stage: The [`KernelExecutor`] and the [`NodeRemote`]
102//! When the node is built and awaited (as seen in the examples below), the node creates a [`NodeRemote`] which is used to communicate between the [`NetKernel`] and the lower level networking protocol. Then, the [`KernelExecutor`] passes the remote to [`NetKernel::load_remote`] (which uses a mutable reference to
103//! the kernel itself to allow mutation of the inner data, effectively ensuring that the remote may be stored without need of atomics, as well as any other config). Thereafter, the [`KernelExecutor`] calls [`NetKernel::on_start`] (uses an ``&self`` reference) where any first asynchronous calls using the remote itself may be made.
104//!
105//! ## Passive Stage
106//! As the protocol generates events, the developer may choose to add program logic to react to the events. When an event is sent from the protocol to the [`KernelExecutor`], the [`KernelExecutor`] executes [`NetKernel::on_node_event_received`], passing the new event. Importantly,
107//! every call to [`NetKernel::on_node_event_received`] is executed *concurrently* (**not** to be confused with *parallel*), allowing the developer to react to each event separately without having to await completion before handling the next event. If an error is returned from [`NetKernel::on_node_event_received`], then the [`KernelExecutor`] will attempt
108//! a graceful shutdown of the protocol and any running sessions. Errors returned from [`NetKernel::on_node_event_received`] are propagated to the initial awaited call site on the node.
109//!
110//! Important note: Since [`NetKernel::on_node_event_received`] takes self by reference and is executed concurrently, [`NetKernel`] requires that ``Self: Sync`` since by definition, if ``&T: Send``, then ``T: Sync``
111//!
112//! ## Shutdown stage
113//! Whether through an error, or, a call to [`NodeRemote::shutdown`], the [`KernelExecutor`] will call [`NetKernel::on_stop`] (which is passed an &mut). During and after the execution of [`NetKernel::on_stop`], no more calls to [`NetKernel::on_node_event_received`] will occur. Any errors returned from [`NetKernel::on_stop`] will be propagated
114//! to the initial awaited call site on the node. Execution is complete, returning the initial kernel on success
115//!
116//! # Examples
117//!
118//! ## Server
119//! When building either a client/peer or server node, a [`NetKernel`] is expected. In the case below, an EmptyKernel is used that does no additional processing of inbound connections:
120//! ```
121//! use citadel_sdk::prelude::*;
122//! use citadel_sdk::prefabs::server::empty::EmptyKernel;
123//! # fn every_path_in_order() -> PathPolicy {
124//! # let rules = PathKind::ALL.into_iter().map(|kind| PathRule { kind, enabled: true,
125//! # attempt_timeout: std::time::Duration::from_secs(30), cost_weight: 1,
126//! # classes: PathClasses::ALL }).collect();
127//! # PathPolicy::new(rules, PathClasses::NONE).unwrap()
128//! # }
129//!
130//! // this server will listen on 127.0.0.1:25021. STUN servers are required configuration for
131//! // every node: there is no built-in list. A server also requires a path policy: which network
132//! // paths its peers may use, in which order (see `PathPolicy`). When calling 'build', a
133//! // NetKernel is specified
134//! let server = DefaultNodeBuilder::default()
135//! .with_node_type(NodeType::server("127.0.0.1:25021")?)
136//! .with_stun_servers(["stun1.example.com:3478", "stun2.example.com:3478", "stun3.example.com:3478"])
137//! .with_path_policy(every_path_in_order())
138//! .build(EmptyKernel::default())?;
139//!
140//! // await the server to execute
141//! # async move {
142//! let result = server.await;
143//! # };
144//! # Ok::<(), Box<dyn std::error::Error>>(())
145//! ```
146//!
147//! ## Client/Peer
148//! This client will connect to the server above. It will first register (if the account is not yet registered), and thereafter, connect to the server, calling the provided future to handle the received channel
149//! ```
150//! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel;
151//! use futures::StreamExt;
152//! use citadel_sdk::prelude::*;
153//!
154//! let server_connection_settings = DefaultServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?;
155//!
156//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |conn| async move {
157//! // handle program logic here
158//! let (sink, mut stream) = conn.split();
159//! while let Some(message) = stream.next().await {
160//! // message received in the form of a SecBuffer (memory-protected)
161//! }
162//!
163//! Ok(())
164//! });
165//!
166//! let client = DefaultNodeBuilder::default()
167//! .with_stun_servers(["stun1.example.com:3478", "stun2.example.com:3478", "stun3.example.com:3478"])
168//! .build(client_kernel)?;
169//! # async move {
170//! let result = client.await;
171//! # };
172//! # Ok::<(), Box<dyn std::error::Error>>(())
173//! ```
174//!
175//! ## Remote Encrypted Virtual Filesystem (RE-VFS)
176//! The RE-VFS allows clients, servers, and peers to treat each other as remote endpoints for encrypted file storage.
177//! Since encrypting data locally using a symmetric key poses a vulnerability if the local node is compromised, The
178//! Citadel Protocol solves this issue by using a local 1024-Kyber public key to encrypt the data (via Kyber Hybrid Encryption for
179//! keeping the data size to a minimum), then, sending the contents to the adjacent endpoint. By doing this, the private decryption
180//! key and the contents are kept separate, forcing the hacker to compromise both endpoints.
181//!
182//! In order to use the RE-VFS, both endpoints must use the Filesystem backend. Second, the endpoint serving as a storage point
183//! must accept the inbound file transfer requests, otherwise, the transfer will fail. The example below for the receiving endpoint
184//! shows how to auto-accept inbound file transfer requests
185//!
186//! # Examples
187//!
188//! ## Receiving endpoint
189//! ```
190//! use citadel_sdk::prelude::*;
191//! use citadel_sdk::prefabs::server::accept_file_transfer_kernel::AcceptFileTransferKernel;
192//! # fn every_path_in_order() -> PathPolicy {
193//! # let rules = PathKind::ALL.into_iter().map(|kind| PathRule { kind, enabled: true,
194//! # attempt_timeout: std::time::Duration::from_secs(30), cost_weight: 1,
195//! # classes: PathClasses::ALL }).collect();
196//! # PathPolicy::new(rules, PathClasses::NONE).unwrap()
197//! # }
198//!
199//! // this server will listen on 127.0.0.1:25021, and will use the built-in defaults with a kernel
200//! // that auto-accepts inbound file transfer requests
201//! let server = DefaultNodeBuilder::default()
202//! .with_node_type(NodeType::server("127.0.0.1:25021")?)
203//! .with_stun_servers(["stun1.example.com:3478", "stun2.example.com:3478", "stun3.example.com:3478"])
204//! .with_path_policy(every_path_in_order())
205//! .build(AcceptFileTransferKernel::default())?;
206//!
207//! // await the server to execute
208//! # async move {
209//! let result = server.await;
210//! # };
211//! # Ok::<(), Box<dyn std::error::Error>>(())
212//! ```
213//!
214//! ## Sending endpoint
215//! ```
216//! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel;
217//! use futures::StreamExt;
218//! use citadel_sdk::prelude::*;
219//!
220//! let server_connection_settings = DefaultServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?;
221//!
222//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |conn| async move {
223//! let virtual_path = "/home/virtual_user/output.pdf";
224//! // write the contents with reinforced security.
225//! citadel_sdk::fs::write_with_security_level(&conn.remote, "../path/to/input.pdf", SecurityLevel::Reinforced, virtual_path).await?;
226//! // read the contents. Reading downloads the file to a local path
227//! let stored_local_path = citadel_sdk::fs::read(&conn.remote, virtual_path).await?;
228//!
229//! Ok(())
230//! });
231//!
232//! let client = DefaultNodeBuilder::default()
233//! .with_stun_servers(["stun1.example.com:3478", "stun2.example.com:3478", "stun3.example.com:3478"])
234//! .build(client_kernel)?;
235//! # async move {
236//! let result = client.await;
237//! # };
238//! # Ok::<(), Box<dyn std::error::Error>>(())
239//! ```
240//!
241//! [`UdpChannel`]: crate::prelude::UdpChannel
242//! [`NetKernel`]: crate::prelude::NetKernel
243//! [`NetKernel::load_remote`]: crate::prelude::NetKernel::load_remote
244//! [`NetKernel::on_start`]: crate::prelude::NetKernel::on_start
245//! [`NetKernel::on_node_event_received`]: crate::prelude::NetKernel::on_node_event_received
246//! [`NetKernel::on_stop`]: crate::prelude::NetKernel::on_stop
247//! [`KernelExecutor`]: crate::prelude::KernelExecutor
248//! [`NodeRemote`]: crate::prelude::NodeRemote
249//! [`NodeRemote::shutdown`]: crate::prelude::NodeRemote::shutdown
250//! [`NodeType`]: crate::prelude::NodeType
251//! [`NodeType::Peer`]: crate::prelude::NodeType::Peer
252//! [`PeerConnectionType`]: crate::prelude::PeerConnectionType
253//! [`TargetLockedRemote`]: crate::prelude::TargetLockedRemote
254//! [`PersistenceHandler`]: crate::prelude::PersistenceHandler
255//! [`SecurityLevel`]: crate::prelude::SecurityLevel
256//! [`ObjectSource`]: crate::prelude::ObjectSource
257//! [`NetworkError`]: crate::prelude::NetworkError
258#![forbid(unsafe_code)]
259#![allow(rustdoc::invalid_html_tags)]
260#![deny(
261 clippy::cognitive_complexity,
262 trivial_numeric_casts,
263 unused_extern_crates,
264 unused_import_braces,
265 variant_size_differences,
266 unused_features,
267 unused_results
268)]
269
270/// Convenience import for building applications
271pub mod prelude {
272 pub use crate::backend_kv_store::BackendHandler;
273 pub use crate::builder::node_builder::*;
274 pub use crate::prefabs::client::peer_connection::PeerConnectionSetupAggregator;
275 pub use crate::prefabs::client::PrefabFunctions;
276 pub use crate::prefabs::client::{
277 DefaultServerConnectionSettingsBuilder, ServerConnectionSettings,
278 };
279 pub use crate::prefabs::ClientServerRemote;
280 pub use crate::remote_ext::remote_specialization::PeerRemote;
281 pub use crate::remote_ext::user_ids::*;
282 pub use crate::remote_ext::*;
283 pub use crate::responses;
284 pub use crate::sign_in::SignInManagementExt;
285 #[cfg(feature = "security-key")]
286 pub use citadel_proto::auth::SecurityKey;
287 #[cfg(all(feature = "security-key", not(target_family = "wasm")))]
288 pub use citadel_proto::auth::SecurityKeyPrompt;
289 pub use citadel_proto::auth::{
290 security_key_channel, SecurityKeyCeremony, SecurityKeyChallenge, SecurityKeyPrf,
291 SecurityKeyPurpose, SecurityKeyRequest, SignInFactors,
292 };
293 pub use citadel_proto::prelude::*;
294 pub use citadel_types::auth::{
295 FactorId, FactorKind, SessionScope, SignInCredential, SignInManagementOp,
296 SignInManagementOutcome, SignInPolicy,
297 };
298 pub use citadel_types::prelude::*;
299 pub use citadel_user::auth::pq::admission::{
300 challenge_of, AdmissionChallenge, AdmissionProvider,
301 };
302}
303
304/// Store data to the backend using this library
305pub mod backend_kv_store;
306mod builder;
307/// Convenience functions for interacting with the remote encrypted virtual filesystem (RE-VFS)
308pub mod fs;
309/// Extension implementations endowed upon the [NodeRemote](crate::prelude::NodeRemote)
310mod group_create_wait;
311/// Real-time audio/video transport bound to a connection (see [`citadel_media`])
312pub mod media;
313/// The prefabs module contains pre-built kernels for common use cases.
314pub mod prefabs;
315
316pub mod remote_ext;
317/// For easy construction of replies to common message types
318pub mod responses;
319
320mod server_probe;
321/// Managing a post-quantum account's sign-in factors
322pub mod sign_in;
323#[doc(hidden)]
324pub mod test_common;
325
326/// Browser-to-browser serverless connections (WASM only).
327#[cfg(target_family = "wasm")]
328pub mod net;
329
330#[macro_use]
331pub(crate) mod macros;
332
333pub use citadel_logging as logging;
334/// Codec-agnostic media primitives used by [`media`]
335pub use citadel_media;
336/// Convenience for SDK users
337pub use citadel_proto::prelude::async_trait;
338pub use citadel_types as types;