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](crate::prelude::UdpChannel::into_webrtc_compat))
28//!
29//!
30//! # Post-quantum key encapsulation mechanisms
31//! 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
32//! - Kyber (default)
33//! - NTRU (Sntrup761)
34//!
35//! # Encryption Algorithms
36//! The user may also select a symmetric encryption algorithm before a session starts (see: [SessionSecuritySettingsBuilder](crate::prelude::SessionSecuritySettingsBuilder))
37//!
38//! - AES-256-GCM
39//! - Chacha20Poly-1305
40//! - Ascon-80pq
41//! - Kyber Hybrid Encryption (see below for explanation)
42//!
43//! Whereas AES-GCM and ChaCha are only quantum resistant (as opposed to post-quantum), a novel method of encryption may be used that
44//! 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
45//! 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
46//! 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
47//! discover the AES key, the attacker would also have to break the lattice-based Kyber cryptography in order to properly order
48//! 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,
49//! the size of each packet is increased at a minimum constant value, helping keep packet sizes minimal and security very high.
50//!
51//! # Network Architecture
52#![cfg_attr(
53    feature = "doc-images",
54    doc = ::embed_doc_image::embed_image!(
55    "network_direct_p2p",
56    "../resources/network_direct_p2p.png"
57    )
58)]
59//! ![Network Architecture w/ direct P2P][network_direct_p2p]
60//! Each network has a central node that peers may connect to. This central node helps facilitate P2P connections, and, can itself serve
61//! as a peer on a network if the program implementation on the central server so chooses.
62//!
63//! 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
64//! may begin attempting connecting to each other via NAT traversal. Each peer begins NAT traversal by attempting to determine what type of NAT
65//! they're each behind by communicating to 3 different STUN servers to find a predictable pattern in their internal/external socket mappings.
66//! If at least one has a predictable pattern, a direct P2P connection bypassing the central server may be facilitated.
67//!
68//! 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
69//! their central server to relay their packets to each other using endpoint-to-endpoint encryption, preventing the central server from
70//! decrypting the packets.
71//!
72#![cfg_attr(
73    feature = "doc-images",
74    doc = ::embed_doc_image::embed_image!(
75    "network_relay_p2p",
76    "../resources/network_relay_p2p.png"
77    )
78)]
79//! ![Network Architecture w/ relay P2P][network_relay_p2p]
80//!
81//!
82//! # Executor Architecture: The [`NetKernel`]
83#![cfg_attr(
84    feature = "doc-images",
85    doc = ::embed_doc_image::embed_image!(
86        "proto_kernel_iface",
87        "../resources/proto_kernel_iface.png"
88    )
89)]
90//! ![Protocol/Executor/NetKernel Architecture][proto_kernel_iface]
91//! 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,
92//! they are sent to the [`NetKernel`]. The [`NetKernel`] is where your application logic must be written.
93//!
94//! ## Initialization Stage: The [`KernelExecutor`] and the [`NodeRemote`]
95//! 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
96//! 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.
97//!
98//! ## Passive Stage
99//! 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,
100//! 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
101//! 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.
102//!
103//! 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``
104//!
105//! ## Shutdown stage
106//! 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
107//! to the initial awaited call site on the node. Execution is complete, returning the initial kernel on success
108//!
109//! # Examples
110//!
111//! ## Server
112//! 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:
113//! ```
114//! use citadel_sdk::prelude::*;
115//! use citadel_sdk::prefabs::server::empty::EmptyKernel;
116//!
117//! // this server will listen on 127.0.0.1:25021, and will use the built-in defaults. When calling 'build', a NetKernel is specified
118//! let server = DefaultNodeBuilder::default()
119//! .with_node_type(NodeType::server("127.0.0.1:25021")?)
120//! .build(EmptyKernel::default())?;
121//!
122//! // await the server to execute
123//! # async move {
124//! let result = server.await;
125//! # };
126//! # Ok::<(), Box<dyn std::error::Error>>(())
127//! ```
128//!
129//! ## Client/Peer
130//! 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
131//! ```
132//! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel;
133//! use futures::StreamExt;
134//! use citadel_sdk::prelude::*;
135//!
136//! let server_connection_settings = DefaultServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?;
137//!
138//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |conn| async move {
139//!     // handle program logic here
140//!     let (sink, mut stream) = conn.split();
141//!     while let Some(message) = stream.next().await {
142//!         // message received in the form of a SecBuffer (memory-protected)
143//!     }
144//!
145//!     Ok(())
146//! });
147//!
148//! let client = DefaultNodeBuilder::default().build(client_kernel)?;
149//! # async move {
150//! let result = client.await;
151//! # };
152//! # Ok::<(), Box<dyn std::error::Error>>(())
153//! ```
154//!
155//! ## Remote Encrypted Virtual Filesystem (RE-VFS)
156//! The RE-VFS allows clients, servers, and peers to treat each other as remote endpoints for encrypted file storage.
157//! Since encrypting data locally using a symmetric key poses a vulnerability if the local node is compromised, The
158//! Citadel Protocol solves this issue by using a local 1024-Kyber public key to encrypt the data (via Kyber Hybrid Encryption for
159//! keeping the data size to a minimum), then, sending the contents to the adjacent endpoint. By doing this, the private decryption
160//! key and the contents are kept separate, forcing the hacker to compromise both endpoints.
161//!
162//! In order to use the RE-VFS, both endpoints must use the Filesystem backend. Second, the endpoint serving as a storage point
163//! must accept the inbound file transfer requests, otherwise, the transfer will fail. The example below for the receiving endpoint
164//! shows how to auto-accept inbound file transfer requests
165//!
166//! # Examples
167//!
168//! ## Receiving endpoint
169//! ```
170//! use citadel_sdk::prelude::*;
171//! use citadel_sdk::prefabs::server::accept_file_transfer_kernel::AcceptFileTransferKernel;
172//!
173//! // this server will listen on 127.0.0.1:25021, and will use the built-in defaults with a kernel
174//! // that auto-accepts inbound file transfer requests
175//! let server = DefaultNodeBuilder::default()
176//! .with_node_type(NodeType::server("127.0.0.1:25021")?)
177//! .build(AcceptFileTransferKernel::default())?;
178//!
179//! // await the server to execute
180//! # async move {
181//!     let result = server.await;
182//! # };
183//! # Ok::<(), Box<dyn std::error::Error>>(())
184//! ```
185//!
186//! ## Sending endpoint
187//! ```
188//! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel;
189//! use futures::StreamExt;
190//! use citadel_sdk::prelude::*;
191//!
192//! let server_connection_settings = DefaultServerConnectionSettingsBuilder::credentialed_registration("127.0.0.1:25021", "john.doe", "John Doe", "password").build()?;
193//!
194//! let client_kernel = SingleClientServerConnectionKernel::new(server_connection_settings, |conn| async move {
195//!     let virtual_path = "/home/virtual_user/output.pdf";
196//!     // write the contents with reinforced security.
197//!     citadel_sdk::fs::write_with_security_level(&conn.remote, "../path/to/input.pdf", SecurityLevel::Reinforced, virtual_path).await?;
198//!     // read the contents. Reading downloads the file to a local path
199//!     let stored_local_path = citadel_sdk::fs::read(&conn.remote, virtual_path).await?;
200//!  
201//!     Ok(())
202//! });
203//!
204//! let client = DefaultNodeBuilder::default().build(client_kernel)?;
205//! # async move {
206//!     let result = client.await;
207//! # };
208//! # Ok::<(), Box<dyn std::error::Error>>(())
209//! ```
210//!
211//! [`UdpChannel`]: crate::prelude::UdpChannel
212//! [`NetKernel`]: crate::prelude::NetKernel
213//! [`NetKernel::load_remote`]: crate::prelude::NetKernel::load_remote
214//! [`NetKernel::on_start`]: crate::prelude::NetKernel::on_start
215//! [`NetKernel::on_node_event_received`]: crate::prelude::NetKernel::on_node_event_received
216//! [`NetKernel::on_stop`]: crate::prelude::NetKernel::on_stop
217//! [`KernelExecutor`]: crate::prelude::KernelExecutor
218//! [`NodeRemote`]: crate::prelude::NodeRemote
219//! [`NodeRemote::shutdown`]: crate::prelude::NodeRemote::shutdown
220//! [`NodeType`]: crate::prelude::NodeType
221//! [`NodeType::Peer`]: crate::prelude::NodeType::Peer
222//! [`PeerConnectionType`]: crate::prelude::PeerConnectionType
223//! [`TargetLockedRemote`]: crate::prelude::TargetLockedRemote
224//! [`PersistenceHandler`]: crate::prelude::PersistenceHandler
225//! [`SecurityLevel`]: crate::prelude::SecurityLevel
226//! [`ObjectSource`]: crate::prelude::ObjectSource
227//! [`NetworkError`]: crate::prelude::NetworkError
228#![forbid(unsafe_code)]
229#![allow(rustdoc::invalid_html_tags)]
230#![deny(
231    clippy::cognitive_complexity,
232    trivial_numeric_casts,
233    unused_extern_crates,
234    unused_import_braces,
235    variant_size_differences,
236    unused_features,
237    unused_results
238)]
239
240/// Convenience import for building applications
241pub mod prelude {
242    pub use crate::backend_kv_store::BackendHandler;
243    pub use crate::builder::node_builder::*;
244    pub use crate::prefabs::client::peer_connection::PeerConnectionSetupAggregator;
245    pub use crate::prefabs::client::PrefabFunctions;
246    pub use crate::prefabs::client::{
247        DefaultServerConnectionSettingsBuilder, ServerConnectionSettings,
248    };
249    pub use crate::prefabs::ClientServerRemote;
250    pub use crate::remote_ext::remote_specialization::PeerRemote;
251    pub use crate::remote_ext::user_ids::*;
252    pub use crate::remote_ext::*;
253    pub use crate::responses;
254    pub use citadel_proto::prelude::*;
255    pub use citadel_types::prelude::*;
256}
257
258/// Store data to the backend using this library
259pub mod backend_kv_store;
260mod builder;
261/// Convenience functions for interacting with the remote encrypted virtual filesystem (RE-VFS)
262pub mod fs;
263/// The prefabs module contains pre-built kernels for common use cases.
264pub mod prefabs;
265/// Extension implementations endowed upon the [NodeRemote](crate::prelude::NodeRemote)
266pub mod remote_ext;
267/// For easy construction of replies to common message types
268pub mod responses;
269#[doc(hidden)]
270pub mod test_common;
271
272#[macro_use]
273pub(crate) mod macros;
274
275pub use citadel_logging as logging;
276/// Convenience for SDK users
277pub use citadel_proto::prelude::async_trait;
278pub use citadel_types as types;