├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── Nautilus-Benchmark-Analysis.ipynb └── README.md ├── core ├── .gitignore ├── Cargo.toml ├── src │ ├── event_bus.rs │ ├── lib.rs │ └── traits │ │ ├── connection │ │ ├── connection_error.rs │ │ ├── connection_event.rs │ │ ├── connection_traits.rs │ │ ├── datagram_trait.rs │ │ ├── framing.rs │ │ ├── framing │ │ │ ├── backpressure_framing.rs │ │ │ ├── delimiter.rs │ │ │ ├── length_prefixed.rs │ │ │ ├── length_prefixed_with_checksum.rs │ │ │ └── streaming_frame.rs │ │ ├── middleware_traits.rs │ │ ├── mod.rs │ │ └── transport_trait.rs │ │ └── mod.rs └── tests │ └── connection_tests.rs ├── docs └── CONTRIBUTING.md ├── identity ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── benches │ ├── benchmark.rs │ └── pki_benchmark │ │ ├── keypair_exchange_benchmark.rs │ │ ├── keypair_generation_benchmark.rs │ │ ├── keypair_serialization_benchmark.rs │ │ ├── keypair_throughput_benchmark.rs │ │ └── keypair_verify_sign_benchmark.rs ├── build.rs ├── decentralized_identity │ ├── Cargo.toml │ ├── build.rs │ ├── examples │ │ ├── decentralized_identity.rs │ │ └── decentralized_vc_example.rs │ ├── src │ │ ├── credential_issuance.rs │ │ ├── did.rs │ │ ├── did_document.rs │ │ ├── identity_error.rs │ │ ├── identity_flow.rs │ │ ├── identity_mgmt.rs │ │ ├── key_mgmt.rs │ │ ├── lib.rs │ │ ├── pki_factory.rs │ │ └── vc.rs │ └── tests │ │ ├── did_document_test.rs │ │ ├── key_mgmt_test.rs │ │ ├── user_document_test.rs │ │ └── vc_test.rs ├── examples │ └── identity_example.rs ├── key-storage │ ├── Cargo.toml │ └── src │ │ ├── cloud_storage.rs │ │ ├── cloud_storage │ │ └── amazon_s3_storage.rs │ │ ├── file_format │ │ ├── json_formatter.rs │ │ ├── mod.rs │ │ └── pem_formatter.rs │ │ ├── file_format_trait.rs │ │ ├── file_storage.rs │ │ ├── in_memory_key_storage.rs │ │ ├── key_storage_error.rs │ │ ├── key_storage_trait.rs │ │ ├── lib.rs │ │ ├── linux_storage.rs │ │ ├── linux_storage │ │ └── linux_keyring_storage.rs │ │ ├── windows_storage.rs │ │ └── windows_storage │ │ ├── windows_key_ring_storage.rs │ │ └── windows_tsm_storage.rs ├── src │ ├── cipher_suite.rs │ ├── key_exchange.rs │ ├── key_serde_trait.rs │ ├── lib.rs │ ├── pki │ │ ├── dilithium_keypair.rs │ │ ├── ecdsa_keypair.rs │ │ ├── ed25519_keypair.rs │ │ ├── falcon_keypair.rs │ │ ├── kyber_keypair.rs │ │ ├── mod.rs │ │ ├── rsa_keypair.rs │ │ ├── secp256k1_keypair.rs │ │ └── spincs_keypair.rs │ ├── pki_error.rs │ └── pki_trait.rs └── tests │ ├── dilithium_test.rs │ ├── ecdsa_test.rs │ ├── ed25519_test.rs │ ├── falcon_test.rs │ ├── kyber_keyexchange_test.rs │ ├── rsa_test.rs │ ├── secp256k1_test.rs │ └── spincs_test.rs ├── protocols ├── handshake │ ├── Cargo.toml │ ├── README.md │ ├── examples │ │ └── handshake_testing.rs │ ├── src │ │ ├── handshake.rs │ │ ├── handshake_error.rs │ │ ├── lib.rs │ │ ├── steps.rs │ │ └── traits.rs │ └── tests │ │ ├── basic_test.rs │ │ ├── edge_cases.rs │ │ └── integeration_test.rs ├── kad │ ├── Cargo.toml │ ├── README.md │ ├── examples │ │ └── bootstrap_service.rs │ ├── src │ │ ├── behaviour │ │ │ ├── behaviour.rs │ │ │ ├── learning.rs │ │ │ ├── maintenance.rs │ │ │ ├── messaging.rs │ │ │ └── mod.rs │ │ ├── bootstrap.rs │ │ ├── kad_message.rs │ │ ├── kad_protocol.rs │ │ ├── lib.rs │ │ ├── node.rs │ │ ├── routing_table.rs │ │ ├── utils.rs │ │ └── xor_distance.rs │ └── tests │ │ └── basic_tests.rs ├── mdns │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── examples │ │ └── mdns_example.rs │ ├── src │ │ ├── behaviour.rs │ │ ├── behaviour │ │ │ ├── mdns_error.rs │ │ │ ├── mdns_event.rs │ │ │ ├── mdns_service.rs │ │ │ └── records │ │ │ │ ├── mdns_records.rs │ │ │ │ ├── mdns_registry.rs │ │ │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── name.rs │ │ ├── packet.rs │ │ └── record.rs │ └── tests │ │ └── mdns_service_tests.rs ├── negotiation │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ ├── context.rs │ │ ├── lib.rs │ │ ├── negotiation.rs │ │ ├── negotiation_error.rs │ │ ├── negotiation_strategy.rs │ │ └── traits.rs │ └── tests │ │ └── negotiation_strategy_test.rs ├── ping │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ └── peer.rs │ └── tests │ │ ├── basic_test.rs │ │ ├── integration_test.rs │ │ └── stress_test.rs └── tls │ ├── Cargo.toml │ ├── examples │ └── tls_session_example.rs │ └── src │ ├── connection.rs │ ├── handshake.rs │ ├── lib.rs │ ├── record.rs │ ├── tls_session.rs │ └── tls_state.rs ├── security ├── authentication │ ├── Cargo.toml │ ├── src │ │ ├── cmac_auth.rs │ │ ├── hash_chain.rs │ │ ├── hmac_auth.rs │ │ ├── lib.rs │ │ └── traits.rs │ └── tests │ │ └── basic_auth_test.rs └── data_encryption │ ├── .gitignore │ ├── Cargo.toml │ ├── examples │ └── tcp_stream_encryption.rs │ ├── src │ ├── encryption.rs │ ├── encryption │ │ ├── aes_symmetric.rs │ │ ├── blowfish_symmetric.rs │ │ ├── chacha20_symmetric.rs │ │ └── des_symmetric.rs │ ├── encryption_error.rs │ ├── encryption_trait.rs │ ├── key_derivation_trait.rs │ ├── key_derive │ │ ├── argon2_key_derive.rs │ │ ├── mod.rs │ │ ├── pbkdf2_key_derive.rs │ │ └── scrypt_key_derive.rs │ ├── lib.rs │ ├── stream_encryption_trait.rs │ └── utils.rs │ └── tests │ ├── 3des_encryption_test.rs │ ├── aes_encryption_test.rs │ ├── argon2_derive_test.rs │ ├── blwfish_encryption_test.rs │ ├── chacha20_encryption_test.rs │ ├── pbkdf_derive_test.rs │ ├── scrypt_derive_test.rs │ └── symmetric_suite_test.rs ├── src └── main.rs ├── transport ├── tcp │ ├── .gitignore │ ├── Cargo.toml │ ├── examples │ │ └── tcp_conn_example.rs │ ├── src │ │ ├── lib.rs │ │ ├── tcp_conn.rs │ │ └── tcp_events.rs │ └── tests │ │ └── tcp_conn_framing_test.rs └── udp │ ├── Cargo.toml │ ├── examples │ └── udp_example.rs │ └── src │ ├── lib.rs │ └── udp_conn.rs └── utilities ├── certificate_parser ├── Cargo.toml ├── src │ ├── cert_util_trait.rs │ ├── certificate_builder.rs │ ├── certificate_parsing.rs │ ├── certificate_parsing_erorr.rs │ ├── certificate_type.rs │ ├── lib.rs │ └── utils │ │ ├── der_utils.rs │ │ ├── mod.rs │ │ └── pem_utils.rs └── test_assets │ ├── ecdsa_cert.der │ ├── ecdsa_cert.pem │ ├── ecdsa_csr.pem │ ├── ecdsa_key.pem │ ├── rsa_cert.der │ ├── rsa_cert.p12 │ ├── rsa_cert.pem │ ├── rsa_csr.pem │ └── rsa_key.pem └── registry ├── .gitignore ├── Cargo.toml ├── examples └── registry_example.rs ├── src ├── data_source_traits.rs ├── hashring_shard.rs ├── lib.rs ├── record_trait.rs ├── registry_mods │ ├── in_memory_registry.rs │ ├── mod.rs │ └── redis_registry.rs ├── registry_record_error.rs └── registry_traits.rs └── tests ├── in_memory_testing.rs ├── redis_testing.rs ├── testing_hashring_shard.rs └── testing_pipeline.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "Nautilus/identity/src/**" # Trigger only if changes are in this directory 7 | branches: 8 | - "master" # Only for PRs targeting the master branch 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | identity-build: 15 | name: Identity Build 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Build in Workspace 21 | run: | 22 | cd Nautilus/identity 23 | cargo build --verbose --all-features 24 | 25 | identity-test: 26 | name: Identity Test 27 | runs-on: ubuntu-latest 28 | needs: identity-build # Ensure this runs after the build job 29 | 30 | steps: 31 | - uses: actions/checkout@v4 32 | - name: Test with Specific Features 33 | run: | 34 | cd Nautilus/identity 35 | cargo test --verbose --features ecdsa,ed25519,secp256k1,dilithium,kyber,falcon 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | tmp 4 | benches/*.csv -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Nautilus" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | 9 | [workspace] 10 | members = [ 11 | "identity", # Existing identity crate 12 | "identity/key-storage", # Key-storage crate inside identity 13 | "identity/decentralized_identity", 14 | "security/data_encryption", # Symmetric Encryption and Key-Derivation 15 | "utilities/registry", # Generic Registry module Utilized for Backend Storage for records 16 | "protocols/mdns", # Decentralized Device/Service Discovery Protocol [Primary Protocol Used for Discovering Peers] 17 | "core", 18 | "transport/tcp", 19 | "utilities/certificate_parser", 20 | "protocols/negotiation", 21 | "security/authentication", 22 | "protocols/handshake", 23 | "transport/udp", "protocols/tls", "protocols/ping", "protocols/kad", 24 | ] 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🐚 Nautilus 2 | 3 | **Nautilus** is a **Rust-based** framework for developing **decentralized networks** and **secure communication systems**. It emphasizes **modularity, scalability, and privacy**, making it an ideal choice for exploring and building innovative network architectures. 4 | 5 | 🚀 **Goal:** 6 | To explore and develop a **modular structure** and frameworks aimed at experimentation and providing **Proof-of-Concept (PoC)** solutions for **future-proofing** decentralized systems. 7 | 8 | --- 9 | 10 | ## ✨ Features 11 | 12 | Nautilus provides a **rich set of features**, focusing on **privacy, security, and modularity**, empowering developers to build **next-generation decentralized solutions**. 13 | 14 | - 🆔 **Decentralized Identity (DID):** 15 | Create and manage decentralized identifiers and verifiable credentials. 16 | 17 | - 🔑 **Public Key Infrastructure (PKI):** 18 | Secure communication with modular PKI components. 19 | 20 | - 🔍 **Service Discovery:** 21 | Efficient discovery of services and devices in a decentralized environment. 22 | 23 | - 🔒 **Custom TLS with PQC:** 24 | Implementation of **Post-Quantum Cryptographic (PQC)** algorithms for secure identity-based transactions and **TLS encryption**. 25 | 26 | - 🧩 **Modular Architecture:** 27 | A **plug-and-play** architecture enabling each protocol/crate to be used independently in various scenarios. 28 | 29 | --- 30 | 31 | ## 📝 Take Note Of 32 | 33 | Nautilus consists of **various Rust crates**, each serving as a **building block** that can be used **independently** or **together**. Each crate includes several important folders that developers should pay attention to: 34 | 35 | - 📂 **`examples/` – Practical Usage Examples:** 36 | - Contains simple, ready-to-run examples demonstrating how to utilize the protocol. 37 | - **Tip:** Start here to get a quick understanding of implementation. 38 | 39 | - 🧪 **`tests/` – Unit & Integration Tests:** 40 | - Contains test cases that showcase how functions are used. 41 | - **Tip:** Great for understanding function usage and expected outcomes. 42 | 43 | - ⚡ **`benchmarks/` – Performance Insights:** 44 | - Includes benchmarking tests to measure protocol performance, especially useful when dealing with cryptographic algorithms. 45 | - **Tip:** Use this to analyze efficiency and scalability under different conditions. 46 | 47 | ## Contact: 48 | 49 | For any questions or suggestions, feel free to open an issue on the repository or reach out to us. We hope Nautilus inspires your journey in decentralized networking! 🌐 -------------------------------------------------------------------------------- /benches/README.md: -------------------------------------------------------------------------------- 1 | # Benchmarks Overview 2 | 3 | ## Introduction 4 | 5 | This folder contains the benchmark results for the crates in Nautilus. The benchmark tests are designed to measure the performance of various storage and service discovery operations, including memory usage, execution time, and efficiency across different configurations. 6 | 7 | ## Structure 8 | Each CSV will have varying columns in the CSV, but there are some common elements to take note of 9 | 10 | | Column Name | Description | 11 | |----------------|---------------------------------------------------------------| 12 | | `set_no` | The benchmark iteration or test batch number | 13 | | `iteration` | The iteration count within a benchmark set | 14 | | `method` | The method or approach used for storage (e.g., memory, file) | 15 | | `time_taken` | Time taken for the operation in nanoseconds | 16 | | `memory_usage` | Memory consumption during the operation in bytes | 17 | 18 | ## Usage 19 | 20 | To utilize this folder, go to any of the crates that contain benchmark tests and run : 21 | ```rust 22 | cargo bench --all-features 23 | 24 | ``` 25 | This will load in the CSV files into the `Nautilus/benches`. A Jupyton notebook will be provided for data analysis and produce charts for easier reference to technical developers. -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nautilus_core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-trait = {version = "0.1.85"} 8 | tokio = {version = "1.0.0",features = ["full"]} 9 | crc32fast = {version = "1.4.2"} 10 | futures = {version = "0.3.31"} -------------------------------------------------------------------------------- /core/src/event_bus.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::broadcast; 2 | use std::fmt::Debug; 3 | 4 | #[derive(Clone, Debug)] 5 | pub struct EventBus { 6 | sender: broadcast::Sender, 7 | } 8 | 9 | impl EventBus { 10 | /// Creates a new EventBus with the specified buffer size. 11 | pub fn new(buffer_size: usize) -> Self { 12 | let (sender, _) = broadcast::channel(buffer_size); 13 | Self { sender } 14 | } 15 | 16 | /// Subscribes to the EventBus, receiving a `Receiver` to listen for events. 17 | pub fn subscribe(&self) -> broadcast::Receiver { 18 | self.sender.subscribe() 19 | } 20 | 21 | /// Publishes an event to all subscribers. 22 | pub async fn publish(&self, event: T) { 23 | let _ = self.sender.send(event); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod traits; 2 | 3 | pub mod event_bus; 4 | pub use traits::*; -------------------------------------------------------------------------------- /core/src/traits/connection/connection_error.rs: -------------------------------------------------------------------------------- 1 | // nautilus_core_proto\src\traits\connection_error_traits.rs 2 | use std::fmt; 3 | 4 | 5 | #[derive(Debug, Clone)] 6 | pub enum ConnectionError { 7 | ConnectionFailed(String), 8 | SendFailed(String), 9 | ReceiveFailed(String), 10 | BindFailed(String), 11 | Generic(String), 12 | } 13 | 14 | impl fmt::Display for ConnectionError { 15 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 16 | match self { 17 | ConnectionError::ConnectionFailed(msg) => write!(f, "Connection failed: {}", msg), 18 | ConnectionError::SendFailed(msg) => write!(f, "Send failed: {}", msg), 19 | ConnectionError::ReceiveFailed(msg) => write!(f, "Receive failed: {}", msg), 20 | ConnectionError::BindFailed(msg) => write!(f, "Bind failed: {}", msg), 21 | ConnectionError::Generic(msg) => write!(f, "Error: {}", msg), 22 | } 23 | } 24 | } 25 | 26 | impl std::error::Error for ConnectionError {} 27 | 28 | impl From for ConnectionError { 29 | fn from(err: std::io::Error) -> Self { 30 | ConnectionError::Generic(err.to_string()) 31 | } 32 | } -------------------------------------------------------------------------------- /core/src/traits/connection/connection_event.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\connection_event.rs 2 | #[derive(Debug, Clone)] 3 | pub enum ConnectionEvent { 4 | Connected { peer: String }, 5 | Disconnected { peer: String }, 6 | Error { peer: String, error: String }, 7 | } -------------------------------------------------------------------------------- /core/src/traits/connection/connection_traits.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\connection_traits.rs 2 | use async_trait::async_trait; 3 | 4 | #[async_trait] 5 | pub trait Connection: Send + Sync { 6 | type Error: std::error::Error + Send + Sync + 'static; 7 | 8 | /// Establishes a connection to a remote address. 9 | async fn connect(&mut self, addr: &str) -> Result<(), Self::Error>; 10 | 11 | /// Disconnects the current connection. 12 | async fn disconnect(&mut self) -> Result<(), Self::Error>; 13 | 14 | /// Sends data over the connection. 15 | async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error>; 16 | 17 | /// Receives data from the connection. 18 | async fn receive(&mut self) -> Result, Self::Error>; 19 | 20 | /// Checks if the connection is active. 21 | fn is_connected(&self) -> bool; 22 | } 23 | -------------------------------------------------------------------------------- /core/src/traits/connection/datagram_trait.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\datagram_trait.rs 2 | use async_trait::async_trait; 3 | 4 | #[async_trait] 5 | pub trait Datagram: Send + Sync { 6 | type Error: std::error::Error + Send + Sync + 'static; 7 | 8 | /// Binds to a local address for receiving datagrams. 9 | async fn bind(&self, addr: &str) -> Result<(), Self::Error>; 10 | 11 | /// Sends a datagram to a remote address. 12 | async fn send_to(&self, data: &[u8], addr: &str) -> Result<(), Self::Error>; 13 | 14 | /// Receives a datagram from any remote address. 15 | async fn receive_from(&self) -> Result<(Vec, String), Self::Error>; 16 | } 17 | -------------------------------------------------------------------------------- /core/src/traits/connection/framing.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\framing 2 | use std::fmt; 3 | 4 | pub trait Framing { 5 | /// Encodes the raw data into a framed message 6 | fn encode(&self, data: &[u8]) -> Vec; 7 | 8 | /// Decodes a framed message from the raw buffer 9 | /// Returns the decoded frame and the number of bytes consumed 10 | fn decode(&self, buf: &[u8]) -> Result<(Vec, usize), FramingError>; 11 | } 12 | 13 | #[derive(Debug)] 14 | pub enum FramingError { 15 | IncompleteFrame, 16 | InvalidFrame, 17 | ChecksumMismatch, 18 | Other(String), 19 | } 20 | 21 | 22 | 23 | impl fmt::Display for FramingError { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | match self { 26 | FramingError::IncompleteFrame => write!(f, "Incomplete frame"), 27 | FramingError::InvalidFrame => write!(f, "Invalid frame"), 28 | FramingError::ChecksumMismatch => write!(f, "Checksum mismatch"), 29 | FramingError::Other(msg) => write!(f, "{}", msg), 30 | } 31 | } 32 | } 33 | 34 | impl std::error::Error for FramingError {} 35 | 36 | mod length_prefixed; 37 | mod delimiter; 38 | mod length_prefixed_with_checksum; 39 | mod streaming_frame; 40 | mod backpressure_framing; 41 | 42 | pub use streaming_frame::StreamingFraming; 43 | pub use backpressure_framing::BackpressureFraming; -------------------------------------------------------------------------------- /core/src/traits/connection/framing/backpressure_framing.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::Semaphore; 2 | use std::sync::Arc; 3 | use super::{Framing, FramingError}; 4 | 5 | pub struct BackpressureFraming { 6 | semaphore: Arc, 7 | } 8 | 9 | impl BackpressureFraming { 10 | pub fn new(max_concurrent_frames: usize) -> Self { 11 | Self { 12 | semaphore: Arc::new(Semaphore::new(max_concurrent_frames)), 13 | } 14 | } 15 | } 16 | 17 | impl Framing for BackpressureFraming { 18 | fn encode(&self, data: &[u8]) -> Vec { 19 | let mut framed = (data.len() as u32).to_be_bytes().to_vec(); 20 | framed.extend_from_slice(data); 21 | framed 22 | } 23 | 24 | fn decode(&self, buf: &[u8]) -> Result<(Vec, usize), FramingError> { 25 | // Use a synchronous block for the semaphore acquisition 26 | let permit = futures::executor::block_on(self.semaphore.acquire()); 27 | 28 | if permit.is_err() { 29 | return Err(FramingError::Other("Backpressure limit reached".to_string())); 30 | } 31 | 32 | if buf.len() < 4 { 33 | return Err(FramingError::IncompleteFrame); 34 | } 35 | let length = u32::from_be_bytes(buf[..4].try_into().unwrap()) as usize; 36 | if buf.len() < 4 + length { 37 | return Err(FramingError::IncompleteFrame); 38 | } 39 | 40 | Ok((buf[4..4 + length].to_vec(), 4 + length)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/traits/connection/framing/delimiter.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\framing\delimiter.rs 2 | use super::{Framing, FramingError}; 3 | 4 | pub struct DelimiterFraming { 5 | delimiter: u8, 6 | } 7 | 8 | impl DelimiterFraming { 9 | #[allow(dead_code)] 10 | pub fn new(delimiter: u8) -> Self { 11 | Self { delimiter } 12 | } 13 | } 14 | 15 | impl Framing for DelimiterFraming { 16 | fn encode(&self, data: &[u8]) -> Vec { 17 | let mut framed = data.to_vec(); 18 | framed.push(self.delimiter); 19 | framed 20 | } 21 | 22 | fn decode(&self, buf: &[u8]) -> Result<(Vec, usize), FramingError> { 23 | if let Some(pos) = buf.iter().position(|&x| x == self.delimiter) { 24 | Ok((buf[..pos].to_vec(), pos + 1)) 25 | } else { 26 | Err(FramingError::IncompleteFrame) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /core/src/traits/connection/framing/length_prefixed.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\framing\length_prefixed.rs 2 | use super::{Framing, FramingError}; 3 | 4 | pub struct LengthPrefixed; 5 | 6 | impl Framing for LengthPrefixed { 7 | fn encode(&self, data: &[u8]) -> Vec { 8 | let mut framed = (data.len() as u32).to_be_bytes().to_vec(); 9 | framed.extend_from_slice(data); 10 | framed 11 | } 12 | 13 | fn decode(&self, buf: &[u8]) -> Result<(Vec, usize), FramingError> { 14 | if buf.len() < 4 { 15 | return Err(FramingError::IncompleteFrame); 16 | } 17 | let length = u32::from_be_bytes(buf[..4].try_into().unwrap()) as usize; 18 | if buf.len() < 4 + length { 19 | return Err(FramingError::IncompleteFrame); 20 | } 21 | Ok((buf[4..4 + length].to_vec(), 4 + length)) 22 | } 23 | } 24 | 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use crate::traits::connection::framing::Framing; 29 | use super::LengthPrefixed; 30 | 31 | #[test] 32 | fn test_encode() { 33 | let protocol = LengthPrefixed; 34 | let data = b"Hello"; 35 | let framed = protocol.encode(data); 36 | assert_eq!(&framed[..4], &(data.len() as u32).to_be_bytes()); 37 | assert_eq!(&framed[4..], data); 38 | } 39 | 40 | #[test] 41 | fn test_decode() { 42 | let protocol = LengthPrefixed; 43 | let data = b"Hello"; 44 | let framed = protocol.encode(data); 45 | let (decoded, consumed) = protocol.decode(&framed).unwrap(); 46 | assert_eq!(decoded, data); 47 | assert_eq!(consumed, framed.len()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/traits/connection/framing/length_prefixed_with_checksum.rs: -------------------------------------------------------------------------------- 1 | 2 | // core/src/traits/connection/framing/length_prefixed_with_checksum.rs 3 | use super::{Framing, FramingError}; 4 | use crc32fast::Hasher; 5 | 6 | pub struct LengthPrefixedWithChecksum; 7 | 8 | impl Framing for LengthPrefixedWithChecksum { 9 | fn encode(&self, data: &[u8]) -> Vec { 10 | let mut hasher = Hasher::new(); 11 | hasher.update(data); 12 | let checksum = hasher.finalize(); 13 | 14 | let mut framed = (data.len() as u32).to_be_bytes().to_vec(); 15 | framed.extend_from_slice(&checksum.to_be_bytes()); 16 | framed.extend_from_slice(data); 17 | framed 18 | } 19 | 20 | fn decode(&self, buf: &[u8]) -> Result<(Vec, usize), FramingError> { 21 | if buf.len() < 8 { 22 | return Err(FramingError::IncompleteFrame); 23 | } 24 | let length = u32::from_be_bytes(buf[..4].try_into().unwrap()) as usize; 25 | let checksum = u32::from_be_bytes(buf[4..8].try_into().unwrap()); 26 | 27 | if buf.len() < 8 + length { 28 | return Err(FramingError::IncompleteFrame); 29 | } 30 | 31 | let data = &buf[8..8 + length]; 32 | let mut hasher = Hasher::new(); 33 | hasher.update(data); 34 | 35 | if hasher.finalize() != checksum { 36 | return Err(FramingError::ChecksumMismatch); 37 | } 38 | 39 | Ok((data.to_vec(), 8 + length)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/traits/connection/framing/streaming_frame.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\framing\streaming_frame.rs 2 | pub struct StreamingFraming; 3 | use super::Framing; 4 | use super::FramingError; 5 | 6 | impl Framing for StreamingFraming { 7 | fn encode(&self, data: &[u8]) -> Vec { 8 | let mut framed = Vec::new(); 9 | for chunk in data.chunks(1024) { 10 | framed.extend_from_slice(&(chunk.len() as u32).to_be_bytes()); 11 | framed.extend_from_slice(chunk); 12 | } 13 | framed 14 | } 15 | 16 | fn decode(&self, buf: &[u8]) -> Result<(Vec, usize), FramingError> { 17 | if buf.len() < 4 { 18 | return Err(FramingError::IncompleteFrame); 19 | } 20 | let length = u32::from_be_bytes(buf[..4].try_into().unwrap()) as usize; 21 | if buf.len() < 4 + length { 22 | return Err(FramingError::IncompleteFrame); 23 | } 24 | Ok((buf[4..4 + length].to_vec(), 4 + length)) 25 | } 26 | } -------------------------------------------------------------------------------- /core/src/traits/connection/middleware_traits.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\middleware_traits.rs 2 | 3 | /// Middleware trait that wraps connections 4 | pub trait Middleware: Send + Sync { 5 | type Wrapped; 6 | 7 | fn wrap_connection(&self, connection: C) -> Self::Wrapped; 8 | } 9 | 10 | /// MiddlewareConnection trait that defines basic connection operations 11 | pub trait MiddlewareConnection: Send + Sync { 12 | fn send(&mut self, data: &[u8]) -> Result<(), String>; 13 | fn receive(&mut self) -> Result, String>; 14 | fn close(&mut self) -> Result<(), String>; 15 | } 16 | 17 | /// MiddlewareStack to manage and compose multiple middleware layers 18 | pub struct MiddlewareStack { 19 | middlewares: Vec>>, 20 | } 21 | 22 | impl MiddlewareStack { 23 | pub fn new() -> Self { 24 | MiddlewareStack { 25 | middlewares: Vec::new(), 26 | } 27 | } 28 | 29 | pub fn add + 'static>(&mut self, middleware: M) { 30 | self.middlewares.push(Box::new(middleware)); 31 | } 32 | 33 | pub fn wrap(&self, connection: C) -> C { 34 | let mut wrapped = connection; 35 | for middleware in &self.middlewares { 36 | wrapped = middleware.wrap_connection(wrapped); 37 | } 38 | wrapped 39 | } 40 | } -------------------------------------------------------------------------------- /core/src/traits/connection/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod connection_traits; 3 | mod datagram_trait; 4 | mod transport_trait; 5 | mod connection_error; 6 | mod connection_event; 7 | pub mod framing; 8 | // mod middleware_traits; 9 | // ============================= Public Interface ================================= 10 | pub use connection_traits::Connection; 11 | pub use datagram_trait::Datagram; 12 | pub use transport_trait::{Transport,TransportListener}; 13 | pub use connection_error::ConnectionError; 14 | pub use connection_event::ConnectionEvent; 15 | // pub use middleware_traits::{Middleware,MiddlewareConnection,MiddlewareStack}; -------------------------------------------------------------------------------- /core/src/traits/connection/transport_trait.rs: -------------------------------------------------------------------------------- 1 | // core\src\traits\connection\transport_trait.rs 2 | use async_trait::async_trait; 3 | #[async_trait] 4 | pub trait Transport: Send + Sync { 5 | type Connection: Send + Sync; 6 | type Listener: TransportListener; 7 | type Error: std::error::Error + Send + Sync + 'static; 8 | 9 | /// Starts listening on a specified address. 10 | async fn listen(&self, addr: &str) -> Result; 11 | 12 | /// Dials a remote address and establishes a connection. 13 | async fn dial(&self, addr: &str) -> Result; 14 | } 15 | /// repeatedly yields new connections when a peer connects. 16 | #[async_trait] 17 | pub trait TransportListener: Send + Sync { 18 | /// Accept the next inbound connection. 19 | async fn accept(&mut self) -> Result; 20 | } 21 | -------------------------------------------------------------------------------- /core/src/traits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod connection; 2 | -------------------------------------------------------------------------------- /core/tests/connection_tests.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use std::error::Error; 3 | use std::fmt; 4 | use nautilus_core::connection::Connection; 5 | 6 | // Define a custom error type 7 | #[derive(Debug)] 8 | struct MockError(String); 9 | 10 | impl fmt::Display for MockError { 11 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | write!(f, "{}", self.0) 13 | } 14 | } 15 | 16 | impl Error for MockError {} 17 | 18 | // Mock implementation of the `Connection` trait 19 | struct MockConnection { 20 | connected: bool, 21 | } 22 | 23 | #[async_trait] 24 | impl Connection for MockConnection { 25 | type Error = MockError; 26 | 27 | async fn connect(&mut self, _: &str) -> Result<(), Self::Error> { 28 | self.connected = true; 29 | Ok(()) 30 | } 31 | 32 | async fn disconnect(&mut self) -> Result<(), Self::Error> { 33 | self.connected = false; 34 | Ok(()) 35 | } 36 | 37 | async fn send(&mut self, _: &[u8]) -> Result<(), Self::Error> { 38 | if self.connected { 39 | Ok(()) 40 | } else { 41 | Err(MockError("Not connected".to_string())) 42 | } 43 | } 44 | 45 | async fn receive(&mut self) -> Result, Self::Error> { 46 | if self.connected { 47 | Ok(vec![1, 2, 3]) 48 | } else { 49 | Err(MockError("Not connected".to_string())) 50 | } 51 | } 52 | 53 | fn is_connected(&self) -> bool { 54 | self.connected 55 | } 56 | } 57 | 58 | #[tokio::test] 59 | async fn test_mock_connection() { 60 | let mut connection = MockConnection { connected: false }; 61 | 62 | // Test connection 63 | assert!(!connection.is_connected()); 64 | connection.connect("127.0.0.1").await.unwrap(); 65 | assert!(connection.is_connected()); 66 | 67 | // Test sending data 68 | connection.send(b"test data").await.unwrap(); 69 | 70 | // Test receiving data 71 | let data = connection.receive().await.unwrap(); 72 | assert_eq!(data, vec![1, 2, 3]); 73 | 74 | // Test disconnecting 75 | connection.disconnect().await.unwrap(); 76 | assert!(!connection.is_connected()); 77 | } -------------------------------------------------------------------------------- /identity/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /benches/benchmark_results -------------------------------------------------------------------------------- /identity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "identity" 3 | version = "0.1.0" 4 | edition = "2021" 5 | build = "build.rs" 6 | 7 | [dependencies] 8 | # Default Dependenecies 9 | rand_core = {version = "0.6",features = ["getrandom"]} 10 | serde_json = "1.0.135" 11 | serde = {version = "1.0.0", features = ["derive",]} 12 | serde_bytes = {version = "0.11.15"} 13 | 14 | # PKI Dependencies [Feature Flagged] 15 | rsa = {version = "0.9.6",features = ["sha2"], optional = true} # For RSA 16 | sha2 = {version = "0.10.1",optional = true} # For Hashing/RSA 17 | 18 | k256 = {version="0.13.4", optional = true,features = ["ecdh"]} # For secp256k1 19 | 20 | p256 = {version = "0.13.2",optional = true} # For ECDSA 21 | 22 | ed25519-dalek = {version = "2.1.1",optional = true} # For Ed25519 23 | curve25519-dalek = {version = "4.0.0",optional = true} 24 | 25 | fips203 = {version ="0.4.2",optional = true} # For Kyber 26 | 27 | fips204 = {version = "0.4.6",optional = true} # For Dilithium 28 | 29 | fips205 = {version = "0.4.1",optional = true} # For Spincs+ 30 | 31 | pqcrypto-falcon = {version = "0.4.0",optional = true} # For Falcon 32 | pqcrypto-traits = {version = "0.3.5",optional = true} # For Falcon 33 | 34 | 35 | [features] 36 | default = ["pki_rsa"] # default features 37 | pki_rsa = ["rsa","sha2"] # rsa features 38 | secp256k1 = ["k256"] # secp256k1 features 39 | ecdsa = ["p256","sha2"] # ecdsa features 40 | ed25519 = ["ed25519-dalek","curve25519-dalek"] # Enable Ed25519 support when this feature flag is specified 41 | dilithium = ["fips204"] # Enable Dilithium support when this feature flag is specified 42 | spincs = ["fips205"] # Enable Spincs+ support when this feature flag is specified 43 | falcon = ["pqcrypto-falcon","pqcrypto-traits"] # Not FIPS STANDARD, OFFICIAL RELEASE 44 | kyber = ["fips203","sha2"] # Kyber Implmentation for KEM and PKI Trait 45 | 46 | [[bench]] 47 | name = "benchmark" 48 | harness = false 49 | 50 | [dev-dependencies] 51 | criterion = "0.5.1" 52 | tokio = {version = "1.0.0",features = ["full"]} 53 | sysinfo = {version = "0.33.1"} -------------------------------------------------------------------------------- /identity/benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | // identity\benches\benchmark.rs 2 | /// Purpose: This module sets up and organizes cryptographic benchmarks 3 | /// using the Criterion benchmarking framework. It includes tests for keypair 4 | /// generation, signing, verification, serialization, and throughput performance. 5 | 6 | 7 | /// Declare the `pki_benchmark` module and its submodules. 8 | /// Each submodule corresponds to a specific benchmarking functionality. 9 | mod pki_benchmark { 10 | /// Benchmark for keypair generation across different cryptographic algorithms. 11 | pub mod keypair_generation_benchmark; 12 | 13 | /// Benchmark for measuring signing and verification times. 14 | pub mod keypair_verify_sign_benchmark; 15 | 16 | /// Benchmark for serialization and deserialization of keypairs. 17 | pub mod keypair_serialization_benchmark; 18 | 19 | /// Benchmark for measuring throughput in terms of signing and verification operations per second. 20 | pub mod keypair_throughput_benchmark; 21 | 22 | /// Benchmark for measuing Keypair exchange rates 23 | pub mod keypair_exchange_benchmark; 24 | } 25 | 26 | // Use `criterion_main` to define the entry point for the benchmarks. 27 | // This macro initializes the benchmark tests based on the selected feature flags. 28 | criterion::criterion_main!( 29 | // Runs the keypair generation benchmark. 30 | pki_benchmark::keypair_generation_benchmark::crypto_benchmarks, 31 | 32 | // Runs the signing and verification benchmark. 33 | pki_benchmark::keypair_verify_sign_benchmark::crypto_benchmarks, 34 | 35 | // Runs the serialization and deserialization benchmark. 36 | pki_benchmark::keypair_serialization_benchmark::crypto_benchmarks, 37 | 38 | // Runs the throughput benchmark (sign/verify operations per second). 39 | pki_benchmark::keypair_throughput_benchmark::crypto_benchmarks, 40 | 41 | // Run the Key Exchange benchmark 42 | pki_benchmark::keypair_exchange_benchmark::crypto_benchmarks, 43 | ); 44 | -------------------------------------------------------------------------------- /identity/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); 3 | 4 | if target_os == "windows" { 5 | println!("cargo:rustc-link-arg=/STACK:8388608"); 6 | } else if target_os == "linux" { 7 | println!("cargo:rustc-link-arg=-Wl,-z,stack-size=8388608"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /identity/decentralized_identity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decentralized_identity" 3 | version = "0.1.0" 4 | edition = "2021" 5 | build = "build.rs" 6 | [dependencies] 7 | serde = { version = "1.0", features = ["derive"] } 8 | serde_json = {version = "1.0"} 9 | chrono = {version = "0.4.39"} 10 | 11 | identity = { path = "../", optional = true , default-features = false} 12 | base64 = "0.22.1" 13 | uuid = { version = "1.10.0", features = ["v4"] } 14 | 15 | [features] 16 | default = ["pki_rsa"] 17 | pki_rsa = ["identity/pki_rsa"] 18 | ecdsa = ["identity/ecdsa"] 19 | ed25519 = ["identity/ed25519"] 20 | dilithium = ["identity/dilithium"] 21 | falcon = ["identity/falcon"] -------------------------------------------------------------------------------- /identity/decentralized_identity/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); 3 | 4 | if target_os == "windows" { 5 | println!("cargo:rustc-link-arg=/STACK:8388608"); 6 | } else if target_os == "linux" { 7 | println!("cargo:rustc-link-arg=-Wl,-z,stack-size=8388608"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /identity/decentralized_identity/examples/decentralized_identity.rs: -------------------------------------------------------------------------------- 1 | use decentralized_identity::{DIDDocument, KeyManager, Algorithm}; 2 | use uuid::Uuid; 3 | 4 | /// Generates a DID document for a specific algorithm. 5 | fn generate_did_for_algorithm( 6 | identity_suffix: &str, 7 | key_manager: &mut KeyManager, 8 | algorithm: Algorithm, 9 | ) -> Result { 10 | let key_id = format!("key-{}", Uuid::new_v4()); 11 | println!("Generating DIDDocument for algorithm: {:?}", algorithm); 12 | 13 | let did_document = DIDDocument::new_with_keys(identity_suffix, key_id, key_manager, algorithm) 14 | .map_err(|e| format!("Failed to create DIDDocument: {:?}", e))?; 15 | 16 | Ok(did_document) 17 | } 18 | 19 | fn main() { 20 | let mut key_manager = KeyManager::new(); 21 | let identity_suffix = "did:example"; 22 | 23 | // Test RSA (feature-gated) 24 | #[cfg(feature = "pki_rsa")] 25 | { 26 | if let Ok(did_document) = generate_did_for_algorithm(identity_suffix, &mut key_manager, Algorithm::RSA) { 27 | println!("Generated DIDDocument for RSA: {:#?}", did_document); 28 | } else { 29 | eprintln!("Failed to generate DIDDocument for RSA"); 30 | } 31 | println!("--------------------------------------------------"); 32 | } 33 | 34 | // Test Dilithium (feature-gated) 35 | #[cfg(feature = "dilithium")] 36 | { 37 | if let Ok(did_document) = generate_did_for_algorithm(identity_suffix, &mut key_manager, Algorithm::Dilithium) { 38 | println!("Generated DIDDocument for Dilithium: {:#?}", did_document); 39 | } else { 40 | eprintln!("Failed to generate DIDDocument for Dilithium"); 41 | } 42 | println!("--------------------------------------------------"); 43 | } 44 | 45 | // Test Falcon (feature-gated) 46 | #[cfg(feature = "falcon")] 47 | { 48 | if let Ok(did_document) = generate_did_for_algorithm(identity_suffix, &mut key_manager, Algorithm::Falcon) { 49 | println!("Generated DIDDocument for Falcon: {:#?}", did_document); 50 | } else { 51 | eprintln!("Failed to generate DIDDocument for Falcon"); 52 | } 53 | println!("--------------------------------------------------"); 54 | } 55 | 56 | // Test Ed25519 (feature-gated) 57 | #[cfg(feature = "ed25519")] 58 | { 59 | if let Ok(did_document) = generate_did_for_algorithm(identity_suffix, &mut key_manager, Algorithm::Ed25519) { 60 | println!("Generated DIDDocument for Ed25519: {:#?}", did_document); 61 | } else { 62 | eprintln!("Failed to generate DIDDocument for Ed25519"); 63 | } 64 | println!("--------------------------------------------------"); 65 | } 66 | } -------------------------------------------------------------------------------- /identity/decentralized_identity/src/credential_issuance.rs: -------------------------------------------------------------------------------- 1 | use crate::{DIDDocument, VerifiableCredential, PublicKey, KeyManager, IdentityError}; 2 | use chrono::{Utc, DateTime}; 3 | use std::collections::HashMap; 4 | use base64::engine::general_purpose; 5 | use base64::Engine as _; 6 | 7 | pub struct CredentialIssuer { 8 | pub did_document: DIDDocument, 9 | pub signing_key: PublicKey, // The signing public key of the issuer 10 | pub key_manager: KeyManager, // Key manager to retrieve private key 11 | } 12 | 13 | impl CredentialIssuer { 14 | pub fn new(did_document: DIDDocument, signing_key: PublicKey, key_manager: KeyManager) -> Self { 15 | CredentialIssuer { 16 | did_document, 17 | signing_key, 18 | key_manager, 19 | } 20 | } 21 | 22 | // Method to issue a Verifiable Credential 23 | pub fn issue_credential( 24 | &self, 25 | subject: String, 26 | credential_id: String, 27 | claims: HashMap, 28 | vc_type: Option>, 29 | ) -> Result { 30 | // Derive the issuer DID from the DIDDocument 31 | let issuer_did = self.did_document.id.clone(); 32 | 33 | let mut vc = VerifiableCredential::new( 34 | credential_id, 35 | issuer_did, 36 | subject, 37 | vc_type, 38 | Some(self.signing_key.type_.to_string()), // Use explicit proof type based on key type 39 | ); 40 | 41 | // Add claims to the credential 42 | for (key, value) in claims { 43 | vc.add_claim(key, value); 44 | } 45 | 46 | // Generate the proof 47 | let proof_value = self.sign_credential(&vc)?; 48 | let created: DateTime = Utc::now(); 49 | let verification_method = self 50 | .did_document 51 | .proof 52 | .as_ref() 53 | .map(|proof| proof.verification_method.clone()) 54 | .unwrap_or_else(|| format!("{}-Anonymous", self.did_document.id)); 55 | 56 | // Sign the credential 57 | vc.sign(proof_value, created.to_rfc3339(), verification_method); 58 | 59 | Ok(vc) 60 | } 61 | 62 | // Method to sign the credential using the public key's corresponding private key 63 | fn sign_credential(&self, vc: &VerifiableCredential) -> Result { 64 | let key_id = &self.signing_key.id; 65 | 66 | // Retrieve the private key using the key manager 67 | let private_key = self.key_manager.get_private_key(key_id)?; 68 | 69 | // Perform signing operation 70 | let signature = private_key 71 | .sign(vc.id.as_bytes()) 72 | .map_err(|_| IdentityError::Other("Signing failed".to_string()))?; 73 | 74 | Ok(general_purpose::STANDARD.encode(signature)) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /identity/decentralized_identity/src/did_document.rs: -------------------------------------------------------------------------------- 1 | use crate::{DIDDocument, VerifiableCredential, PublicKey, Proof,KeyManager,IdentityError}; 2 | use serde::{Serialize,Deserialize}; 3 | use serde_json; 4 | #[derive(Debug, Serialize, Deserialize, Clone)] 5 | pub struct UserDocument { 6 | pub did_document: DIDDocument, 7 | credentials: Vec, 8 | verifying_key: PublicKey, 9 | } 10 | 11 | impl UserDocument { 12 | pub fn new(did_document: DIDDocument, verifying_key: PublicKey) -> Self { 13 | UserDocument { 14 | did_document, 15 | credentials: Vec::new(), 16 | verifying_key, 17 | } 18 | } 19 | 20 | pub fn add_credential(&mut self, credential: VerifiableCredential) { 21 | self.credentials.push(credential); 22 | } 23 | 24 | pub fn get_public_key_raw_bytes(&self) -> Result, IdentityError> { 25 | KeyManager::decode_key_from_base64(&self.verifying_key.public_key_base64) 26 | } 27 | pub fn get_did_document(&self) -> &DIDDocument { 28 | &self.did_document 29 | } 30 | 31 | pub fn get_credentials(&self) -> Vec { 32 | self.credentials.clone() 33 | } 34 | pub fn add_proof_to_vc(&mut self, vc_id: &str, proof: Proof) -> Result<(), String> { 35 | if let Some(vc) = self.credentials.iter_mut().find(|vc| vc.id == vc_id) { 36 | vc.proof = proof; 37 | Ok(()) 38 | } else { 39 | Err(format!("Verifiable Credential with ID {} not found", vc_id)) 40 | } 41 | } 42 | 43 | pub fn display_vcs(&self) { 44 | println!("Verifiable Credentials:"); 45 | for vc in &self.credentials { 46 | println!("ID: {}\nIssuer: {}\nSubject: {}\nProof: {:?}\nClaims: {:?}\n", 47 | vc.id, vc.issuer, vc.subject, vc.proof, vc.credential_subject); 48 | } 49 | } 50 | 51 | pub fn to_json(&self) -> Result { 52 | serde_json::to_string_pretty(self) 53 | } 54 | 55 | pub fn from_json(json_str: &str) -> Result { 56 | serde_json::from_str(json_str) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /identity/decentralized_identity/src/identity_error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug)] 4 | pub enum IdentityError { 5 | MissingPublicKey, 6 | DocumentNotFound(String), 7 | InvalidDID(String), 8 | SerializationError(String), 9 | Other(String), 10 | } 11 | 12 | impl fmt::Display for IdentityError { 13 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 14 | match self { 15 | IdentityError::MissingPublicKey => write!(f, "DIDDocument must have at least one public key"), 16 | IdentityError::DocumentNotFound(did) => write!(f, "Document with DID '{}' not found", did), 17 | IdentityError::InvalidDID(did) => write!(f, "Invalid DID: '{}'", did), 18 | IdentityError::SerializationError(msg) => write!(f, "Serialization error: {}", msg), 19 | IdentityError::Other(msg) => write!(f, "{}", msg), 20 | } 21 | } 22 | } 23 | 24 | impl std::error::Error for IdentityError {} 25 | -------------------------------------------------------------------------------- /identity/decentralized_identity/src/identity_flow.rs: -------------------------------------------------------------------------------- 1 | use crate::{DIDDocument, Algorithm, PKIFactory, KeyManager}; 2 | use crate::identity_error::IdentityError; 3 | 4 | pub struct IdentityFlow; 5 | 6 | impl IdentityFlow { 7 | pub fn create_did_with_algorithm( 8 | identity_suffix: &str, 9 | key_id: String, 10 | key_manager: &mut KeyManager, 11 | algorithm: Algorithm, 12 | ) -> Result { 13 | DIDDocument::new_with_keys(identity_suffix, key_id, key_manager, algorithm) 14 | } 15 | 16 | pub fn add_key_to_did( 17 | did_document: &mut DIDDocument, 18 | key_id: String, 19 | key_manager: &mut KeyManager, 20 | algorithm: Algorithm, 21 | ) -> Result<(), IdentityError> { 22 | let pki = PKIFactory::create_pki(algorithm)?; 23 | key_manager.add_key(key_id.clone(), pki)?; 24 | did_document.add_public_key(key_manager.get_private_key(&key_id)?) 25 | } 26 | } -------------------------------------------------------------------------------- /identity/decentralized_identity/src/identity_mgmt.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{DIDDocument, UserDocument,KeyType}; 3 | use crate::identity_error::IdentityError; 4 | 5 | pub struct IdentityManager { 6 | pub storage: HashMap, // In-memory storage for UserDocuments 7 | } 8 | 9 | impl IdentityManager { 10 | pub fn new() -> Self { 11 | IdentityManager { 12 | storage: HashMap::new(), 13 | } 14 | } 15 | 16 | pub fn save_user_document(&mut self, did_document: DIDDocument, verifying_key: String,key_type: KeyType) { 17 | let verifying_key = crate::PublicKey { 18 | id: verifying_key.clone(), 19 | type_: key_type, 20 | controller: did_document.id.clone(), 21 | public_key_base64: verifying_key, 22 | }; 23 | 24 | let user_document = UserDocument::new(did_document.clone(), verifying_key); 25 | self.storage.insert(did_document.id.clone(), user_document); 26 | } 27 | 28 | pub fn remove_user_document(&mut self, did: &str) -> Result { 29 | self.storage 30 | .remove(did) 31 | .ok_or_else(|| IdentityError::DocumentNotFound(did.to_string())) 32 | } 33 | 34 | pub fn get_user_document(&self, did: &str) -> Result<&UserDocument, IdentityError> { 35 | self.storage 36 | .get(did) 37 | .ok_or_else(|| IdentityError::DocumentNotFound(did.to_string())) 38 | } 39 | 40 | pub fn upsert_user_document( 41 | &mut self, 42 | did_document: DIDDocument, 43 | verifying_key: String, 44 | key_type: KeyType, 45 | ) { 46 | let verifying_key = crate::PublicKey { 47 | id: verifying_key.clone(), 48 | type_: key_type, 49 | controller: did_document.id.clone(), 50 | public_key_base64: verifying_key, 51 | }; 52 | 53 | let user_document = UserDocument::new(did_document.clone(), verifying_key); 54 | self.storage.insert(did_document.id.clone(), user_document); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /identity/decentralized_identity/src/key_mgmt.rs: -------------------------------------------------------------------------------- 1 | // identity\decentralized_identity\src\key_mgmt.rs 2 | use std::collections::HashMap; 3 | use crate::{PKI, IdentityError}; 4 | use base64::engine::general_purpose; 5 | use base64::Engine; 6 | pub struct KeyManager { 7 | private_keys: HashMap, // Map key ID to private key (PKI) 8 | public_keys: HashMap>, // Map key ID to public key bytes 9 | } 10 | 11 | impl KeyManager { 12 | pub fn new() -> Self { 13 | KeyManager { 14 | private_keys: HashMap::new(), 15 | public_keys: HashMap::new(), 16 | } 17 | } 18 | 19 | pub fn encode_key_to_base64(key: &[u8]) -> String { 20 | general_purpose::STANDARD.encode(key) 21 | } 22 | 23 | pub fn decode_key_from_base64(encoded_key: &str) -> Result, IdentityError> { 24 | general_purpose::STANDARD 25 | .decode(encoded_key) 26 | .map_err(|_| IdentityError::Other("Base64 decoding error".to_string())) 27 | } 28 | 29 | 30 | pub fn add_key(&mut self, key_id: String, pki: PKI) -> Result<(), IdentityError> { 31 | let public_key = pki.public_key_raw_bytes(); 32 | self.private_keys.insert(key_id.clone(), pki); 33 | self.public_keys.insert(key_id, public_key); 34 | Ok(()) 35 | } 36 | 37 | pub fn get_private_key(&self, key_id: &str) -> Result<&PKI, IdentityError> { 38 | self.private_keys 39 | .get(key_id) 40 | .ok_or_else(|| IdentityError::Other(format!("Private key not found for key ID: {}", key_id))) 41 | } 42 | 43 | pub fn get_public_key(&self, key_id: &str) -> Result<&[u8], IdentityError> { 44 | self.public_keys 45 | .get(key_id) 46 | .map(|key| key.as_slice()) 47 | .ok_or_else(|| IdentityError::Other(format!("Public key not found for key ID: {}", key_id))) 48 | } 49 | } -------------------------------------------------------------------------------- /identity/decentralized_identity/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod did_document; 2 | mod did; 3 | mod vc; 4 | mod credential_issuance; 5 | mod identity_mgmt; 6 | mod identity_flow; 7 | mod identity_error; 8 | mod pki_factory; 9 | mod key_mgmt; 10 | 11 | pub use did::{Authentication,DIDDocument,Proof,PublicKey,Service,KeyType}; 12 | pub use vc::VerifiableCredential; 13 | pub use did_document::UserDocument; 14 | pub use identity_mgmt::IdentityManager; 15 | pub use key_mgmt::KeyManager; 16 | pub use identity_flow::IdentityFlow; 17 | pub use identity_error::IdentityError; 18 | pub use pki_factory::{Algorithm,PKI,PKIFactory}; 19 | pub use credential_issuance::CredentialIssuer; -------------------------------------------------------------------------------- /identity/decentralized_identity/src/vc.rs: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Deserialize}; 2 | use std::collections::HashMap; 3 | use crate::{Proof, IdentityError, PKI}; 4 | use chrono::Utc; 5 | use base64::engine::general_purpose; 6 | use base64::Engine as _; // For encoding 7 | 8 | #[derive(Serialize, Deserialize, Debug,Clone)] 9 | pub struct VerifiableCredential { 10 | pub id: String, 11 | pub issuer: String, 12 | pub subject: String, 13 | pub type_: Vec, 14 | pub credential_subject: HashMap, 15 | pub proof: Proof, 16 | } 17 | 18 | impl VerifiableCredential { 19 | pub fn new( 20 | id: String, 21 | issuer: String, 22 | subject: String, 23 | type_: Option>, 24 | proof_type: Option, 25 | ) -> Self { 26 | VerifiableCredential { 27 | id, 28 | issuer, 29 | subject, 30 | type_: type_.unwrap_or_else(|| vec!["VerifiableCredential".to_string()]), 31 | credential_subject: HashMap::new(), 32 | proof: Proof { 33 | type_: proof_type.unwrap_or_else(|| "Ed25519Signature2020".to_string()), // Update to match supported types 34 | created: "".to_string(), 35 | proof_value: "".to_string(), 36 | verification_method: "".to_string(), 37 | } 38 | } 39 | } 40 | 41 | pub fn add_claim(&mut self, key: String, value: String) { 42 | self.credential_subject.insert(key, value); 43 | } 44 | 45 | pub fn sign(&mut self, proof_value: String, created: String, verification_method: String) { 46 | self.proof.proof_value = proof_value; 47 | self.proof.created = created; 48 | self.proof.verification_method = verification_method; 49 | } 50 | 51 | pub fn issue_credential( 52 | issuer_did: &str, 53 | subject: String, 54 | credential_id: String, 55 | claims: HashMap, 56 | pki: &PKI, 57 | ) -> Result { 58 | let mut vc = VerifiableCredential::new( 59 | credential_id, 60 | issuer_did.to_string(), 61 | subject, 62 | None, // Default type_ 63 | None, // Default proof_type 64 | ); 65 | 66 | // Add claims to the credential 67 | for (key, value) in claims { 68 | vc.add_claim(key, value); 69 | } 70 | 71 | // Generate the proof 72 | let proof_value = pki 73 | .sign(vc.id.as_bytes()) 74 | .map_err(|e| IdentityError::Other(format!("Signing failed: {:?}", e)))?; 75 | let created = Utc::now().to_rfc3339(); 76 | let verification_method = issuer_did.to_string(); 77 | 78 | vc.sign( 79 | general_purpose::STANDARD.encode(proof_value), 80 | created, 81 | verification_method, 82 | ); 83 | 84 | Ok(vc) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /identity/decentralized_identity/tests/did_document_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod did_document_tests { 3 | use decentralized_identity::{KeyManager, Algorithm, DIDDocument}; 4 | 5 | #[cfg(feature = "pki_rsa")] 6 | #[test] 7 | fn test_did_document_generation_with_rsa() { 8 | let mut key_manager = KeyManager::new(); 9 | let identity_suffix = "did:example"; 10 | 11 | let key_id = format!("key-{}", uuid::Uuid::new_v4()); 12 | let did_document = DIDDocument::new_with_keys( 13 | identity_suffix, 14 | key_id.clone(), 15 | &mut key_manager, 16 | Algorithm::RSA, 17 | ); 18 | 19 | assert!(did_document.is_ok(), "Failed to create DIDDocument with RSA"); 20 | let did_document = did_document.unwrap(); 21 | 22 | assert_eq!(did_document.public_keys.len(), 1); 23 | assert_eq!(did_document.authentication.len(), 1); 24 | } 25 | 26 | #[cfg(feature = "ed25519")] 27 | #[test] 28 | fn test_did_document_generation_with_ed25519() { 29 | let mut key_manager = KeyManager::new(); 30 | let identity_suffix = "did:example"; 31 | 32 | let key_id = format!("key-{}", uuid::Uuid::new_v4()); 33 | let did_document = DIDDocument::new_with_keys( 34 | identity_suffix, 35 | key_id.clone(), 36 | &mut key_manager, 37 | Algorithm::Ed25519, 38 | ); 39 | 40 | assert!(did_document.is_ok(), "Failed to create DIDDocument with Ed25519"); 41 | let did_document = did_document.unwrap(); 42 | 43 | assert_eq!(did_document.public_keys.len(), 1); 44 | assert_eq!(did_document.authentication.len(), 1); 45 | } 46 | 47 | #[cfg(feature = "ed25519")] 48 | #[test] 49 | fn test_did_document_serialization() { 50 | let mut key_manager = KeyManager::new(); 51 | let identity_suffix = "did:example"; 52 | let key_id = format!("key-{}", uuid::Uuid::new_v4()); 53 | let did_document = DIDDocument::new_with_keys( 54 | identity_suffix, 55 | key_id.clone(), 56 | &mut key_manager, 57 | Algorithm::Ed25519, 58 | ) 59 | .expect("Failed to create DIDDocument"); 60 | 61 | let serialized = serde_json::to_string(&did_document).expect("Serialization failed"); 62 | let deserialized: DIDDocument = 63 | serde_json::from_str(&serialized).expect("Deserialization failed"); 64 | 65 | assert_eq!(did_document.id, deserialized.id); 66 | assert_eq!(did_document.public_keys.len(), deserialized.public_keys.len()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /identity/decentralized_identity/tests/key_mgmt_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod key_manager_tests { 3 | use decentralized_identity::{KeyManager, Algorithm, PKIFactory}; 4 | 5 | #[cfg(feature = "ed25519")] 6 | #[test] 7 | fn test_key_manager_add_key_with_ed25519() { 8 | let mut key_manager = KeyManager::new(); 9 | let algorithm = Algorithm::Ed25519; 10 | let key_id = "test-key".to_string(); 11 | let pki = PKIFactory::create_pki(algorithm).expect("Failed to create PKI"); 12 | 13 | key_manager.add_key(key_id.clone(), pki).expect("Failed to add key"); 14 | assert!(key_manager.get_public_key(&key_id).is_ok()); 15 | } 16 | 17 | #[cfg(feature = "pki_rsa")] 18 | #[test] 19 | fn test_key_manager_add_key_with_rsa() { 20 | let mut key_manager = KeyManager::new(); 21 | let algorithm = Algorithm::RSA; 22 | let key_id = "test-key".to_string(); 23 | let pki = PKIFactory::create_pki(algorithm).expect("Failed to create PKI"); 24 | 25 | key_manager.add_key(key_id.clone(), pki).expect("Failed to add key"); 26 | assert!(key_manager.get_public_key(&key_id).is_ok()); 27 | } 28 | 29 | #[cfg(feature = "pki_rsa")] 30 | #[test] 31 | fn test_key_manager_get_private_key_with_rsa() { 32 | let mut key_manager = KeyManager::new(); 33 | let algorithm = Algorithm::RSA; 34 | let key_id = "test-key".to_string(); 35 | let pki = PKIFactory::create_pki(algorithm).expect("Failed to create PKI"); 36 | 37 | key_manager.add_key(key_id.clone(), pki).expect("Failed to add key"); 38 | assert!(key_manager.get_private_key(&key_id).is_ok()); 39 | } 40 | 41 | #[cfg(feature = "ed25519")] 42 | #[test] 43 | fn test_key_manager_get_private_key_with_ed25519() { 44 | let mut key_manager = KeyManager::new(); 45 | let algorithm = Algorithm::Ed25519; 46 | let key_id = "test-key".to_string(); 47 | let pki = PKIFactory::create_pki(algorithm).expect("Failed to create PKI"); 48 | 49 | key_manager.add_key(key_id.clone(), pki).expect("Failed to add key"); 50 | assert!(key_manager.get_private_key(&key_id).is_ok()); 51 | } 52 | } -------------------------------------------------------------------------------- /identity/decentralized_identity/tests/vc_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod credential_tests { 3 | use decentralized_identity::{Proof, VerifiableCredential}; 4 | 5 | #[test] 6 | fn test_credential_creation() { 7 | let vc = VerifiableCredential::new( 8 | "vc-1".to_string(), 9 | "did:example:issuer".to_string(), 10 | "did:example:subject".to_string(), 11 | None, 12 | None, 13 | ); 14 | 15 | assert_eq!(vc.id, "vc-1"); 16 | assert_eq!(vc.issuer, "did:example:issuer"); 17 | assert_eq!(vc.subject, "did:example:subject"); 18 | } 19 | 20 | #[test] 21 | fn test_credential_proof() { 22 | let mut vc = VerifiableCredential::new( 23 | "vc-1".to_string(), 24 | "did:example:issuer".to_string(), 25 | "did:example:subject".to_string(), 26 | None, 27 | None, 28 | ); 29 | 30 | let proof = Proof { 31 | type_: "Ed25519Signature2020".to_string(), 32 | created: "2023-01-01T00:00:00Z".to_string(), 33 | proof_value: "test-proof".to_string(), 34 | verification_method: "did:example:issuer".to_string(), 35 | }; 36 | 37 | vc.sign(proof.proof_value.clone(), proof.created.clone(), proof.verification_method.clone()); 38 | 39 | assert_eq!(vc.proof.proof_value, proof.proof_value); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /identity/key-storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "key-storage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | # Default Dependenecies 8 | serde = { version = "1.0", features = ["derive"] } 9 | serde_json = {version = "1.0"} 10 | pem = {version = "3.0.4"} # For File Format Coversion 11 | 12 | # For Windows Dependencies => {Feature Flagged : True, Features : [tsm,keyring]} 13 | bincode = {version = "1.3.3",optional = true} 14 | winapi = { version = "0.3.9", features = ["dpapi", "errhandlingapi", "winbase", "minwindef", "wincrypt","wincred"] , optional = true} 15 | 16 | # For Linux Dependencies => {Feature Flagged : True, Features : [linx_secure_storage]} 17 | linux-keyutils = {version = "0.2.4",optional = true} 18 | 19 | # For Cloud Storage => {Feature Flagged : True, Features : [amazon_secure_kms]} 20 | aws-config = {version = "1.5.13",optional = true} 21 | aws-sdk-kms ={ version = "1.54",optional = true} 22 | 23 | [features] 24 | default = ["memory"] # Features enabled by default 25 | memory = ["bincode"] # Feature flag for in-memory storage 26 | keyring = ["winapi"] 27 | tsm = ["winapi"] 28 | linux_secure_storage = ["linux-keyutils"] # Linux secure storage feature 29 | amazon_secure_kms = ["aws-config","aws-sdk-kms"] -------------------------------------------------------------------------------- /identity/key-storage/src/cloud_storage.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\cloud_storage.rs 2 | // ==== Cloud-Based Key Storage ==== 3 | // 4 | // This module provides support for integrating cloud-based key storage solutions. Currently, 5 | // it focuses on Amazon's Key Management Service (KMS) as a secure backend for storing and 6 | // managing cryptographic keys. 7 | // 8 | // ## Overview 9 | // 10 | // - **Backend:** Amazon KMS (via `aws_sdk_kms`). 11 | // - **Feature Dependency:** Enabled only when the `amazon_secure_kms` feature is specified. 12 | #[cfg(feature="amazon_secure_kms")] 13 | mod amazon_s3_storage; 14 | #[cfg(feature="amazon_secure_kms")] 15 | pub use amazon_s3_storage::{AwsKmsKey,AwsKmsStorage}; -------------------------------------------------------------------------------- /identity/key-storage/src/file_format/json_formatter.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\file_format\json_formatter.rs 2 | use crate::FileFormat; 3 | #[derive(Debug)] 4 | pub struct JsonFormat; 5 | 6 | impl FileFormat for JsonFormat { 7 | type DataType = serde_json::Value; // Or your specific data type 8 | type Error = String; 9 | 10 | fn serialize(&self, data: &Self::DataType) -> Result, Self::Error> { 11 | serde_json::to_vec(data).map_err(|e| format!("Failed to serialize to JSON: {}", e)) 12 | } 13 | 14 | fn deserialize(&self, input: &[u8]) -> Result { 15 | serde_json::from_slice(input).map_err(|e| format!("Failed to deserialize JSON: {}", e)) 16 | } 17 | 18 | fn file_extension(&self) -> &'static str { 19 | "json" 20 | } 21 | } -------------------------------------------------------------------------------- /identity/key-storage/src/file_format/mod.rs: -------------------------------------------------------------------------------- 1 | // identity\src\file_format\mod.rs 2 | mod json_formatter; 3 | 4 | pub use json_formatter::JsonFormat; 5 | 6 | mod pem_formatter; 7 | 8 | pub use pem_formatter::PemFormat; -------------------------------------------------------------------------------- /identity/key-storage/src/file_format/pem_formatter.rs: -------------------------------------------------------------------------------- 1 | use crate::FileFormat; 2 | use pem::{Pem, encode, parse}; 3 | 4 | #[derive(Debug)] 5 | pub struct PemFormat { 6 | pub label: String, 7 | } 8 | 9 | impl FileFormat for PemFormat { 10 | type DataType = Vec; 11 | type Error = String; 12 | 13 | fn serialize(&self, data: &Self::DataType) -> Result, Self::Error> { 14 | let pem = Pem::new(self.label.clone(), data.clone()); 15 | Ok(encode(&pem).into_bytes()) 16 | } 17 | 18 | fn deserialize(&self, input: &[u8]) -> Result { 19 | let pem = parse(input).map_err(|e| format!("Failed to parse PEM: {}", e))?; 20 | Ok(pem.into_contents()) 21 | } 22 | 23 | fn file_extension(&self) -> &'static str { 24 | "pem" 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn test_pem_format() { 34 | let pem_format = PemFormat { 35 | label: "TEST LABEL".to_string(), 36 | }; 37 | 38 | let data = b"Hello, PEM!".to_vec(); 39 | let serialized = pem_format.serialize(&data).expect("Serialization failed"); 40 | let deserialized = pem_format.deserialize(&serialized).expect("Deserialization failed"); 41 | 42 | assert_eq!(data, deserialized); 43 | } 44 | } -------------------------------------------------------------------------------- /identity/key-storage/src/file_format_trait.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\file_format_trait.rs 2 | use serde::{Serialize, Deserialize}; 3 | use std::fmt::Debug; 4 | 5 | pub trait FileFormat: Debug { 6 | type DataType: Serialize + for<'a> Deserialize<'a>; 7 | type Error; 8 | 9 | /// Serialize data into the specific format. 10 | fn serialize(&self, data: &Self::DataType) -> Result, Self::Error>; 11 | 12 | /// Deserialize data from the specific format. 13 | fn deserialize(&self, input: &[u8]) -> Result; 14 | 15 | /// Get the file extension for this format (e.g., "json", "pem"). 16 | fn file_extension(&self) -> &'static str; 17 | } -------------------------------------------------------------------------------- /identity/key-storage/src/key_storage_error.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\key_storage_error.rs 2 | //? **Key Storage Error Enum** 3 | //? 4 | //? Defines error variants for key storage operations, providing a structured way to handle 5 | //? errors that may occur during key management. 6 | 7 | /// Represents various errors that can occur in key storage operations. 8 | #[derive(Debug)] 9 | pub enum KeyStorageError { 10 | /// Error when saving a key. 11 | SaveError(String), 12 | /// Error when loading a key. 13 | LoadError(String), 14 | /// Error when removing a key. 15 | RemoveError(String), 16 | /// Error during encryption or decryption. 17 | EncryptionError(String), 18 | /// Error related to the storage backend. 19 | BackendError(String), 20 | /// Indicates an unsupported operation or feature. 21 | NotSupported(String), 22 | /// Unknown or unexpected error. 23 | Unknown(String), 24 | /// Error related to file operations. 25 | FileError(String), 26 | /// Error during serialization or deserialization. 27 | SerializationError(String), 28 | } -------------------------------------------------------------------------------- /identity/key-storage/src/key_storage_trait.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\key_storage_trait.rs 2 | /// Defines the traits for key storage. 3 | use serde::{Serialize, Deserialize}; 4 | use std::fmt::Debug; 5 | //? **Key Storage Trait and Metadata Structure** 6 | //? 7 | //? This module defines the `KeyStorage` trait and supporting structures for securely managing 8 | //? cryptographic keys. It provides a unified interface for key storage backends, enabling 9 | //? functionalities like saving, loading, and removing keys, along with optional metadata management. 10 | //? 11 | //? ## Overview 12 | //? 13 | //? - **`KeyStorage` Trait**: Defines the core methods required for implementing a key storage backend. 14 | //? - **`KeyMetadata` Struct**: Represents metadata associated with stored keys, such as creation date, 15 | //? expiration, and storage location. 16 | //? 17 | //? ## `KeyStorage` Trait 18 | //? 19 | //? The `KeyStorage` trait is designed to be flexible and extensible, allowing implementation for various 20 | //? storage backends. It requires the following associated types and methods: 21 | //? 22 | //? ### Associated Types 23 | //? - **`StoredType`**: The type of data to be stored, which must implement `Serialize` and `Deserialize`. 24 | //? - **`Error`**: Custom error type for the implementation. 25 | //? 26 | pub trait KeyStorage: Debug + Send + Sync { 27 | type StoredType: Serialize + for<'a> Deserialize<'a>; 28 | type Error; 29 | 30 | /// Initialize the storage backend (e.g., for setting up connections or configurations). 31 | fn initialize(&self, config: Option<&str>) -> Result<(), Self::Error>; 32 | 33 | /// Save a key pair to storage. 34 | fn save(&self, keypair: &Self::StoredType, location: &str, encrypt: bool) -> Result<(), Self::Error>; 35 | 36 | /// Load a key pair from storage. 37 | fn load(&self, location: &str, decrypt: bool) -> Result; 38 | 39 | /// Remove a key pair from storage. 40 | fn remove(&self, location: &str) -> Result<(), Self::Error>; 41 | 42 | /// List all stored keys (optional). 43 | fn list(&self) -> Result, Self::Error>; 44 | 45 | /// Fetch metadata for a stored key (optional). 46 | fn metadata(&self, location: &str) -> Result; 47 | } 48 | 49 | 50 | //? ## `KeyMetadata` Struct 51 | //? 52 | //? This struct provides metadata details for stored keys, useful for tracking and managing keys efficiently. 53 | //? 54 | //? ### Fields 55 | //? - **`created_at`**: Timestamp when the key was created. 56 | //? - **`expires_at`**: Optional timestamp when the key expires. 57 | //? - **`key_type`**: Type of the key (e.g., RSA, Ed25519, etc.). 58 | //? - **`location`**: Storage location of the key. 59 | //? - **`modified_at`**: Timestamp when the key was last modified. 60 | //? - **`file_size`**: Size of the stored key file, in bytes. 61 | //? 62 | #[derive(Debug, Serialize, Deserialize)] 63 | pub struct KeyMetadata { 64 | pub created_at: String, 65 | pub expires_at: Option, 66 | pub key_type: String, 67 | pub location: String, 68 | pub modified_at: String, 69 | pub file_size: u64, 70 | } 71 | -------------------------------------------------------------------------------- /identity/key-storage/src/lib.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\lib.rs 2 | 3 | mod key_storage_trait; 4 | 5 | pub use key_storage_trait::{KeyMetadata,KeyStorage}; 6 | 7 | mod key_storage_error; 8 | 9 | pub use key_storage_error::KeyStorageError; 10 | 11 | mod file_format_trait; 12 | pub use file_format_trait::FileFormat; 13 | // ==================================== Storage Methods Public Method Exposure =================================== 14 | 15 | #[cfg(feature = "memory")] 16 | mod in_memory_key_storage; 17 | #[cfg(feature = "memory")] 18 | pub use in_memory_key_storage::MemoryStorage; 19 | 20 | mod file_storage; 21 | 22 | pub use file_storage::FileStorage; 23 | 24 | #[cfg(target_os = "windows")] 25 | mod windows_storage; 26 | 27 | #[cfg(all(target_os = "windows", feature = "tsm"))] 28 | pub use windows_storage::TSMStorage; 29 | 30 | #[cfg(all(target_os = "windows", feature = "keyring"))] 31 | pub use windows_storage::WindowKeyRingStorage; 32 | 33 | 34 | #[cfg(target_os = "linux")] 35 | mod linux_storage; 36 | 37 | #[cfg(target_os = "linux")] 38 | pub use linux_storage::*; 39 | 40 | 41 | mod cloud_storage; 42 | // ============================= File Format Setup =================================== 43 | mod file_format; 44 | 45 | pub use file_format::*; -------------------------------------------------------------------------------- /identity/key-storage/src/linux_storage.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\linux_storage.rs 2 | /// **Linux Key Storage Module** 3 | /// 4 | /// This module provides support for secure key storage on Linux platforms, enabled by the `linux_secure_storage` feature. 5 | /// 6 | /// ## Conditional Compilation 7 | /// 8 | /// - Available only on Linux (`target_os = "linux`). 9 | /// - Requires the `linux_secure_storage` feature to be enabled. 10 | #[cfg(all(target_os = "linux", feature = "linux_secure_storage"))] 11 | mod linux_key_storage; 12 | #[cfg(all(target_os = "linux", feature = "linux_secure_storage"))] 13 | pub use linux_key_storage::LinuxKeyUtilsStorage; -------------------------------------------------------------------------------- /identity/key-storage/src/windows_storage.rs: -------------------------------------------------------------------------------- 1 | // identity\key-storage\src\windows_storage.rs 2 | /// **Windows Key Storage Module** 3 | /// 4 | /// This module provides conditional compilation for different Windows-specific key storage backends. 5 | /// Depending on the enabled feature, it allows the use of either a keyring-based storage system or 6 | /// a Trusted Security Module (TSM)-based storage system for managing cryptographic keys securely 7 | /// on Windows platforms. 8 | /// 9 | /// ## Overview 10 | /// 11 | /// The module contains two key components, each enabled through feature flags: 12 | /// 13 | /// - **Keyring Storage** (`keyring` feature): Utilizes the operating system's keyring service to securely 14 | /// store and retrieve keys. 15 | /// - **Trusted Security Module (TSM) Storage** (`tsm` feature): Leverages a TSM for advanced security 16 | /// features and storage options. 17 | #[cfg(all(target_os = "windows", feature = "keyring"))] 18 | mod windows_key_ring_storage; 19 | 20 | #[cfg(all(target_os = "windows", feature = "keyring"))] 21 | pub use windows_key_ring_storage::WindowKeyRingStorage; 22 | 23 | #[cfg(all(target_os = "windows", feature = "tsm"))] 24 | mod windows_tsm_storage; 25 | 26 | #[cfg(all(target_os = "windows", feature = "tsm"))] 27 | pub use windows_tsm_storage::TSMStorage; 28 | -------------------------------------------------------------------------------- /identity/src/key_exchange.rs: -------------------------------------------------------------------------------- 1 | // identity\src\key_exchnage_traits.rs 2 | 3 | /// A trait defining core functionalities for Key Exchange operations. 4 | /// 5 | /// This trait provides methods for encapsulating and decapsulating keys 6 | /// to facilitate secure key exchange between parties. 7 | /// 8 | pub trait KeyExchange { 9 | /// Represents the shared secret key derived during key exchange. 10 | type SharedSecretKey; 11 | 12 | /// Represents the public key type used in cryptographic operations. 13 | type PublicKey; 14 | 15 | /// Represents the private key type used in cryptographic operations. 16 | type PrivateKey; 17 | 18 | /// Represents the error type for key exchange operations. 19 | type Error; 20 | 21 | /// Encapsulates a key for secure transmission to another party. 22 | /// 23 | /// # Arguments 24 | /// - `public_key`: The recipient's public key. 25 | /// - `context`: Optional additional context (e.g., session ID, auxiliary data). 26 | /// 27 | /// # Returns 28 | /// - `Ok((SharedSecretKey, Vec))`: The shared secret key and the encapsulated ciphertext. 29 | /// - `Err(Error)`: If encapsulation fails. 30 | fn encapsulate( 31 | public_key: &Self::PublicKey, 32 | context: Option<&[u8]>, 33 | ) -> Result<(Self::SharedSecretKey, Vec), Self::Error>; 34 | 35 | /// Decapsulates a received ciphertext to retrieve the shared secret key. 36 | /// 37 | /// # Arguments 38 | /// - `private_key`: The recipient's private key. 39 | /// - `ciphertext`: A slice of bytes representing the encapsulated ciphertext. 40 | /// - `context`: Optional additional context (e.g., session ID, auxiliary data). 41 | /// 42 | /// # Returns 43 | /// - `Ok(SharedSecretKey)`: The shared secret key derived from the ciphertext. 44 | /// - `Err(Error)`: If decapsulation fails. 45 | fn decapsulate( 46 | private_key: &Self::PrivateKey, 47 | ciphertext: &[u8], 48 | context: Option<&[u8]>, 49 | ) -> Result; 50 | 51 | /// Retrieves the name or type of the key exchange mechanism. 52 | /// 53 | /// # Returns 54 | /// - `String`: The name of the key exchange mechanism (e.g., "Kyber", "RSA-KEM"). 55 | fn key_exchange_type() -> String; 56 | } 57 | -------------------------------------------------------------------------------- /identity/src/key_serde_trait.rs: -------------------------------------------------------------------------------- 1 | // identity\src\key_serde_trait.rs 2 | use crate::PKIError; 3 | pub trait KeySerialization { 4 | /// Serialize the key into bytes. 5 | fn to_bytes(&self) -> Vec; 6 | 7 | /// Deserialize the key from bytes. 8 | fn from_bytes(bytes: &[u8]) -> Result 9 | where 10 | Self: Sized; 11 | } -------------------------------------------------------------------------------- /identity/src/lib.rs: -------------------------------------------------------------------------------- 1 | // identity\src\lib.rs 2 | /// The main module for the PKI (Public Key Infrastructure) library. 3 | /// This library provides implementations for various cryptographic key pair algorithms, 4 | /// including RSA, ECDSA, Dilithium, SPHINCS+, and others. Each algorithm implements the 5 | /// `PKITraits` trait, which defines a common interface for key pair operations such as 6 | /// key generation, signing, and verification. 7 | 8 | // Module defining the `PKITraits` trait. 9 | mod pki_trait; 10 | // Module defining the `PKIError` enum for error handling. 11 | mod pki_error; 12 | // Module containing specific implementations of cryptographic algorithms. 13 | mod pki; 14 | // Module contains the trait for Key Exchange Mechanism 15 | mod key_exchange; 16 | // Ciphersuite For Defining the Supported Ciphers 17 | mod cipher_suite; 18 | // Modue containing the Trait for Key Serialization 19 | mod key_serde_trait; 20 | /// # Overview 21 | /// This library is designed to facilitate cryptographic operations for 22 | /// secure communication and data integrity. By using standardized algorithms 23 | /// and robust error handling, it ensures interoperability and security. 24 | /// 25 | /// ## Modules 26 | /// - `pki_trait`: Defines the `PKITraits` trait, which all cryptographic key pair implementations must adhere to. 27 | /// - `pki_error`: Provides a comprehensive error handling mechanism for cryptographic operations. 28 | /// - `pki`: Contains the implementations for supported cryptographic algorithms. 29 | /// 30 | 31 | // Publicly export the `PKITraits` trait for use by external modules. 32 | pub use pki_trait::PKITraits; 33 | // Publicly export the `PKIError` enum for error handling by external modules. 34 | pub use pki_error::PKIError; 35 | // Publicly export the `KeyExchange` trait for use by external Modules 36 | pub use key_exchange::KeyExchange; 37 | // CipherSuite Supported By Crate 38 | pub use cipher_suite::CipherSuite; 39 | // Publicly export the `KeySerialization`trait for use by external Module 40 | pub use key_serde_trait::KeySerialization; 41 | // Publicly export all contents of the `pki` module for external use. 42 | pub use pki::*; -------------------------------------------------------------------------------- /identity/src/pki/mod.rs: -------------------------------------------------------------------------------- 1 | //! PKI Module 2 | //! 3 | //! This module provides various cryptographic key pair implementations, 4 | //! enabled through feature flags. Each implementation adheres to the 5 | //! `PKITraits` trait and supports operations like key generation, signing, 6 | //! and verification. 7 | // identity\src\pki\mod.rs 8 | // RSA key pair implementation 9 | #[cfg(feature = "pki_rsa")] 10 | mod rsa_keypair; 11 | #[cfg(feature = "pki_rsa")] 12 | pub use rsa_keypair::RSAkeyPair; 13 | 14 | // SECP256K1 key pair implementation (under development) 15 | #[cfg(feature = "secp256k1")] 16 | mod secp256k1_keypair; 17 | #[cfg(feature = "secp256k1")] 18 | pub use secp256k1_keypair::SECP256K1KeyPair; 19 | 20 | // ECDSA key pair implementation 21 | #[cfg(feature = "ecdsa")] 22 | mod ecdsa_keypair; 23 | #[cfg(feature = "ecdsa")] 24 | pub use ecdsa_keypair::ECDSAKeyPair; 25 | 26 | // Ed25519 key pair implementation 27 | #[cfg(feature = "ed25519")] 28 | mod ed25519_keypair; 29 | #[cfg(feature = "ed25519")] 30 | pub use ed25519_keypair::Ed25519KeyPair; 31 | 32 | // Dilithium key pair implementation 33 | #[cfg(feature = "dilithium")] 34 | mod dilithium_keypair; 35 | #[cfg(feature = "dilithium")] 36 | pub use dilithium_keypair::DilithiumKeyPair; 37 | 38 | // SPHINCS+ key pair implementation 39 | #[cfg(feature = "spincs")] 40 | mod spincs_keypair; 41 | #[cfg(feature = "spincs")] 42 | pub use spincs_keypair::SPHINCSKeyPair; 43 | 44 | // Falcon key pair implementation 45 | #[cfg(feature = "falcon")] 46 | mod falcon_keypair; 47 | #[cfg(feature = "falcon")] 48 | pub use falcon_keypair::FalconKeyPair; 49 | 50 | 51 | // Kyber key pair Implementation 52 | #[cfg(feature = "kyber")] 53 | mod kyber_keypair; 54 | #[cfg(feature = "kyber")] 55 | pub use kyber_keypair::KyberKeyPair; -------------------------------------------------------------------------------- /identity/src/pki/spincs_keypair.rs: -------------------------------------------------------------------------------- 1 | // ======================= Public Key Infrastructure (PKI) ======================= 2 | // identity\src\pki\spincs_keypair.rs 3 | 4 | #[cfg(feature = "spincs")] 5 | use crate::{PKIError, PKITraits}; 6 | #[cfg(feature = "spincs")] 7 | use fips205::slh_dsa_shake_256s::{self, PrivateKey, PublicKey}; 8 | #[cfg(feature = "spincs")] 9 | use fips205::traits::{SerDes, Signer, Verifier}; 10 | 11 | // ======================= SPHINCS+ Key Pair Definition ======================= 12 | #[cfg(feature = "spincs")] 13 | #[deprecated(since = "1.0.0", note = "SPHINCS+ is under beta testing and may be unstable. Use at your own risk.")] 14 | #[derive(Clone)] 15 | pub struct SPHINCSKeyPair { 16 | pub private_key: PrivateKey, 17 | pub public_key: PublicKey, 18 | } 19 | 20 | // ======================= PKITraits Implementation ======================= 21 | #[cfg(feature = "spincs")] 22 | impl PKITraits for SPHINCSKeyPair { 23 | type KeyPair = Self; 24 | type Error = PKIError; 25 | 26 | /// Generates a new SPHINCS+ key pair. 27 | fn generate_key_pair() -> Result { 28 | let (public_key, private_key) = slh_dsa_shake_256s::try_keygen() 29 | .map_err(|e| PKIError::KeyPairGenerationError(format!("Key generation failed: {}", e)))?; 30 | 31 | Ok(Self { 32 | private_key, 33 | public_key, 34 | }) 35 | } 36 | 37 | /// Signs data using the private key. 38 | fn sign(&self, data: &[u8]) -> Result, Self::Error> { 39 | let signature = self 40 | .private_key 41 | .try_sign(data, &[], false) 42 | .map_err(|e| PKIError::SigningError(format!("Signing failed: {}", e)))?; 43 | 44 | Ok(signature.to_vec()) 45 | } 46 | 47 | /// Verifies a signature using the public key. 48 | fn verify(&self, data: &[u8], signature: &[u8]) -> Result { 49 | let signature_array: [u8; slh_dsa_shake_256s::SIG_LEN] = signature 50 | .try_into() 51 | .map_err(|_| PKIError::VerificationError("Invalid signature length".to_string()))?; 52 | 53 | let is_valid = self.public_key.verify(data, &signature_array, &[]); 54 | Ok(is_valid) 55 | } 56 | 57 | /// Retrieves the public key from the key pair. 58 | fn get_public_key_raw_bytes(&self) -> Vec { 59 | self.public_key.clone().into_bytes().to_vec() 60 | } 61 | 62 | /// Retrieves the key type. 63 | fn key_type() -> String { 64 | "SPHINCS+".to_string() 65 | } 66 | } 67 | 68 | // ======================= Future Enhancements ======================= 69 | // Additional features such as key serialization and deserialization can be implemented here if required. 70 | -------------------------------------------------------------------------------- /identity/src/pki_trait.rs: -------------------------------------------------------------------------------- 1 | // identity\src\pki_trait.rs 2 | /// A trait defining core functionalities for Public Key Infrastructure (PKI) operations. 3 | /// 4 | /// This trait provides methods for generating key pairs, signing and verifying data, 5 | /// and performing encryption and decryption. It is designed to be implemented for various 6 | /// cryptographic algorithms, ensuring flexibility and extensibility. 7 | /// 8 | /// # Associated Types 9 | /// - `KeyPair`: Represents the public and private key pair. 10 | /// - `Error`: Represents errors that may occur during PKI operations. 11 | pub trait PKITraits { 12 | /// Represents the key pair used in cryptographic operations. 13 | type KeyPair; 14 | 15 | /// Represents the error type for PKI operations. 16 | type Error; 17 | 18 | /// Generates a new public and private key pair. 19 | /// 20 | /// # Returns 21 | /// - `Ok(KeyPair)`: A newly generated key pair. 22 | /// - `Err(Error)`: If key pair generation fails. 23 | fn generate_key_pair() -> Result; 24 | 25 | /// Signs data using the private key. 26 | /// 27 | /// # Arguments 28 | /// - `data`: A slice of bytes to be signed. 29 | /// 30 | /// # Returns 31 | /// - `Ok(Vec)`: The signature of the data. 32 | /// - `Err(Error)`: If signing fails. 33 | fn sign(&self, data: &[u8]) -> Result, Self::Error>; 34 | 35 | /// Verifies the signature of data using the public key. 36 | /// 37 | /// # Arguments 38 | /// - `data`: A slice of bytes whose signature needs to be verified. 39 | /// - `signature`: A slice of bytes representing the signature. 40 | /// 41 | /// # Returns 42 | /// - `Ok(true)`: If the signature is valid. 43 | /// - `Ok(false)`: If the signature is invalid. 44 | /// - `Err(Error)`: If verification fails due to other reasons. 45 | fn verify(&self, data: &[u8], signature: &[u8]) -> Result; 46 | 47 | /// Retrieves the public key from the key pair. 48 | fn get_public_key_raw_bytes(&self) -> Vec; 49 | 50 | /// Retrieves the key type (e.g., "RSA", "Ed25519"). 51 | fn key_type() -> String; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /protocols/handshake/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "handshake" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | thiserror = {version = "2.0.11"} 8 | async-trait = {version = "0.1.85"} 9 | tokio = {version = "1.0.0",features = ["full"]} 10 | futures = {version = "0.3.31"} -------------------------------------------------------------------------------- /protocols/handshake/README.md: -------------------------------------------------------------------------------- 1 | # Nautilus Handshake Protocol 2 | 3 | The `nautilus_handshake` module provides a **flexible and modular handshake framework** for establishing secure communication between nodes. It enables **customizable multi-step handshakes**, making it suitable for **secure authentication, key exchange, and protocol negotiation** in decentralized and distributed systems. 4 | 5 | This module is built with **Rust** and leverages **Tokio** for asynchronous execution. 6 | 7 | --- 8 | 9 | ## 🌟 Features 10 | 11 | ### 🔄 **Modular Handshake System** 12 | - Supports **multi-step handshakes** with **customizable steps**. 13 | - Enables **insertion, removal, and modification** of handshake steps dynamically. 14 | - Protocol-aware execution ensures **steps are executed only if compatible**. 15 | 16 | ### 🔐 **Security & Negotiation** 17 | - Supports **cipher suite exchange** and **negotiation-based security**. 18 | - Implements **authentication and key agreement steps**. 19 | - Ensures **session key derivation** is executed securely. 20 | 21 | ### 🏗 **Error Handling** 22 | - Provides structured errors through `HandshakeError`. 23 | - Handles **negotiation failures**, **invalid responses**, and **I/O errors** gracefully. 24 | 25 | ### ⚡ **Asynchronous & Efficient** 26 | - Uses **Tokio’s async runtime** for high-performance execution. 27 | - Supports streaming-based handshake execution. 28 | 29 | --- 30 | 31 | ## 🏛 **Architecture Overview** 32 | 33 | This module consists of the following core components: 34 | 35 | ### **1️⃣ Handshake Engine** 36 | - **`Handshake`** – Orchestrates a sequence of steps to establish communication. 37 | - Manages **adding, removing, and executing** handshake steps. 38 | 39 | ### **2️⃣ Handshake Steps** 40 | - **`NodeHello`** – Initiates the handshake by sending a "HELLO". 41 | - **`HelloResponse`** – Responds to the hello message. 42 | - **`CipherSuiteExchange`** – Negotiates available cipher suites. 43 | - **`CipherSuiteAck`** – Acknowledges a selected cipher suite. 44 | - **`CustomProtocolStep`** – Allows integration of user-defined handshake logic. 45 | 46 | ### **3️⃣ Handshake Traits** 47 | - **`HandshakeStep`** – Defines individual handshake steps. 48 | - **`HandshakeStream`** – Abstracts the underlying I/O stream for transport. 49 | 50 | ### **4️⃣ Error Handling** 51 | - **`HandshakeError`** – Covers: 52 | - **NegotiationFailed** – No common cipher suite found. 53 | - **AuthenticationFailed** – Identity verification failure. 54 | - **KeyAgreementFailed** – Issues in key exchange. 55 | - **SessionKeyDerivationFailed** – Secure session establishment failure. 56 | 57 | --- 58 | 59 | 60 | # ⚠ Limitations 61 | - Requires a defined protocol – Steps must be compatible with the handshake protocol ID. 62 | - No built-in encryption – The handshake framework negotiates security but doesn’t encrypt messages by default. 63 | - Does not support automatic retry – If a handshake fails, it must be retried manually. 64 | # 🎯 Future Improvements 65 | - ✅ Extend with TLS-based Handshake Support – Allow integration with existing TLS implementations. 66 | - ✅ Add Retry & Timeout Handling – Improve reliability in unstable networks. 67 | - ✅ Integrate with Decentralized Identity Systems – Support DID-based authentication. 68 | -------------------------------------------------------------------------------- /protocols/handshake/examples/handshake_testing.rs: -------------------------------------------------------------------------------- 1 | use tokio::net::{TcpListener, TcpStream}; 2 | use handshake::{Handshake, NodeHello, HelloResponse, CipherSuiteExchange, CipherSuiteAck}; 3 | 4 | 5 | /// Function to run a server on a specified port 6 | /// Function to run a server on a specified port 7 | async fn run_server(port: u16) { 8 | let address = format!("127.0.0.1:{}", port); 9 | let listener = TcpListener::bind(&address).await.unwrap(); 10 | println!("Server listening on {}", address); 11 | 12 | loop { 13 | let (mut stream, _) = listener.accept().await.unwrap(); 14 | tokio::spawn(async move { 15 | // Initialize handshake 16 | let mut handshake = Handshake::new("protocol_a"); 17 | handshake.add_step(Box::new(NodeHello::new())); // Wrap in Box 18 | handshake.add_step(Box::new(HelloResponse::new())); // Wrap in Box 19 | handshake.add_step(Box::new(CipherSuiteExchange::new())); // Wrap in Box 20 | 21 | // Execute the handshake 22 | match handshake.execute(&mut stream).await { 23 | Ok(_) => println!("Server: Handshake completed successfully!"), 24 | Err(e) => eprintln!("Server: Handshake failed: {}", e), 25 | } 26 | }); 27 | } 28 | } 29 | 30 | /// Function to run a client connecting to a specific server port 31 | async fn run_client(port: u16) { 32 | let address = format!("127.0.0.1:{}", port); 33 | tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; 34 | 35 | match TcpStream::connect(&address).await { 36 | Ok(mut stream) => { 37 | println!("Client connected to {}", address); 38 | 39 | 40 | let mut handshake = Handshake::new("protocol_a"); 41 | handshake.add_step(Box::new(NodeHello::new())); // Wrap in Box 42 | handshake.add_step(Box::new(HelloResponse::new())); // Wrap in Box 43 | handshake.add_step(Box::new(CipherSuiteExchange::new())); // Wrap in Box 44 | handshake.add_step(Box::new(CipherSuiteAck::new())); // Wrap in Box 45 | 46 | // Execute the handshake 47 | match handshake.execute(&mut stream).await { 48 | Ok(_) => println!("Client: Handshake completed successfully!"), 49 | Err(e) => eprintln!("Client: Handshake failed: {}", e), 50 | } 51 | } 52 | Err(e) => { 53 | eprintln!("Client: Failed to connect to {}: {}", address, e); 54 | } 55 | } 56 | } 57 | 58 | #[tokio::main] 59 | async fn main() { 60 | // Run the servers on ports 8080 and 8082 61 | let server_8080 = run_server(8080); 62 | 63 | // Run the clients connecting to the server; 64 | let client_8082 = run_client(8080); 65 | 66 | // Join all tasks 67 | tokio::join!(server_8080, client_8082); 68 | 69 | println!("All tasks completed."); 70 | } 71 | -------------------------------------------------------------------------------- /protocols/handshake/src/handshake.rs: -------------------------------------------------------------------------------- 1 | use crate::traits::{HandshakeStep, HandshakeStream}; 2 | use crate::handshake_error::HandshakeError; 3 | use std::collections::VecDeque; 4 | 5 | pub struct Handshake { 6 | protocol_id: String, 7 | steps: VecDeque>, 8 | } 9 | 10 | impl Handshake { 11 | /// Create a new handshake with an empty list of steps. 12 | pub fn new(protocol_id: &str) -> Self { 13 | Self { 14 | protocol_id: protocol_id.to_string(), 15 | steps: VecDeque::new(), 16 | } 17 | } 18 | 19 | /// Get the protocol ID 20 | pub fn protocol_id(&self) -> &str { 21 | &self.protocol_id 22 | } 23 | 24 | /// Add a new step to the handshake. 25 | pub fn add_step(&mut self, mut step: Box) { 26 | step.set_protocol_id(&self.protocol_id); 27 | self.steps.push_back(step); 28 | } 29 | 30 | /// Insert a step at a specific position. 31 | pub fn insert_step(&mut self, index: usize, step: Box) -> Result<(), HandshakeError> { 32 | if index <= self.steps.len() { 33 | self.steps.insert(index, step); 34 | Ok(()) 35 | } else { 36 | Err(HandshakeError::Generic(format!( 37 | "Index {} is out of bounds for inserting a step.", 38 | index 39 | ))) 40 | } 41 | } 42 | 43 | /// Remove a step by index. 44 | pub fn remove_step(&mut self, index: usize) -> Result<(), HandshakeError> { 45 | if index < self.steps.len() { 46 | self.steps.remove(index); 47 | Ok(()) 48 | } else { 49 | Err(HandshakeError::Generic(format!( 50 | "Index {} is out of bounds for removing a step.", 51 | index 52 | ))) 53 | } 54 | } 55 | 56 | /// Replace a step at a specific index. 57 | pub fn update_step(&mut self, index: usize, step: Box) -> Result<(), HandshakeError> { 58 | if index < self.steps.len() { 59 | self.steps[index] = step; 60 | Ok(()) 61 | } else { 62 | Err(HandshakeError::Generic(format!( 63 | "Index {} is out of bounds for updating a step.", 64 | index 65 | ))) 66 | } 67 | } 68 | 69 | /// Retrieve a reference to the current steps. 70 | pub fn list_steps(&self) -> Vec<&dyn HandshakeStep> { 71 | self.steps.iter().map(|step| step.as_ref()).collect() 72 | } 73 | 74 | /// **Execute** the handshake. Returns the final `Vec` from the last step. 75 | pub async fn execute( 76 | &mut self, 77 | stream: &mut dyn HandshakeStream, 78 | ) -> Result, HandshakeError> { 79 | let mut input = Vec::new(); 80 | for step in &mut self.steps { 81 | if step.supports_protocol(&self.protocol_id) { 82 | // Each step returns a new Vec 83 | input = step.execute(stream, input).await?; 84 | } else { 85 | eprintln!( 86 | "Skipping step due to protocol mismatch: Expected '{}', Found '{}'", 87 | self.protocol_id, 88 | step.get_protocol_id() 89 | ); 90 | } 91 | } 92 | // Return the final data from the handshake 93 | Ok(input) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /protocols/handshake/src/handshake_error.rs: -------------------------------------------------------------------------------- 1 | // protocols/handshake/src/handshake_error.rs 2 | #[derive(Debug, thiserror::Error)] 3 | pub enum HandshakeError { 4 | #[error("Cipher suite negotiation failed: {0}")] 5 | NegotiationFailed(String), 6 | 7 | #[error("Authentication failed: {0}")] 8 | AuthenticationFailed(String), 9 | 10 | #[error("Key agreement failed: {0}")] 11 | KeyAgreementFailed(String), 12 | 13 | #[error("Session key derivation failed: {0}")] 14 | SessionKeyDerivationFailed(String), 15 | 16 | #[error("Generic handshake error: {0}")] 17 | Generic(String), 18 | 19 | #[error("I/O error: {0}")] 20 | IoError(#[from] std::io::Error), 21 | 22 | #[error("Invalid hello response")] 23 | InvalidHelloResponse, 24 | 25 | #[error("Step error: {0}")] 26 | StepError(String), 27 | 28 | #[error("Negotiation failed: {0}")] 29 | NegotiationError(String), 30 | } 31 | -------------------------------------------------------------------------------- /protocols/handshake/src/lib.rs: -------------------------------------------------------------------------------- 1 | // protocols/handshake/src/lib.rs 2 | mod handshake; 3 | mod handshake_error; 4 | mod traits; 5 | mod steps; 6 | 7 | pub use handshake::Handshake; 8 | pub use handshake_error::HandshakeError; 9 | pub use traits::{HandshakeStep,HandshakeStream}; 10 | pub use steps::{CipherSuiteAck,CipherSuiteExchange,NodeHello,HelloResponse,CustomProtocolStep}; -------------------------------------------------------------------------------- /protocols/handshake/src/traits.rs: -------------------------------------------------------------------------------- 1 | use tokio::io::{AsyncRead, AsyncWrite}; 2 | use futures::future::BoxFuture; 3 | use crate::handshake_error::HandshakeError; 4 | 5 | pub trait HandshakeStep: Send + Sync { 6 | /// Get the protocol ID of the step 7 | fn get_protocol_id(&self) -> &str; 8 | 9 | /// Set the protocol ID for the step 10 | fn set_protocol_id(&mut self, protocol_id: &str); 11 | 12 | /// Check if the step supports the given protocol ID 13 | fn supports_protocol(&self, protocol_id: &str) -> bool { 14 | self.get_protocol_id() == protocol_id 15 | } 16 | 17 | /// Execute the step 18 | fn execute<'a>( 19 | &'a mut self, 20 | stream: &'a mut dyn HandshakeStream, 21 | input: Vec, 22 | ) -> BoxFuture<'a, Result, HandshakeError>>; 23 | } 24 | pub trait HandshakeStream: AsyncRead + AsyncWrite + Unpin + Send {} 25 | impl HandshakeStream for T {} 26 | -------------------------------------------------------------------------------- /protocols/handshake/tests/basic_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod basic_testing { 3 | use tokio::net::{TcpListener, TcpStream}; 4 | use handshake::{Handshake, NodeHello, HelloResponse, CipherSuiteExchange, CipherSuiteAck}; 5 | 6 | 7 | #[tokio::test] 8 | async fn test_basic_handshake() { 9 | // Run a mock server 10 | let server = tokio::spawn(async { 11 | let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap(); 12 | let (mut stream, _) = listener.accept().await.unwrap(); 13 | 14 | let mut handshake = Handshake::new("protocol_a"); 15 | handshake.add_step(Box::new(NodeHello::new())); 16 | handshake.add_step(Box::new(HelloResponse::new())); 17 | handshake.add_step(Box::new(CipherSuiteExchange::new())); 18 | handshake.add_step(Box::new(CipherSuiteAck::new())); 19 | 20 | let result = handshake.execute(&mut stream).await; 21 | assert!(result.is_ok(), "Server handshake failed: {:?}", result); 22 | }); 23 | 24 | // Run a mock client 25 | let client = tokio::spawn(async { 26 | let mut stream = TcpStream::connect("127.0.0.1:8080").await.unwrap(); 27 | 28 | let mut handshake = Handshake::new("protocol_a"); 29 | handshake.add_step(Box::new(NodeHello::new())); 30 | handshake.add_step(Box::new(HelloResponse::new())); 31 | handshake.add_step(Box::new(CipherSuiteExchange::new())); 32 | handshake.add_step(Box::new(CipherSuiteAck::new())); 33 | 34 | let result = handshake.execute(&mut stream).await; 35 | assert!(result.is_ok(), "Client handshake failed: {:?}", result); 36 | }); 37 | 38 | let _ = tokio::join!(server, client); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /protocols/handshake/tests/integeration_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod complex_handshake_test { 3 | use tokio::net::{TcpListener, TcpStream}; 4 | use handshake::{ 5 | Handshake, NodeHello, HelloResponse, CipherSuiteExchange,HandshakeError,HandshakeStream, HandshakeStep, 6 | }; 7 | use tokio::io::{AsyncWriteExt,AsyncReadExt}; 8 | use futures::future::BoxFuture; 9 | 10 | 11 | pub struct EncryptedDataExchange { 12 | protocol_id: String, 13 | } 14 | 15 | impl EncryptedDataExchange { 16 | pub fn new(protocol_id: &str) -> Self { 17 | Self { 18 | protocol_id: protocol_id.to_string(), 19 | } 20 | } 21 | } 22 | 23 | impl HandshakeStep for EncryptedDataExchange { 24 | fn get_protocol_id(&self) -> &str { 25 | &self.protocol_id 26 | } 27 | 28 | fn set_protocol_id(&mut self, protocol_id: &str) { 29 | self.protocol_id = protocol_id.to_string(); 30 | } 31 | 32 | fn execute<'a>( 33 | &'a mut self, 34 | stream: &'a mut dyn HandshakeStream, 35 | _: Vec, 36 | ) -> BoxFuture<'a, Result, HandshakeError>> { 37 | Box::pin(async move { 38 | // Simulating encryption key exchange 39 | let encryption_key = b"ENCRYPTED_KEY"; // Length is 13 40 | stream 41 | .write_all(encryption_key) 42 | .await 43 | .map_err(|e| HandshakeError::Generic(e.to_string()))?; 44 | println!("N1 -> N2: Sending encryption key"); 45 | 46 | let mut buffer = [0u8; 13]; // Match the length of encryption_key 47 | stream 48 | .read_exact(&mut buffer) 49 | .await 50 | .map_err(|e| HandshakeError::Generic(e.to_string()))?; 51 | if &buffer == encryption_key { 52 | println!("N1 <- N2: Encryption key exchange confirmed"); 53 | Ok(vec![]) 54 | } else { 55 | Err(HandshakeError::NegotiationFailed( 56 | "Encryption key mismatch".to_string(), 57 | )) 58 | } 59 | }) 60 | } 61 | } 62 | 63 | #[tokio::test] 64 | async fn test_complex_handshake() { 65 | let server = tokio::spawn(async { 66 | let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap(); 67 | let (mut stream, _) = listener.accept().await.unwrap(); 68 | 69 | let mut handshake = Handshake::new("protocol_a"); 70 | handshake.add_step(Box::new(NodeHello::new())); 71 | handshake.add_step(Box::new(HelloResponse::new())); 72 | handshake.add_step(Box::new(CipherSuiteExchange::new())); 73 | handshake.add_step(Box::new(EncryptedDataExchange::new("protocol_a"))); 74 | 75 | let result = handshake.execute(&mut stream).await; 76 | assert!(result.is_ok(), "Server handshake failed: {:?}", result); 77 | }); 78 | 79 | let client = tokio::spawn(async { 80 | let mut stream = TcpStream::connect("127.0.0.1:8080").await.unwrap(); 81 | 82 | let mut handshake = Handshake::new("protocol_a"); 83 | handshake.add_step(Box::new(NodeHello::new())); 84 | handshake.add_step(Box::new(HelloResponse::new())); 85 | handshake.add_step(Box::new(CipherSuiteExchange::new())); 86 | handshake.add_step(Box::new(EncryptedDataExchange::new("protocol_a"))); 87 | 88 | let result = handshake.execute(&mut stream).await; 89 | assert!(result.is_ok(), "Client handshake failed: {:?}", result); 90 | }); 91 | 92 | let _ = tokio::join!(server, client); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /protocols/kad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = {version = "0.8.5"} 8 | serde = { version = "1.0", features = ["derive"] } 9 | serde_json = {version = "1.0.0"} 10 | bincode = {version = "1.0.0"} 11 | tokio = {version = "1.0.0"} 12 | udp = {path = "../../transport/udp"} 13 | nautilus_core = {path = "../../core"} 14 | hex = "0.4" -------------------------------------------------------------------------------- /protocols/kad/README.md: -------------------------------------------------------------------------------- 1 | # Nautilus Kademlia (KAD) Distributed Hash Table (DHT) 2 | 3 | The `nautilus_kad` module provides a **fully decentralized routing protocol** based on **Kademlia (KAD)**. It is designed to efficiently store and locate nodes in a **peer-to-peer (P2P) network**, making it ideal for: 4 | - **Decentralized file sharing** 5 | - **Blockchain networks** 6 | - **Decentralized identity & communication** 7 | 8 | This module is built in **Rust**, leveraging **Tokio** for asynchronous networking and **UDP** for lightweight communication. 9 | 10 | --- 11 | 12 | ## 🌟 Features 13 | 14 | ### 🗺 **Decentralized Node Lookup** 15 | - Uses **XOR distance metric** to find the **closest** nodes. 16 | - Implements **iterative node lookup** for efficient routing. 17 | - Supports **bootstrapping into an existing network**. 18 | 19 | ### ⚡ **Efficient Routing & Storage** 20 | - Implements **160-bit node IDs** for structured routing. 21 | - **Routing table management** with **bucket-based node storage**. 22 | - Supports **network maintenance** and **self-healing**. 23 | 24 | ### 🔄 **Kademlia Protocol Support** 25 | - **Ping/Pong** – Check node availability. 26 | - **FindNode** – Locate the closest nodes to a target ID. 27 | - **NodeFound** – Respond to node queries. 28 | - **Store & Retrieve** – (Future extension) Store & retrieve values from the DHT. 29 | 30 | ### 🛠 **Built-in Error Handling** 31 | - Gracefully handles **unresponsive nodes**. 32 | - Implements **timeouts** to avoid infinite lookups. 33 | - Provides structured errors for **better debugging**. 34 | 35 | --- 36 | 37 | ## 🏛 **Architecture Overview** 38 | 39 | This module consists of several key components: 40 | 41 | ### **1️⃣ Node & Routing** 42 | - **`Node`** – Represents a Kademlia node (`id` + `IP address`). 43 | - **`RoutingTable`** – Stores nodes in **buckets**, enabling efficient lookups. 44 | - **`xor_distance`** – Calculates XOR distance for **closest-node selection**. 45 | 46 | ### **2️⃣ Kademlia Protocol** 47 | - **`KadProtocol`** – Core logic for handling Kademlia operations. 48 | - **`KadMessage`** – Defines Kademlia messages (`Ping`, `FindNode`, etc.). 49 | - **`Bootstrapper`** – Manages the **initial network entry**. 50 | 51 | ### **3️⃣ Communication** 52 | - **`UdpConnection`** – Handles **UDP-based message passing**. 53 | - **`query_find_node`** – Sends queries to locate **closest nodes**. 54 | 55 | --- 56 | 57 | 58 | # ⚠ Limitations 59 | - No Data Storage Yet – Currently supports node lookup only (Store/Retrieve operations not implemented yet). 60 | - UDP-Based – Not optimized for TCP-based communications. 61 | - Fixed Bucket Sizes – Routing table is limited to 160 buckets. 62 | # 🎯 Future Improvements 63 | - ✅ Support for Storing & Retrieving Values – Implement Store and ValueFound operations. 64 | - ✅ Optimize Routing Table – Improve node eviction and bucket management. 65 | - ✅ Add TCP Support – Extend support beyond UDP for better reliability. 66 | # 📜 License 67 | This project is licensed under the MIT License. See the LICENSE file for details. 68 | 69 | # 🤝 Contributing 70 | Contributions are welcome! Open an issue or submit a pull request with your improvements. -------------------------------------------------------------------------------- /protocols/kad/examples/bootstrap_service.rs: -------------------------------------------------------------------------------- 1 | use kad::{bootstrap::Bootstrapper, kad_protocol::KadProtocol, node::Node}; 2 | use std::net::SocketAddr; 3 | use kad::utils::generate_random_node_id; 4 | use tokio::signal; 5 | 6 | /// Function to spawn a bootstrap service 7 | pub async fn spawn_bootstrap_service() -> (Node, tokio::task::JoinHandle<()>) { 8 | let port = 5000; // Fixed port for the bootstrap service 9 | let address: SocketAddr = format!("127.0.0.1:{}", port) 10 | .parse() 11 | .expect("Failed to parse bootstrap address"); 12 | 13 | // Create the bootstrap node 14 | let node_id = generate_random_node_id(); 15 | let bootstrap_node = Node::new(node_id.clone(), address); 16 | 17 | // Initialize KadProtocol for the bootstrap node 18 | let kad_protocol = KadProtocol::new(bootstrap_node.clone()).await; 19 | 20 | // Bind the bootstrap node to the address 21 | kad_protocol 22 | .lock() 23 | .await 24 | .bind(&address.to_string()) 25 | .await 26 | .expect("Failed to bind bootstrap node to address"); 27 | 28 | // Create the bootstrapper 29 | let bootstrapper = Bootstrapper::new(kad_protocol.clone(), None, true); 30 | 31 | // Spawn the bootstrap service in a separate task 32 | let handle = tokio::spawn(async move { 33 | println!("Bootstrap service running at {}", address); 34 | bootstrapper.bootstrap().await; 35 | 36 | // Run the KadProtocol event loop 37 | if let Err(e) = kad_protocol.lock().await.run().await { 38 | eprintln!("Bootstrap service error: {}", e); 39 | } 40 | }); 41 | 42 | (bootstrap_node, handle) 43 | } 44 | 45 | /// Main function 46 | #[tokio::main] 47 | async fn main() { 48 | // Start the bootstrap service 49 | let (bootstrap_node, bootstrap_handle) = spawn_bootstrap_service().await; 50 | 51 | println!( 52 | "Bootstrap service started with ID: {:?} at {}", 53 | bootstrap_node.id, bootstrap_node.address 54 | ); 55 | 56 | // Wait for Ctrl+C to shut down 57 | signal::ctrl_c() 58 | .await 59 | .expect("Failed to listen for Ctrl+C"); 60 | 61 | println!("Shutting down bootstrap service..."); 62 | bootstrap_handle.abort(); // Gracefully stop the bootstrap service 63 | println!("Bootstrap service shut down."); 64 | } 65 | -------------------------------------------------------------------------------- /protocols/kad/src/behaviour/behaviour.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\behaviour\behaviour.rs 2 | use crate::node::Node; 3 | 4 | pub trait KadBehaviour { 5 | /// Called when a new node is discovered. 6 | fn on_node_discovered(&mut self, node: Node); 7 | 8 | /// Periodic update for maintaining the routing table. 9 | fn on_maintenance(&mut self); 10 | 11 | /// Handle incoming messages related to the behaviour. 12 | fn on_message(&mut self, message: &[u8], sender: &Node); 13 | } 14 | -------------------------------------------------------------------------------- /protocols/kad/src/behaviour/learning.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\behaviour\learning.rs 2 | use crate::node::Node; 3 | use crate::routing_table::RoutingTable; 4 | use crate::kad_protocol::KadProtocol; // Import KadProtocol for interaction 5 | use std::sync::Arc; 6 | use tokio::sync::Mutex; 7 | /// Handles learning and managing nodes within the routing table. 8 | pub struct LearningBehaviour { 9 | routing_table: Arc>, 10 | kad_protocol: Option>>, // Accept wrapped KadProtocol 11 | } 12 | 13 | impl LearningBehaviour { 14 | pub fn new(routing_table: Arc>, kad_protocol: Option>>) -> Self { 15 | Self { 16 | routing_table, 17 | kad_protocol, 18 | } 19 | } 20 | 21 | pub fn set_kad_protocol(&mut self, kad_protocol: Arc>) { 22 | self.kad_protocol = Some(kad_protocol); // Accept wrapped Arc> 23 | } 24 | pub async fn learn_node(&self, new_node: Node) { 25 | let mut stack = vec![new_node]; 26 | 27 | while let Some(node) = stack.pop() { 28 | let mut routing_table = self.routing_table.lock().await; 29 | 30 | // Add the node to the routing table 31 | if routing_table.add_node(node.clone()) { 32 | println!("Node {:?} added to routing table via learning.", node.id); 33 | } 34 | 35 | // Query neighbors of the node to expand knowledge 36 | if let Some(kad_protocol) = &self.kad_protocol { 37 | let kad_protocol_lock = kad_protocol.lock().await; // Lock kad_protocol 38 | if let Ok(neighbors) = kad_protocol_lock 39 | .query_find_node(&node, kad_protocol_lock.local_node.id.clone()) 40 | .await 41 | { 42 | for neighbor in neighbors { 43 | stack.push(neighbor); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /protocols/kad/src/behaviour/maintenance.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\behaviour\maintenance.rs 2 | use crate::routing_table::RoutingTable; 3 | use std::sync::Arc; 4 | use tokio::sync::Mutex; 5 | 6 | pub struct MaintenanceBehaviour { 7 | routing_table: Arc>, 8 | } 9 | 10 | impl MaintenanceBehaviour { 11 | pub fn new(routing_table: Arc>) -> Self { 12 | Self { routing_table } 13 | } 14 | 15 | /// Refresh buckets periodically by querying random nodes. 16 | pub async fn refresh_buckets(&self) { 17 | let routing_table = self.routing_table.lock().await; 18 | 19 | for (i, bucket) in routing_table.buckets.iter().enumerate() { 20 | if bucket.is_empty() { 21 | continue; // Skip empty buckets 22 | } 23 | 24 | if let Some(random_node) = routing_table.get_random_node(i) { 25 | println!("Refreshing bucket {} by querying node {:?}", i, random_node.id); 26 | // TODO: Send FIND_NODE message to `random_node` 27 | } 28 | } 29 | } 30 | 31 | /// Remove unreachable nodes from the routing table. 32 | pub async fn remove_stale_nodes(&self) { 33 | let mut routing_table = self.routing_table.lock().await; 34 | 35 | let stale_nodes: Vec<_> = routing_table 36 | .get_all_nodes() 37 | .into_iter() 38 | .filter(|_node| { 39 | // TODO: Implement logic to check node reachability (e.g., send PING) 40 | false // Replace with actual logic 41 | }) 42 | .collect(); 43 | 44 | for node in stale_nodes { 45 | if routing_table.remove_node(&node) { 46 | println!("Removed stale node {:?}", node.id); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /protocols/kad/src/behaviour/messaging.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\behaviour\messaging.rs 2 | use crate::kad_message::{KadMessage, MessageType}; 3 | use crate::node::Node; 4 | 5 | pub struct MessagingBehaviour; 6 | 7 | impl MessagingBehaviour { 8 | pub fn handle_message(message: KadMessage, sender: Node) { 9 | match message.message_type { 10 | MessageType::Ping => { 11 | println!("Received PING from {:?}", sender); 12 | // TODO: Respond with PONG 13 | } 14 | MessageType::FindNode => { 15 | println!("Received FIND_NODE from {:?}", sender); 16 | // TODO: Respond with closest nodes 17 | } 18 | _ => { 19 | println!("Unhandled message type {:?}", message.message_type); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /protocols/kad/src/behaviour/mod.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\behaviour\mod.rs 2 | pub mod behaviour; 3 | pub mod learning; 4 | pub mod maintenance; 5 | pub mod messaging; 6 | 7 | pub use behaviour::*; 8 | pub use learning::*; 9 | pub use maintenance::*; 10 | pub use messaging::*; 11 | -------------------------------------------------------------------------------- /protocols/kad/src/bootstrap.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use tokio::sync::Mutex; 3 | 4 | use crate::kad_protocol::KadProtocol; 5 | use crate::node::Node; 6 | 7 | pub struct Bootstrapper { 8 | pub kad: Arc>, 9 | pub bootstrap_node: Option, // Use `bootstrap_node` instead of `known_node`. 10 | pub is_bootstrap_node: bool, 11 | } 12 | 13 | impl Bootstrapper { 14 | pub fn new( 15 | kad: Arc>, 16 | bootstrap_node: Option, 17 | is_bootstrap_node: bool, 18 | ) -> Self { 19 | Self { 20 | kad, 21 | bootstrap_node, 22 | is_bootstrap_node, 23 | } 24 | } 25 | 26 | pub async fn bootstrap(&self) { 27 | if self.is_bootstrap_node { 28 | println!("Operating as a bootstrap node."); 29 | let kad = self.kad.clone(); 30 | let kad_protocol = kad.lock().await; 31 | kad_protocol.add_node(kad_protocol.local_node.clone()).await; 32 | return; 33 | } 34 | 35 | if let Some(bootstrap_node) = &self.bootstrap_node { 36 | println!( 37 | "Starting bootstrap process with known bootstrap node: {:?}", 38 | bootstrap_node 39 | ); 40 | let kad = self.kad.clone(); 41 | let kad_protocol = kad.lock().await; 42 | kad_protocol.add_node(bootstrap_node.clone()).await; 43 | 44 | // Try a FIND_NODE to populate the routing table 45 | if let Ok(nodes) = kad_protocol 46 | .query_find_node(bootstrap_node, kad_protocol.local_node.id.clone()) 47 | .await 48 | { 49 | for node in nodes { 50 | kad_protocol.add_node(node).await; 51 | } 52 | } else { 53 | println!("Failed to communicate with the bootstrap node."); 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /protocols/kad/src/kad_message.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\kad_message.rs 2 | use serde::{Serialize, Deserialize}; 3 | use crate::node::NodeId; 4 | 5 | #[derive(Debug, Serialize, Deserialize,PartialEq)] 6 | pub enum MessageType { 7 | Ping, 8 | Pong, 9 | FindNode, 10 | NodeFound, 11 | Store, 12 | ValueFound, 13 | } 14 | 15 | #[derive(Debug, Serialize, Deserialize)] 16 | pub struct KadMessage { 17 | pub message_type: MessageType, 18 | pub sender_id: NodeId, 19 | pub payload: Option, // Ensure this is a String for serialized data 20 | } 21 | 22 | impl KadMessage { 23 | pub fn new(message_type: MessageType, sender_id: NodeId, payload: Option) -> Self { 24 | KadMessage { 25 | message_type, 26 | sender_id, 27 | payload, 28 | } 29 | } 30 | 31 | pub fn serialize(&self) -> Vec { 32 | bincode::serialize(self).expect("Failed to serialize message") 33 | } 34 | 35 | pub fn deserialize(data: &[u8]) -> Result> { 36 | bincode::deserialize(data) 37 | .map_err(|e| Box::::from(e)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /protocols/kad/src/lib.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\lib.rs 2 | pub mod node; 3 | pub mod xor_distance; 4 | pub mod routing_table; 5 | pub mod kad_protocol; 6 | pub mod kad_message; 7 | pub mod utils; 8 | pub mod bootstrap; 9 | pub mod behaviour; -------------------------------------------------------------------------------- /protocols/kad/src/node.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\node.rs 2 | use std::net::SocketAddr; 3 | use serde::{Deserialize,Serialize}; 4 | /// A 160-bit Node ID for identifying nodes. 5 | pub type NodeId = [u8; 20]; 6 | 7 | /// Represents a node in the Kademlia network. 8 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 9 | pub struct Node { 10 | pub id: NodeId, 11 | pub address: SocketAddr, 12 | } 13 | 14 | impl Node { 15 | /// Creates a new Node with a given ID and address. 16 | pub fn new(id: NodeId, address: SocketAddr) -> Self { 17 | Self { id, address } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /protocols/kad/src/utils.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\utils.rs 2 | use rand::Rng; 3 | use crate::node::NodeId; 4 | 5 | /// Generates a random Node ID. 6 | pub fn generate_random_node_id() -> NodeId { 7 | let mut rng = rand::thread_rng(); 8 | let mut id = [0u8; 20]; 9 | rng.fill(&mut id); 10 | id 11 | } 12 | -------------------------------------------------------------------------------- /protocols/kad/src/xor_distance.rs: -------------------------------------------------------------------------------- 1 | // protocols\kad\src\xor_distance.rs 2 | use crate::node::NodeId; 3 | 4 | /// Calculates the XOR distance between two Node IDs. 5 | pub fn xor_distance(a: &NodeId, b: &NodeId) -> NodeId { 6 | let mut distance = [0u8; 20]; 7 | for i in 0..20 { 8 | distance[i] = a[i] ^ b[i]; 9 | } 10 | distance 11 | } 12 | 13 | pub fn is_closer(node1: &NodeId, node2: &NodeId, target: &NodeId) -> bool { 14 | let distance1 = xor_distance(node1, target); 15 | let distance2 = xor_distance(node2, target); 16 | 17 | for i in 0..20 { 18 | if distance1[i] < distance2[i] { 19 | return true; 20 | } else if distance1[i] > distance2[i] { 21 | return false; 22 | } 23 | } 24 | false 25 | } 26 | -------------------------------------------------------------------------------- /protocols/mdns/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /protocols/mdns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdns" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.42", features = ["full"] } 8 | serde = { version = "1.0", features = ["derive"] } 9 | bytes = {version = "1.4"} 10 | socket2 = { version = "0.5.8" } 11 | registry = {path = "../../utilities/registry"} -------------------------------------------------------------------------------- /protocols/mdns/examples/mdns_example.rs: -------------------------------------------------------------------------------- 1 | // protocols\mdns\examples\mdns_example.rs 2 | //! # Example: Using the Nautilus mDNS Service for Discovery and Advertisement 3 | //! 4 | //! This example demonstrates how to utilize the `MdnsService` from the `nautilus_mdns` crate 5 | //! to discover and advertise services over the local network using the mDNS (Multicast DNS) protocol. 6 | //! 7 | //! The example covers the following functionalities: 8 | //! - Initializing an mDNS service with a custom node name. 9 | //! - Running the mDNS service to advertise and query for services. 10 | //! - Listening for mDNS events. 11 | //! - Handling shutdown signals and printing discovered nodes before exiting. 12 | 13 | use mdns::MdnsService; 14 | use std::sync::Arc; 15 | use tokio::{signal, spawn}; 16 | 17 | /// Entry point of the asynchronous mDNS service example. 18 | #[tokio::main] 19 | async fn main() -> Result<(), Box> { 20 | // Create the mDNS service instance with: 21 | // - A node origin of "MyLaptop.local" (custom hostname). 22 | // - A default service type of "_mdnsnode._tcp.local." (compulsory for this implementation). 23 | let mdns_service = MdnsService::new( 24 | Some("MyLaptop.local".to_string()), // Node's local hostname 25 | "_mdnsnode._tcp.local.", // Default service type 26 | ) 27 | .await?; 28 | 29 | // Wrap the service in an `Arc` (atomic reference counting) for thread-safe sharing across tasks. 30 | let mdns_service = Arc::new(mdns_service); 31 | 32 | // Clone the Arc to share the mDNS service instance across tasks 33 | let mdns_clone = Arc::clone(&mdns_service); 34 | spawn(async move { 35 | // Start the mDNS service to: 36 | // - Query for "_myservice._http._tcp.local." every 5 seconds. 37 | // - Advertise the service every 10 seconds. 38 | mdns_clone 39 | .run("_myservice._http._tcp.local.".to_string(), 5, 10) 40 | .await; 41 | }); 42 | 43 | // Get the event receiver to handle discovered mDNS events. 44 | let mut receiver = mdns_service.get_event_receiver(); 45 | spawn(async move { 46 | while let Ok(event) = receiver.recv().await { 47 | // Print each received mDNS event to the console 48 | println!("(EVENT) => {:?}", event); 49 | } 50 | }); 51 | 52 | // Wait for a termination signal (Ctrl-C) to gracefully shut down the service. 53 | signal::ctrl_c().await?; 54 | println!("(MAIN) Shutdown signal received."); 55 | 56 | // Once the service is shut down, retrieve and print the discovered nodes. 57 | let nodes = mdns_service.registry.list_nodes().await; 58 | println!("Discovered nodes: {:?}", nodes); 59 | 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /protocols/mdns/src/behaviour.rs: -------------------------------------------------------------------------------- 1 | // protocols\mdns\src\behaviour.rs 2 | 3 | // ================================================= 4 | // Module Imports 5 | mod mdns_event; 6 | mod mdns_error; 7 | mod mdns_service; 8 | mod records; 9 | 10 | // ================================================= 11 | 12 | // Public Exports 13 | pub use mdns_event::MdnsEvent; 14 | pub use mdns_error::MdnsError; 15 | pub use mdns_service::MdnsService; 16 | pub use records::{MdnsRegistry, ServiceRecord, NodeRecord}; 17 | // ================================================= -------------------------------------------------------------------------------- /protocols/mdns/src/behaviour/mdns_error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// Represents errors that can occur in the mDNS service. 4 | #[derive(Debug)] 5 | pub enum MdnsError { 6 | /// An error occurred during DNS packet serialization/deserialization. 7 | PacketError(String), 8 | 9 | /// An error occurred while managing multicast groups. 10 | MulticastError(String), 11 | 12 | /// A network-related error, e.g., socket bind failure. 13 | NetworkError(std::io::Error), 14 | 15 | /// Indicates a timeout during mDNS operations. 16 | Timeout(String), 17 | 18 | /// A generic error for uncategorized issues. 19 | Generic(String), 20 | } 21 | 22 | impl fmt::Display for MdnsError { 23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 24 | match self { 25 | MdnsError::PacketError(msg) => write!(f, "Packet error: {}", msg), 26 | MdnsError::MulticastError(msg) => write!(f, "Multicast error: {}", msg), 27 | MdnsError::NetworkError(err) => write!(f, "Network error: {}", err), 28 | MdnsError::Timeout(msg) => write!(f, "Timeout: {}", msg), 29 | MdnsError::Generic(msg) => write!(f, "Error: {}", msg), 30 | } 31 | } 32 | } 33 | 34 | impl From for MdnsError { 35 | fn from(err: std::io::Error) -> Self { 36 | MdnsError::NetworkError(err) 37 | } 38 | } 39 | 40 | impl std::error::Error for MdnsError {} 41 | -------------------------------------------------------------------------------- /protocols/mdns/src/behaviour/mdns_event.rs: -------------------------------------------------------------------------------- 1 | /// Events emitted by the mDNS protocol behavior. 2 | use crate::record::DnsRecord; 3 | use crate::packet::DnsQuestion; 4 | #[derive(Debug,Clone)] 5 | pub enum MdnsEvent { 6 | /// A new service or peer has been discovered. 7 | Discovered(DnsRecord), 8 | 9 | /// An existing record has been updated (e.g., TTL refreshed). 10 | Updated(DnsRecord), 11 | 12 | /// A record has expired and been removed from the cache. 13 | Expired(DnsRecord), 14 | 15 | /// A query has been sent, and a response has been received. 16 | QueryResponse { 17 | /// The question that was queried. 18 | question: DnsQuestion, 19 | /// The matching records returned in the response. 20 | records: Vec, 21 | }, 22 | 23 | /// An announcement has been successfully sent. 24 | AnnouncementSent { 25 | /// The record that was announced. 26 | record: DnsRecord, 27 | }, 28 | } -------------------------------------------------------------------------------- /protocols/mdns/src/behaviour/records/mdns_records.rs: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Deserialize}; 2 | use std::time::{SystemTime, Duration}; 3 | use registry::Record; 4 | use std::fmt; 5 | 6 | 7 | #[derive(Debug, Clone, Serialize, Deserialize)] 8 | pub struct ServiceRecord { 9 | pub id: String, 10 | pub service_type: String, 11 | pub port: u16, 12 | pub ttl: Option, 13 | pub origin: String, 14 | pub priority: Option, 15 | pub weight: Option, 16 | pub node_id: String, // New field linking the service to the node 17 | } 18 | 19 | impl Record for ServiceRecord { 20 | fn identifier(&self) -> String { 21 | self.id.clone() 22 | } 23 | 24 | fn expires_at(&self) -> Option { 25 | self.ttl.map(|ttl_secs| SystemTime::now() + Duration::from_secs(ttl_secs.into())) 26 | } 27 | } 28 | impl fmt::Display for ServiceRecord { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | write!( 31 | f, 32 | "ServiceRecord {{ id: {}, service_type: {}, port: {}, ttl: {:?}, origin: {}, priority: {:?}, weight: {:?}, node_id: {} }}", 33 | self.id, 34 | self.service_type, 35 | self.port, 36 | self.ttl, 37 | self.origin, 38 | self.priority, 39 | self.weight, 40 | self.node_id 41 | ) 42 | } 43 | } 44 | 45 | #[derive(Debug, Clone, Serialize, Deserialize)] 46 | pub struct NodeRecord { 47 | pub id: String, 48 | pub ip_address: String, 49 | pub ttl: Option, 50 | pub services: Vec, // New field listing services offered by the node 51 | } 52 | 53 | impl Record for NodeRecord { 54 | fn identifier(&self) -> String { 55 | self.id.clone() 56 | } 57 | 58 | fn expires_at(&self) -> Option { 59 | self.ttl 60 | .map(|ttl_secs| SystemTime::now() + Duration::from_secs(ttl_secs.into())) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /protocols/mdns/src/behaviour/records/mod.rs: -------------------------------------------------------------------------------- 1 | // protocols\mdns\src\behaviour\records\mod.rs 2 | mod mdns_registry; 3 | mod mdns_records; 4 | 5 | pub use mdns_registry::MdnsRegistry; 6 | pub use mdns_records::{ServiceRecord,NodeRecord}; 7 | 8 | -------------------------------------------------------------------------------- /protocols/mdns/src/lib.rs: -------------------------------------------------------------------------------- 1 | // protocols\mdns\src\lib.rs 2 | 3 | // ============== MDNS Packet Strcuture Files ====== 4 | mod record; 5 | mod packet; 6 | mod name; 7 | 8 | pub use record::DnsRecord; 9 | pub use name::DnsName; 10 | pub use packet::{DnsPacket,DnsQuestion}; 11 | 12 | // ================================================= 13 | 14 | mod behaviour; 15 | pub use behaviour::*; 16 | 17 | // ================================================= -------------------------------------------------------------------------------- /protocols/mdns/src/name.rs: -------------------------------------------------------------------------------- 1 | // protocols\mdns\src\name.rs 2 | use std::io::Read; 3 | use bytes::Buf; 4 | use serde::Serialize; 5 | use std::fmt; 6 | 7 | /// Represents a DNS name, composed of multiple labels. 8 | /// 9 | /// A `DnsName` provides methods for creating, writing, and parsing DNS names, 10 | /// along with utilities for validation and formatting. 11 | #[derive(Clone, PartialEq, Debug,Serialize)] 12 | pub struct DnsName { 13 | pub labels: Vec, 14 | } 15 | 16 | impl DnsName { 17 | /// Creates a new `DnsName` from a string. 18 | /// 19 | /// # Arguments 20 | /// * `name` - The DNS name as a string. 21 | /// 22 | /// # Returns 23 | /// * `Ok(DnsName)` - If the name is valid. 24 | /// * `Err(String)` - If any label in the name exceeds 63 characters. 25 | pub fn new(name: &str) -> Result { 26 | let labels: Vec = name 27 | .split('.') 28 | .filter(|label| !label.is_empty()) 29 | .map(|label| label.to_string()) 30 | .collect(); 31 | 32 | // Validate label lengths 33 | for label in &labels { 34 | if label.len() > 63 { 35 | return Err(format!("Label '{}' exceeds 63 characters", label)); 36 | } 37 | } 38 | 39 | Ok(DnsName { labels }) 40 | } 41 | 42 | /// Writes the DNS name into a buffer in DNS wire format. 43 | /// 44 | /// # Arguments 45 | /// * `buffer` - A mutable byte vector to write the DNS name into. 46 | pub fn write(&self, buffer: &mut Vec) { 47 | for label in &self.labels { 48 | buffer.push(label.len() as u8); 49 | buffer.extend_from_slice(label.as_bytes()); 50 | } 51 | buffer.push(0x00); // End of the domain name 52 | } 53 | 54 | /// Parses a `DnsName` from a cursor containing DNS wire format data. 55 | /// 56 | /// # Arguments 57 | /// * `cursor` - A mutable cursor over the byte slice to parse. 58 | /// 59 | /// # Returns 60 | /// * `Ok(DnsName)` - If parsing succeeds. 61 | /// * `Err(Box)` - If parsing fails. 62 | pub fn parse(cursor: &mut std::io::Cursor<&[u8]>) -> Result> { 63 | let mut labels = Vec::new(); 64 | loop { 65 | let len = cursor.get_u8(); 66 | if len == 0 { 67 | break; 68 | } 69 | let mut label = vec![0; len as usize]; 70 | cursor.read_exact(&mut label)?; 71 | labels.push(String::from_utf8(label)?); 72 | } 73 | Ok(DnsName { labels }) 74 | } 75 | } 76 | 77 | impl fmt::Display for DnsName { 78 | /// Formats the DNS name as a human-readable string. 79 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 80 | write!(f, "{}", self.labels.join(".")) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /protocols/mdns/tests/mdns_service_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::sync::Arc; 4 | use mdns::{MdnsService,DnsRecord,DnsName,DnsPacket}; 5 | async fn setup_mdns_service() -> Arc { 6 | MdnsService::new(Some("TestNode.local".to_string()), "_testservice._tcp.local.") 7 | .await 8 | .expect("Failed to create MdnsService") 9 | } 10 | 11 | #[tokio::test] 12 | async fn test_register_default_node_service() { 13 | let service = setup_mdns_service().await; 14 | let result = service.register_default_node_service().await; 15 | assert!(result.is_ok()); 16 | 17 | let node_services = service.registry.list_services().await; 18 | assert!(!node_services.is_empty()); 19 | 20 | let default_service = node_services.iter().find(|s| s.service_type == "_testservice._tcp.local."); 21 | assert!(default_service.is_some()); 22 | } 23 | 24 | #[tokio::test] 25 | async fn test_register_local_service() { 26 | let service = setup_mdns_service().await; 27 | let result = service 28 | .register_local_service( 29 | "Service123.local".to_string(), 30 | "_custom._tcp.local.".to_string(), 31 | 8080, 32 | Some(300), 33 | "TestNode.local".to_string(), 34 | ) 35 | .await; 36 | assert!(result.is_ok()); 37 | 38 | let service_registry = service.registry.list_services().await; 39 | let added_service = service_registry.iter().find(|s| s.id == "Service123.local"); 40 | assert!(added_service.is_some()); 41 | assert_eq!(added_service.unwrap().port, 8080); 42 | } 43 | 44 | #[tokio::test] 45 | async fn test_advertise_services() { 46 | let service = setup_mdns_service().await; 47 | let result = service.advertise_services().await; 48 | assert!(result.is_ok()); 49 | } 50 | 51 | #[tokio::test] 52 | async fn test_create_advertise_packet() { 53 | let service = setup_mdns_service().await; 54 | let packet = service.create_advertise_packet().await.expect("Failed to create advertise packet"); 55 | 56 | assert!(!packet.answers.is_empty()); 57 | assert!(packet.answers.iter().any(|record| matches!(record, DnsRecord::PTR { .. }))); 58 | assert!(packet.answers.iter().any(|record| matches!(record, DnsRecord::SRV { .. }))); 59 | } 60 | 61 | #[tokio::test] 62 | async fn test_process_response() { 63 | let service = setup_mdns_service().await; 64 | let src = "192.168.1.100:5353".parse().unwrap(); 65 | 66 | let packet = DnsPacket { 67 | id: 0, 68 | flags: 0x8400, 69 | questions: Vec::new(), 70 | answers: vec![ 71 | DnsRecord::A { 72 | name: DnsName::new("TestNode.local").unwrap(), 73 | ttl: 300, 74 | ip: [192, 168, 1, 100], 75 | }, 76 | DnsRecord::SRV { 77 | name: DnsName::new("TestService.local").unwrap(), 78 | ttl: 300, 79 | priority: 10, 80 | weight: 10, 81 | port: 8080, 82 | target: DnsName::new("TestNode.local").unwrap(), 83 | }, 84 | ], 85 | authorities: Vec::new(), 86 | additionals : Vec::new() 87 | }; 88 | 89 | 90 | 91 | service.process_response(&packet, &src).await; 92 | let nodes = service.registry.list_nodes().await; 93 | assert!(!nodes.is_empty()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /protocols/negotiation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "negotiation" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | thiserror = {version = "2.0.11"} -------------------------------------------------------------------------------- /protocols/negotiation/README.md: -------------------------------------------------------------------------------- 1 | # Nautilus Negotiation Protocol 2 | 3 | The `nautilus_negotiation` module provides a **flexible negotiation framework** for resolving **compatibility between clients and servers**. It allows structured decision-making by applying **various negotiation strategies**, making it ideal for scenarios like: 4 | - **Cipher suite selection** 5 | - **Compression algorithm negotiation** 6 | - **Feature selection in distributed systems** 7 | 8 | Built in **Rust**, this module is fully **asynchronous** and **modular**, ensuring high performance and adaptability. 9 | 10 | --- 11 | 12 | ## 🌟 Features 13 | 14 | ### 🔄 **Flexible Negotiation Strategies** 15 | - **Client-Preferred** – Prioritizes client-preferred choices. 16 | - **Server-Preferred** – Server dictates the selection. 17 | - **Same Footing** – Ensures equal weighting in the negotiation process. 18 | - **First Match** – Picks the first compatible item. 19 | - **Weighted Strategy** – Balances both client & server weights. 20 | 21 | ### ⚡ **Event-Driven & Efficient** 22 | - **Context-aware** negotiations prevent mismatched selections. 23 | - **Priority-based selection** ensures optimal compatibility. 24 | - **Error-handling built-in** (e.g., `NoCompatibleItems`). 25 | 26 | ### 🏗 **Modular Design** 27 | - **Plug-and-play strategy implementations**. 28 | - **Custom negotiation logic** via trait-based extension. 29 | 30 | --- 31 | 32 | ## 🏛 **Architecture Overview** 33 | 34 | This module consists of the following core components: 35 | 36 | ### 1️⃣ **Traits** 37 | - **`Negotiable`** – Defines how items interact. 38 | - **`NegotiationStrategy`** – Enables different negotiation policies. 39 | 40 | ### 2️⃣ **Negotiation Context** 41 | - **`NegotiationContext`** – Defines what items are available for selection. 42 | - Ensures that **both client and server contexts** are compatible. 43 | 44 | ### 3️⃣ **Negotiation Engine** 45 | - **`negotiate_with_strategy`** – Handles negotiation logic. 46 | - Uses **priority-based selection** to choose the best match. 47 | 48 | ### 4️⃣ **Error Handling** 49 | - **`NegotiationError`** – Manages errors like: 50 | - **NoCompatibleItems** – No valid selection found. 51 | - **InvalidContext** – Context does not support negotiation. 52 | 53 | --- 54 | 55 | # ⚠ Limitations 56 | - Requires Implementations – Types must implement Negotiable and NegotiationContext. 57 | - No Multi-Phase Negotiation – Doesn't support fallback strategies. 58 | - Context Matching is Strict – Both parties must have some common items. 59 | #🎯 Future Improvements 60 | - ✅ Add Multi-Phase Negotiation – Enable fallback logic. 61 | - ✅ Support Dynamic Weights – Allow real-time priority adjustments. 62 | - ✅ Cross-Network Negotiation – Allow remote context negotiation. 63 | 64 | # 📜 License 65 | This project is licensed under the MIT License. See the LICENSE file for details. 66 | 67 | # 🤝 Contributing 68 | Contributions are welcome! Open an issue or submit a pull request with your improvements. 69 | 70 | -------------------------------------------------------------------------------- /protocols/negotiation/src/context.rs: -------------------------------------------------------------------------------- 1 | // protocols\negotiation\src\context.rs 2 | use crate::traits::Negotiable; 3 | pub trait NegotiationContext { 4 | /// Returns the list of items supported in this context. 5 | fn supported_items(&self) -> Vec; 6 | 7 | /// Returns the name of the context (e.g., "CipherSuite Negotiation"). 8 | fn context_name(&self) -> String; 9 | } 10 | -------------------------------------------------------------------------------- /protocols/negotiation/src/lib.rs: -------------------------------------------------------------------------------- 1 | // protocols\negotiation\src\lib.rs 2 | mod context; 3 | mod negotiation; 4 | mod negotiation_error; 5 | mod traits; 6 | pub mod negotiation_strategy; 7 | pub use traits::{Negotiable,NegotiationStrategy}; 8 | pub use context::NegotiationContext; 9 | pub use negotiation::negotiate_with_strategy; 10 | pub use negotiation_error::NegotiationError; -------------------------------------------------------------------------------- /protocols/negotiation/src/negotiation.rs: -------------------------------------------------------------------------------- 1 | // protocols\negotiation\src\negotiation.rs 2 | 3 | use crate::{traits::Negotiable,context::NegotiationContext,traits::NegotiationStrategy}; 4 | use crate::negotiation_error::NegotiationError; 5 | 6 | 7 | pub fn negotiate_with_strategy( 8 | strategy: &S, 9 | client_context: &C, 10 | server_context: &C, 11 | ) -> Result 12 | where 13 | T: Negotiable, 14 | C: NegotiationContext, 15 | S: NegotiationStrategy, 16 | { 17 | strategy.resolve(client_context, server_context) 18 | } -------------------------------------------------------------------------------- /protocols/negotiation/src/negotiation_error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | /// Errors that can occur during negotiation. 4 | #[derive(Debug, Error)] 5 | pub enum NegotiationError { 6 | /// No compatible items were found during negotiation. 7 | #[error("No compatible items found in context '{0}'")] 8 | NoCompatibleItems(String), 9 | 10 | /// The provided context is invalid or unsupported. 11 | #[error("Invalid or unsupported context: {0}")] 12 | InvalidContext(String), 13 | 14 | /// A custom error for user-defined negotiation logic. 15 | #[error("{0}")] 16 | Custom(String), 17 | } -------------------------------------------------------------------------------- /protocols/negotiation/src/traits.rs: -------------------------------------------------------------------------------- 1 | // protocols\negotiation\src\traits.rs 2 | use crate::context::NegotiationContext; 3 | use crate::negotiation_error::NegotiationError; 4 | pub trait Negotiable: Clone + Send + Sync { 5 | /// Returns the priority of the item (higher is better). 6 | fn priority(&self) -> u8; 7 | 8 | /// Returns true if the item is compatible with another. 9 | fn is_compatible(&self, other: &Self) -> bool; 10 | 11 | /// Returns the human-readable name of the item. 12 | fn name(&self) -> String; 13 | } 14 | 15 | 16 | 17 | pub trait NegotiationStrategy 18 | where 19 | T: Negotiable, 20 | C: NegotiationContext, 21 | { 22 | /// Resolves the best match between the client and server contexts. 23 | fn resolve( 24 | &self, 25 | client_context: &C, 26 | server_context: &C, 27 | ) -> Result; 28 | } -------------------------------------------------------------------------------- /protocols/ping/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ping" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /protocols/ping/src/lib.rs: -------------------------------------------------------------------------------- 1 | // protocols\ping\src\lib.rs 2 | mod peer; 3 | 4 | pub use peer::Peer; 5 | -------------------------------------------------------------------------------- /protocols/ping/src/peer.rs: -------------------------------------------------------------------------------- 1 | // protocols\ping\src\peer.rs 2 | use std::net::UdpSocket; 3 | use std::time::Duration; 4 | 5 | /// Represents a peer in the decentralized network 6 | pub struct Peer { 7 | pub addr: String, 8 | } 9 | 10 | impl Peer { 11 | /// Starts a peer to listen for `PING` messages and respond with `ALIVE` 12 | pub fn start_listener(&self) -> Result<(), String> { 13 | let socket = UdpSocket::bind(&self.addr).map_err(|e| format!("Failed to bind socket: {}", e))?; 14 | println!("Peer listening on {}", self.addr); 15 | 16 | let mut buffer = [0u8; 1024]; 17 | loop { 18 | // Receive messages 19 | match socket.recv_from(&mut buffer) { 20 | Ok((size, src)) => { 21 | let received = String::from_utf8_lossy(&buffer[..size]); 22 | if received == "PING" { 23 | println!("Received PING from {}", src); 24 | // Respond with ALIVE 25 | socket 26 | .send_to(b"ALIVE", src) 27 | .map_err(|e| format!("Failed to send response: {}", e)) 28 | .ok(); 29 | } 30 | } 31 | Err(err) => { 32 | println!("Error receiving data: {}", err); 33 | } 34 | } 35 | } 36 | } 37 | 38 | /// Sends a `PING` to the target address and waits for an `ALIVE` response 39 | pub fn send_ping(&self, target: &str, timeout_secs: u64) -> Result { 40 | let socket = UdpSocket::bind("0.0.0.0:0").map_err(|e| format!("Failed to bind socket: {}", e))?; 41 | socket 42 | .set_read_timeout(Some(Duration::from_secs(timeout_secs))) 43 | .map_err(|e| format!("Failed to set timeout: {}", e))?; 44 | 45 | // Send PING 46 | socket 47 | .send_to(b"PING", target) 48 | .map_err(|e| format!("Failed to send PING: {}", e))?; 49 | println!("Sent PING to {}", target); 50 | 51 | // Wait for ALIVE 52 | let mut buffer = [0u8; 1024]; 53 | match socket.recv_from(&mut buffer) { 54 | Ok((size, _src)) => { 55 | let response = String::from_utf8_lossy(&buffer[..size]); 56 | if response == "ALIVE" { 57 | println!("Received ALIVE response"); 58 | return Ok(true); 59 | } 60 | Ok(false) 61 | } 62 | Err(_) => Ok(false), // Timeout or other error 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /protocols/ping/tests/basic_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod basic_test { 3 | use ping::Peer; 4 | use std::thread; 5 | 6 | #[test] 7 | fn test_single_ping() { 8 | let peer_addr = "127.0.0.1:9001"; 9 | let target_addr = "127.0.0.1:9002"; 10 | 11 | let target_peer = Peer { 12 | addr: target_addr.to_string(), 13 | }; 14 | 15 | // Start target listener 16 | thread::spawn(move || { 17 | target_peer.start_listener().expect("Target listener failed"); 18 | }); 19 | 20 | // Give the target peer time to start 21 | thread::sleep(std::time::Duration::from_secs(1)); 22 | 23 | // Test sending a single ping 24 | let initiator_peer = Peer { 25 | addr: peer_addr.to_string(), 26 | }; 27 | let result = initiator_peer.send_ping(target_addr, 2); 28 | assert_eq!(result, Ok(true)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /protocols/ping/tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod integration_test { 3 | use ping::Peer; 4 | use std::thread; 5 | 6 | #[test] 7 | fn test_multiple_peers_ping_each_other() { 8 | let peer1_addr = "127.0.0.1:9201"; 9 | let peer2_addr = "127.0.0.1:9202"; 10 | 11 | let peer1 = Peer { 12 | addr: peer1_addr.to_string(), 13 | }; 14 | let peer2 = Peer { 15 | addr: peer2_addr.to_string(), 16 | }; 17 | 18 | // Start both peers in separate threads 19 | thread::spawn(move || { 20 | peer1.start_listener().expect("Peer 1 listener failed"); 21 | }); 22 | thread::spawn(move || { 23 | peer2.start_listener().expect("Peer 2 listener failed"); 24 | }); 25 | 26 | // Give peers time to start 27 | thread::sleep(std::time::Duration::from_secs(1)); 28 | 29 | // Peer 1 pings Peer 2 30 | let result1 = Peer { 31 | addr: peer1_addr.to_string(), 32 | } 33 | .send_ping(peer2_addr, 2); 34 | assert_eq!(result1, Ok(true)); 35 | 36 | // Peer 2 pings Peer 1 37 | let result2 = Peer { 38 | addr: peer2_addr.to_string(), 39 | } 40 | .send_ping(peer1_addr, 2); 41 | assert_eq!(result2, Ok(true)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /protocols/ping/tests/stress_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod stress_test { 3 | use ping::Peer; 4 | use std::thread; 5 | 6 | #[test] 7 | fn test_multiple_pings() { 8 | let peer_addr = "127.0.0.1:9101"; 9 | let target_addr = "127.0.0.1:9102"; 10 | 11 | let target_peer = Peer { 12 | addr: target_addr.to_string(), 13 | }; 14 | 15 | // Start target listener 16 | thread::spawn(move || { 17 | target_peer.start_listener().expect("Target listener failed"); 18 | }); 19 | 20 | // Give the target peer time to start 21 | thread::sleep(std::time::Duration::from_secs(1)); 22 | 23 | // Stress test: Send 100 pings 24 | let initiator_peer = Peer { 25 | addr: peer_addr.to_string(), 26 | }; 27 | 28 | for _ in 0..100 { 29 | let result = initiator_peer.send_ping(target_addr, 2); 30 | assert_eq!(result, Ok(true)); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /protocols/tls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | handshake = {path = "../handshake"} 8 | tokio = {version = "1.0.0",features = ["full"]} 9 | futures = {version = "0.3.31"} 10 | thiserror = {version = "2.0.11"} 11 | async-trait = {version = "0.1.85"} 12 | aes-gcm = "0.10" # Check for the latest version 13 | nautilus_core = {path = "../../core"} 14 | rand = {version = "0.8.5"} 15 | identity = {path = "../../identity",default-features = false,features = ["kyber"]} 16 | fips203 = {version ="0.4.2"} 17 | sha3 = "0.10" -------------------------------------------------------------------------------- /protocols/tls/examples/tls_session_example.rs: -------------------------------------------------------------------------------- 1 | use tokio::net::{TcpListener, TcpStream}; 2 | use tls::{HandshakeRole, TlsSession}; 3 | use tokio; 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<(), Box> { 7 | tokio::spawn(async { run_server().await.unwrap() }); 8 | tokio::spawn(async { run_client().await.unwrap() }); 9 | 10 | tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; 11 | 12 | Ok(()) 13 | } 14 | 15 | async fn run_server() -> Result<(), Box> { 16 | let listener = TcpListener::bind("127.0.0.1:8080").await?; 17 | println!("[Server] Listening on 127.0.0.1:8080"); 18 | 19 | let (socket, _) = listener.accept().await?; 20 | let mut session = TlsSession::new(socket, HandshakeRole::Initiator) 21 | .await 22 | .map_err(|e| Box::::from(e))?; 23 | 24 | let received = session.receive().await?; 25 | println!("[Server] Received: {}", String::from_utf8_lossy(&received)); 26 | 27 | session.send(b"Hello from server!").await?; 28 | println!("[Server] Response sent"); 29 | 30 | Ok(()) 31 | } 32 | 33 | async fn run_client() -> Result<(), Box> { 34 | tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; 35 | 36 | let socket = TcpStream::connect("127.0.0.1:8080").await?; 37 | let mut session = TlsSession::new(socket, HandshakeRole::Responder) 38 | .await 39 | .map_err(|e| Box::::from(e))?; 40 | 41 | session.send(b"Hello from client!").await?; 42 | println!("[Client] Message sent"); 43 | 44 | let response = session.receive().await?; 45 | println!("[Client] Response received: {}", String::from_utf8_lossy(&response)); 46 | 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /protocols/tls/src/lib.rs: -------------------------------------------------------------------------------- 1 | // protocols\tls\src\lib.rs 2 | mod tls_state; 3 | mod connection; 4 | mod record; 5 | mod handshake; 6 | mod tls_session; 7 | 8 | pub use connection::TlsConnection; 9 | pub use record::{TlsRecord, RecordType, RecordError}; 10 | pub use tls_state::TlsState; 11 | pub use handshake::{HelloStep,CipherSuiteStep,HandshakeRole,KyberExchangeStep,FinishStep}; 12 | pub use tls_session::{TlsSession,adaptive_session}; -------------------------------------------------------------------------------- /protocols/tls/src/record.rs: -------------------------------------------------------------------------------- 1 | // protocols\tls\src\record.rs 2 | use aes_gcm::{Aes256Gcm, Key, Nonce}; 3 | use aes_gcm::aead::{Aead, KeyInit}; 4 | use std::error::Error; 5 | use rand::Rng; 6 | 7 | #[derive(Debug)] 8 | pub enum RecordType { 9 | Handshake, 10 | ApplicationData, 11 | } 12 | #[derive(Debug)] 13 | pub struct TlsRecord { 14 | record_type: RecordType, 15 | payload: Vec, 16 | } 17 | 18 | impl TlsRecord { 19 | pub fn new(record_type: RecordType, payload: Vec) -> Self { 20 | Self { record_type, payload } 21 | } 22 | 23 | pub fn encrypt(&mut self, key: &[u8]) -> Result<(), RecordError> { 24 | let cipher = Aes256Gcm::new(Key::::from_slice(key)); 25 | let nonce_bytes: [u8; 12] = rand::thread_rng().gen(); // Generate random 12-byte nonce 26 | let nonce = Nonce::from_slice(&nonce_bytes); 27 | 28 | self.payload = cipher 29 | .encrypt(nonce, self.payload.as_ref()) 30 | .map_err(|_| RecordError::EncryptionError)?; 31 | self.payload.splice(0..0, nonce_bytes.iter().cloned()); // Prepend nonce to payload 32 | Ok(()) 33 | } 34 | pub fn decrypt(&mut self, key: &[u8]) -> Result, RecordError> { 35 | if self.payload.len() < 12 { 36 | return Err(RecordError::DecryptionError); // Not enough data for nonce 37 | } 38 | 39 | let (nonce_bytes, ciphertext) = self.payload.split_at(12); // Extract nonce 40 | let cipher = Aes256Gcm::new(Key::::from_slice(key)); 41 | let nonce = Nonce::from_slice(nonce_bytes); 42 | 43 | cipher 44 | .decrypt(nonce, ciphertext) 45 | .map_err(|_| RecordError::DecryptionError) 46 | } 47 | pub fn serialize(&self) -> Vec { 48 | let mut data = Vec::new(); 49 | data.push(match self.record_type { 50 | RecordType::Handshake => 0x01, 51 | RecordType::ApplicationData => 0x02, 52 | }); 53 | data.extend(&self.payload); 54 | data 55 | } 56 | pub fn deserialize(data: &[u8]) -> Result { 57 | if data.is_empty() { 58 | eprintln!("[ERROR] Received empty data."); 59 | return Err(RecordError::InvalidRecord); 60 | } 61 | 62 | let record_type = match data[0] { 63 | 0x01 => RecordType::Handshake, 64 | 0x02 => RecordType::ApplicationData, 65 | _ => { 66 | eprintln!("[ERROR] Invalid record type: {}", data[0]); 67 | return Err(RecordError::InvalidRecord); 68 | } 69 | }; 70 | 71 | let payload = data[1..].to_vec(); 72 | println!("[DEBUG] Received record type: {:?}, Payload length: {}", record_type, payload.len()); 73 | 74 | Ok(Self { record_type, payload }) 75 | } 76 | } 77 | 78 | #[derive(Debug)] 79 | pub enum RecordError { 80 | EncryptionError, 81 | DecryptionError, 82 | InvalidRecord, 83 | WriteError, 84 | ReadError, 85 | } 86 | 87 | impl std::fmt::Display for RecordError { 88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 89 | write!(f, "{:?}", self) 90 | } 91 | } 92 | 93 | impl Error for RecordError {} -------------------------------------------------------------------------------- /protocols/tls/src/tls_session.rs: -------------------------------------------------------------------------------- 1 | use tokio::net::{TcpStream, TcpListener}; 2 | use tokio::sync::Mutex; // <-- Use tokio's Mutex for TlsState 3 | use std::sync::Arc; 4 | 5 | use crate::{ 6 | TlsConnection, 7 | TlsState, 8 | HelloStep, 9 | HandshakeRole, 10 | KyberExchangeStep, 11 | FinishStep 12 | }; 13 | use handshake::Handshake; 14 | use nautilus_core::connection::Connection; 15 | use std::time::Duration; 16 | use tokio::time::timeout; 17 | 18 | #[derive(Clone)] 19 | pub struct TlsSession { 20 | pub connection: TlsConnection, 21 | } 22 | 23 | impl TlsSession { 24 | /// Create a new TlsSession in either Initiator or Responder role 25 | pub async fn new( 26 | socket: TcpStream, 27 | role: HandshakeRole, 28 | ) -> Result> { 29 | // Make sure we also use tokio::sync::Mutex for TlsState 30 | let state = Arc::new(Mutex::new(TlsState::default())); 31 | 32 | let mut handshake = Handshake::new("TLS_HANDSHAKE"); 33 | let hello_step = HelloStep::new("TLS_HANDSHAKE", role); 34 | let kyber_step = KyberExchangeStep::new(role, state.clone()); 35 | handshake.add_step(Box::new(hello_step)); 36 | handshake.add_step(Box::new(kyber_step)); 37 | handshake.add_step(Box::new(FinishStep { role })); 38 | 39 | // Build TlsConnection, which does the handshake 40 | let connection = TlsConnection::new(socket, handshake, state).await?; 41 | 42 | println!("[Session] Secure connection established for {:?}", role); 43 | Ok(Self { connection }) 44 | } 45 | 46 | pub async fn send(&mut self, message: &[u8]) -> Result<(), Box> { 47 | self.connection.send(message).await?; 48 | Ok(()) 49 | } 50 | 51 | pub async fn receive(&mut self) -> Result, Box> { 52 | self.connection.receive().await.map_err(Into::into) 53 | } 54 | pub async fn get_session_key(&self) -> Vec { 55 | self.connection.get_session_key().await 56 | } 57 | pub async fn split(&self) -> (crate::connection::TlsReader, crate::connection::TlsWriter) { 58 | self.connection.split().await 59 | } 60 | 61 | 62 | } 63 | 64 | /// Optional: An “adaptive” approach that tries to accept first (Responder), 65 | /// or else tries to connect (Initiator). 66 | pub async fn adaptive_session( 67 | address: &str, 68 | ) -> Result> { 69 | let listener = TcpListener::bind(address).await?; 70 | println!("[Adaptive Session] Listening on {}", address); 71 | 72 | // Wait up to 2 seconds for a client to connect 73 | let accept_future = listener.accept(); 74 | 75 | match timeout(Duration::from_secs(2), accept_future).await { 76 | Ok(Ok((socket, _))) => { 77 | println!("[Adaptive Session] => Acting as Responder"); 78 | TlsSession::new(socket, HandshakeRole::Responder).await 79 | } 80 | Ok(Err(e)) => { 81 | println!("[Adaptive Session] => accept error: {}", e); 82 | Err(Box::new(e)) 83 | } 84 | Err(_) => { 85 | println!("[Adaptive Session] => Acting as Initiator"); 86 | let socket = TcpStream::connect(address).await?; 87 | TlsSession::new(socket, HandshakeRole::Initiator).await 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /protocols/tls/src/tls_state.rs: -------------------------------------------------------------------------------- 1 | // protocols\tls\src\tls_state.rs 2 | #[derive(Default)] 3 | pub struct TlsState { 4 | handshake_complete: bool, 5 | session_key: Option>, 6 | negotiated_cipher_suite: Option>, 7 | supported_cipher_suites: Vec, 8 | } 9 | 10 | impl TlsState { 11 | pub fn set_handshake_complete(&mut self, complete: bool) { 12 | self.handshake_complete = complete; 13 | } 14 | 15 | pub fn handshake_complete(&self) -> bool { 16 | self.handshake_complete 17 | } 18 | 19 | pub fn set_session_key(&mut self, key: Vec) { 20 | self.session_key = Some(key); 21 | } 22 | 23 | pub fn session_key(&self) -> &[u8] { 24 | self.session_key.as_deref().unwrap_or_default() 25 | } 26 | 27 | pub fn set_negotiated_cipher_suite(&mut self, suite: Vec) { 28 | self.negotiated_cipher_suite = Some(suite); 29 | } 30 | 31 | pub fn negotiated_cipher_suite(&self) -> &[u8] { 32 | self.negotiated_cipher_suite.as_deref().unwrap_or_default() 33 | } 34 | 35 | pub fn set_supported_cipher_suites(&mut self, suites: Vec) { 36 | self.supported_cipher_suites = suites; 37 | } 38 | 39 | pub fn supported_cipher_suites(&self) -> &[u8] { 40 | &self.supported_cipher_suites 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /security/authentication/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "authentication" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hmac = "0.12" 8 | sha2 = "0.10" 9 | cmac = "0.7" 10 | aes = "0.8" 11 | 12 | 13 | [features] 14 | default = ["hmac", "cmac"] 15 | hmac = [] 16 | cmac = [] -------------------------------------------------------------------------------- /security/authentication/src/cmac_auth.rs: -------------------------------------------------------------------------------- 1 | // security\authentication\src\cmac_auth.rs 2 | use aes::Aes256; 3 | use cmac::{Cmac, Mac}; 4 | use crate::traits::MessageAuthentication; 5 | 6 | type CmacAes256 = Cmac; 7 | 8 | pub struct CmacAuthentication { 9 | key: Vec, 10 | } 11 | 12 | impl CmacAuthentication { 13 | pub fn new(key: &[u8]) -> Self { 14 | println!("Key length: {}", key.len()); // Debugging print statement 15 | assert_eq!(key.len(), 32, "Key must be 32 bytes long for AES-256"); 16 | Self { key: key.to_vec() } 17 | } 18 | } 19 | 20 | impl MessageAuthentication for CmacAuthentication { 21 | fn sign(&self, message: &[u8]) -> Vec { 22 | let mut mac = CmacAes256::new_from_slice(&self.key).expect("Valid key size"); 23 | mac.update(message); 24 | mac.finalize().into_bytes().to_vec() 25 | } 26 | 27 | fn verify(&self, message: &[u8], signature: &[u8]) -> bool { 28 | let mut mac = CmacAes256::new_from_slice(&self.key).expect("Valid key size"); 29 | mac.update(message); 30 | mac.verify_slice(signature).is_ok() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /security/authentication/src/hash_chain.rs: -------------------------------------------------------------------------------- 1 | // security\authentication\src\hash_chain.rs 2 | use sha2::{Digest, Sha256}; 3 | 4 | pub struct HashChain { 5 | pub chain: Vec>, 6 | } 7 | 8 | impl HashChain { 9 | pub fn new(seed: &[u8], iterations: usize) -> Self { 10 | let mut chain = vec![seed.to_vec()]; 11 | for _ in 1..iterations { 12 | let last = chain.last().unwrap(); 13 | let hash = Sha256::digest(last); 14 | chain.push(hash.to_vec()); 15 | } 16 | Self { chain } 17 | } 18 | 19 | pub fn validate(&self, idx: usize, hash: &[u8]) -> bool { 20 | self.chain.get(idx).map_or(false, |stored| stored == hash) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /security/authentication/src/hmac_auth.rs: -------------------------------------------------------------------------------- 1 | // security\authentication\src\hmac_auth.rs 2 | use hmac::{Hmac, Mac}; 3 | use sha2::Sha256; 4 | use crate::traits::MessageAuthentication; 5 | 6 | type HmacSha256 = Hmac; 7 | 8 | pub struct HmacAuthentication { 9 | key: Vec, 10 | } 11 | 12 | impl HmacAuthentication { 13 | pub fn new(key: &[u8]) -> Self { 14 | Self { key: key.to_vec() } 15 | } 16 | } 17 | 18 | impl MessageAuthentication for HmacAuthentication { 19 | fn sign(&self, message: &[u8]) -> Vec { 20 | let mut mac = HmacSha256::new_from_slice(&self.key).expect("HMAC can take a key of any size"); 21 | mac.update(message); 22 | mac.finalize().into_bytes().to_vec() 23 | } 24 | 25 | fn verify(&self, message: &[u8], signature: &[u8]) -> bool { 26 | let mut mac = HmacSha256::new_from_slice(&self.key).expect("HMAC can take a key of any size"); 27 | mac.update(message); 28 | mac.verify_slice(signature).is_ok() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /security/authentication/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod hmac_auth; 2 | pub mod cmac_auth; 3 | pub mod hash_chain; 4 | pub mod traits; 5 | 6 | // Re-export commonly used modules 7 | pub use hmac_auth::HmacAuthentication; 8 | pub use cmac_auth::CmacAuthentication; 9 | pub use hash_chain::HashChain; 10 | pub use traits::MessageAuthentication; 11 | -------------------------------------------------------------------------------- /security/authentication/src/traits.rs: -------------------------------------------------------------------------------- 1 | // security\authentication\src\traits.rs 2 | pub trait MessageAuthentication { 3 | fn sign(&self, message: &[u8]) -> Vec; 4 | fn verify(&self, message: &[u8], signature: &[u8]) -> bool; 5 | } 6 | -------------------------------------------------------------------------------- /security/authentication/tests/basic_auth_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use authentication::hmac_auth::HmacAuthentication; 4 | use authentication::cmac_auth::CmacAuthentication; 5 | use authentication::hash_chain::HashChain; 6 | use authentication::traits::MessageAuthentication; 7 | 8 | const TEST_KEY: &[u8] = b"thisisexactly32byteslong12345678"; 9 | const TEST_MESSAGE: &[u8] = b"The quick brown fox jumps over the lazy dog"; 10 | 11 | // Test cases for HMAC 12 | mod hmac_tests { 13 | use super::*; 14 | 15 | #[test] 16 | fn test_hmac_sign_and_verify() { 17 | let hmac_auth = HmacAuthentication::new(TEST_KEY); 18 | 19 | let signature = hmac_auth.sign(TEST_MESSAGE); 20 | assert!( 21 | hmac_auth.verify(TEST_MESSAGE, &signature), 22 | "HMAC verification failed" 23 | ); 24 | 25 | let invalid_signature = b"invalidsignature"; 26 | assert!( 27 | !hmac_auth.verify(TEST_MESSAGE, invalid_signature), 28 | "HMAC should not verify an invalid signature" 29 | ); 30 | } 31 | } 32 | 33 | // Test cases for CMAC 34 | mod cmac_tests { 35 | use super::*; 36 | 37 | #[test] 38 | fn test_cmac_sign_and_verify() { 39 | let cmac_auth = CmacAuthentication::new(TEST_KEY); 40 | 41 | let signature = cmac_auth.sign(TEST_MESSAGE); 42 | assert!( 43 | cmac_auth.verify(TEST_MESSAGE, &signature), 44 | "CMAC verification failed" 45 | ); 46 | 47 | let invalid_signature = b"invalidsignature"; 48 | assert!( 49 | !cmac_auth.verify(TEST_MESSAGE, invalid_signature), 50 | "CMAC should not verify an invalid signature" 51 | ); 52 | } 53 | } 54 | 55 | // Test cases for Hash Chain 56 | mod hash_chain_tests { 57 | use super::*; 58 | 59 | #[test] 60 | fn test_hash_chain_generation_and_validation() { 61 | let seed = b"initial"; 62 | let iterations = 5; 63 | let hash_chain = HashChain::new(seed, iterations); 64 | 65 | // Validate hashes at each index 66 | for i in 0..iterations { 67 | let expected_hash = &hash_chain.chain[i]; 68 | assert!( 69 | hash_chain.validate(i, expected_hash), 70 | "HashChain validation failed at index {}", 71 | i 72 | ); 73 | } 74 | 75 | // Validation should fail for incorrect hashes 76 | let invalid_hash = b"notavalidhash"; 77 | assert!( 78 | !hash_chain.validate(0, invalid_hash), 79 | "HashChain should not validate an invalid hash" 80 | ); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /security/data_encryption/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /security/data_encryption/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_encryption" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.0", features = ["full","rt-multi-thread", "macros"] } 8 | rand_core = { version = "0.6.4", features = ["getrandom"] } 9 | zeroize = "1.8.1" 10 | thiserror = {version = "2.0.0"} 11 | aes-gcm = {version = "0.10.3",optional = true} 12 | chacha20poly1305 = {version = "0.10",optional = true} 13 | des = { version = "0.8", optional = true } 14 | blowfish = { version = "0.9", optional = true } 15 | 16 | 17 | 18 | sha2 = {version = "0.10.8",optional = true} 19 | hmac = {version = "0.12.1", optional = true} 20 | 21 | pbkdf2 = {version= "0.12.2",optional = true} 22 | argon2 = { version = "0.5.3", features = ["default", "simple"],optional = true } 23 | scrypt = {version = "0.11.0",optional = true} 24 | 25 | [features] 26 | default = ["aes","pbkdf"] 27 | chacha20 = ["chacha20poly1305"] 28 | aes = ["aes-gcm"] 29 | 3des = ["des","hmac","sha2"] # Include SHA Later on 30 | blwfish = ["blowfish"] 31 | argon = ["argon2"] 32 | scrypt_derive = ["scrypt"] 33 | pbkdf = ["pbkdf2","sha2"] 34 | 35 | symmetric_encryption = ["aes", "3des", "blwfish", "chacha20"] 36 | key_derivation = ["argon", "scrypt_derive", "pbkdf"] 37 | [dev-dependencies] 38 | tempfile = "3.15.0" -------------------------------------------------------------------------------- /security/data_encryption/examples/tcp_stream_encryption.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "aes")] 2 | use std::io::{BufWriter, Write}; 3 | #[cfg(feature = "aes")] 4 | use std::net::TcpStream; 5 | #[cfg(feature = "aes")] 6 | use data_encryption::{Aes256GcmEncryption,StreamEncryption}; 7 | #[cfg(feature = "aes")] 8 | fn main() -> Result<(), Box> { 9 | let key = vec![0u8; 16]; // AES-128 key 10 | let nonce = vec![0u8; 12]; 11 | let aes = Aes256GcmEncryption::new(key.clone(), nonce.clone()) 12 | .expect("Failed to create AES-256 GCM instance"); 13 | 14 | 15 | // Connect to the target server (127.0.0.1 on port 8081) 16 | let stream = TcpStream::connect("127.0.0.1:8081")?; 17 | let mut writer = BufWriter::new(stream); 18 | 19 | // Create a stream of 1000 numbers 20 | let plaintext_stream = (1..=1000) 21 | .map(|number| format!("{}\n", number)) 22 | .map(|num_str| num_str.into_bytes()); 23 | 24 | // Encrypt and send each chunk through the stream 25 | for chunk in plaintext_stream { 26 | aes.encrypt_stream(chunk.as_slice(), &mut writer, &key, &nonce)?; 27 | } 28 | 29 | writer.flush()?; // Ensure all data is written 30 | Ok(()) 31 | } 32 | #[cfg(not(feature = "aes"))] 33 | fn main() { 34 | println!("Enable Aes Feature"); 35 | } 36 | -------------------------------------------------------------------------------- /security/data_encryption/src/encryption_error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Debug, Error)] 4 | pub enum EncryptionError { 5 | #[error("Encryption failed: {0}")] 6 | EncryptionFailed(String), 7 | 8 | #[error("Decryption failed: {0}")] 9 | DecryptionFailed(String), 10 | 11 | #[error("Key generation failed: {0}")] 12 | KeyGenerationFailed(String), 13 | 14 | #[error("Invalid key: {0}")] 15 | InvalidKey(String), 16 | 17 | #[error("Other error: {0}")] 18 | Other(String), 19 | } -------------------------------------------------------------------------------- /security/data_encryption/src/encryption_trait.rs: -------------------------------------------------------------------------------- 1 | // security\data_encryption\encryption_trait.rs 2 | pub trait SymmetricEncryption: Send { 3 | type Error: std::fmt::Debug + Send + Sync + 'static; 4 | 5 | fn encrypt(&self, data: &[u8]) -> Result, Self::Error>; 6 | fn decrypt(&self, data: &[u8]) -> Result, Self::Error>; 7 | } -------------------------------------------------------------------------------- /security/data_encryption/src/key_derivation_trait.rs: -------------------------------------------------------------------------------- 1 | // security\data_encryption\src\key_derivation_trait.rs 2 | 3 | pub trait KeyDerivation { 4 | type Error; 5 | 6 | /// Derive a key from the given password and salt 7 | fn derive_key( 8 | &self, 9 | password: &[u8], 10 | output_length: usize, 11 | ) -> Result, Self::Error>; 12 | } -------------------------------------------------------------------------------- /security/data_encryption/src/key_derive/argon2_key_derive.rs: -------------------------------------------------------------------------------- 1 | // ================================ Data Encryption Module ======================= 2 | // security\data_encryption\src\key_derive\argon2_key_derive.rs 3 | #[cfg(feature = "argon")] 4 | use argon2::{Argon2, Version, Params}; 5 | #[cfg(feature = "argon")] 6 | use crate::{KeyDerivation, utils::generate_secure_salt}; 7 | #[cfg(feature = "argon")] 8 | use zeroize::Zeroize; 9 | 10 | // ========================= Argon2KeyDerivation Struct ========================= 11 | #[cfg(feature = "argon")] 12 | pub struct Argon2KeyDerivation { 13 | pub memory_size_kb: u32, 14 | pub iterations: u32, 15 | pub parallelism: u32, 16 | } 17 | 18 | #[cfg(feature = "argon")] 19 | impl Drop for Argon2KeyDerivation { 20 | fn drop(&mut self) { 21 | self.memory_size_kb.zeroize(); 22 | self.iterations.zeroize(); 23 | self.parallelism.zeroize(); 24 | } 25 | } 26 | 27 | // ========================= KeyDerivation Trait Implementation ========================= 28 | #[cfg(feature = "argon")] 29 | impl KeyDerivation for Argon2KeyDerivation { 30 | type Error = String; 31 | 32 | fn derive_key( 33 | &self, 34 | password: &[u8], 35 | output_length: usize, 36 | ) -> Result, Self::Error> { 37 | if output_length > 1024 * 1024 { 38 | return Err("Output length exceeds maximum allowed size (1MB).".to_string()); 39 | } 40 | 41 | let argon2 = Argon2::new( 42 | argon2::Algorithm::Argon2id, 43 | Version::V0x13, 44 | Params::new( 45 | self.memory_size_kb, 46 | self.iterations, 47 | self.parallelism, 48 | Some(output_length), 49 | ) 50 | .map_err(|e| format!("Argon2 parameter setup failed: {}", e))?, 51 | ); 52 | 53 | // Generate a secure random salt 54 | let mut salt = generate_secure_salt(16); 55 | 56 | // Initialize the output buffer for the derived key 57 | let mut derived_key = vec![0u8; output_length]; 58 | 59 | // Perform the key derivation 60 | let result = argon2.hash_password_into(password, &salt, &mut derived_key); 61 | salt.zeroize(); // Zeroize salt after use 62 | 63 | if let Err(err) = result { 64 | derived_key.zeroize(); // Zero out the derived key on failure 65 | return Err(format!("Argon2 key derivation failed: {}", err)); 66 | } 67 | 68 | Ok(derived_key) 69 | } 70 | } 71 | 72 | // ========================= Argon2KeyDerivation Implementation ========================= 73 | #[cfg(feature = "argon")] 74 | impl Argon2KeyDerivation { 75 | /// Creates a new `Argon2KeyDerivation` instance with validated parameters. 76 | pub fn new(memory_size_kb: u32, iterations: u32, parallelism: u32) -> Result { 77 | if memory_size_kb > 1024 * 1024 { 78 | return Err("Memory size exceeds the limit of 1 GB".to_string()); 79 | } 80 | if iterations == 0 { 81 | return Err("Iterations must be greater than zero".to_string()); 82 | } 83 | if parallelism == 0 { 84 | return Err("Parallelism must be greater than zero".to_string()); 85 | } 86 | Ok(Self { 87 | memory_size_kb, 88 | iterations, 89 | parallelism, 90 | }) 91 | } 92 | } 93 | 94 | // ============================================================================ 95 | -------------------------------------------------------------------------------- /security/data_encryption/src/key_derive/mod.rs: -------------------------------------------------------------------------------- 1 | // security\data_encryption\src\key_derive\mod.rs 2 | #[cfg(feature="pbkdf")] 3 | mod pbkdf2_key_derive; 4 | #[cfg(feature="pbkdf")] 5 | pub use pbkdf2_key_derive::PBKDF2; 6 | #[cfg(feature = "argon")] 7 | mod argon2_key_derive; 8 | #[cfg(feature = "argon")] 9 | pub use argon2_key_derive::Argon2KeyDerivation; 10 | #[cfg(feature = "scrypt_derive")] 11 | mod scrypt_key_derive; 12 | #[cfg(feature = "scrypt_derive")] 13 | pub use scrypt_key_derive::Scrypt; -------------------------------------------------------------------------------- /security/data_encryption/src/key_derive/pbkdf2_key_derive.rs: -------------------------------------------------------------------------------- 1 | // ================================ Data Encryption Module ======================= 2 | // security\data_encryption\src\key_derive\pbkdf2_key_derive.rs 3 | #[cfg(feature = "pbkdf")] 4 | use pbkdf2::pbkdf2_hmac; 5 | #[cfg(feature = "pbkdf")] 6 | use sha2::Sha256; 7 | #[cfg(feature = "pbkdf")] 8 | use crate::{KeyDerivation, utils::generate_secure_salt}; 9 | #[cfg(feature = "pbkdf")] 10 | use zeroize::Zeroize; 11 | 12 | // ========================= PBKDF2 Struct ========================= 13 | #[cfg(feature = "pbkdf")] 14 | pub struct PBKDF2 { 15 | pub iterations: u32, 16 | } 17 | 18 | #[cfg(feature = "pbkdf")] 19 | impl Drop for PBKDF2 { 20 | fn drop(&mut self) { 21 | self.iterations.zeroize(); // Ensure iterations are zeroized on drop 22 | } 23 | } 24 | 25 | // ========================= KeyDerivation Trait Implementation ========================= 26 | #[cfg(feature = "pbkdf")] 27 | impl KeyDerivation for PBKDF2 { 28 | type Error = String; 29 | 30 | fn derive_key( 31 | &self, 32 | password: &[u8], 33 | output_length: usize, 34 | ) -> Result, Self::Error> { 35 | if self.iterations == 0 { 36 | return Err("Iterations must be greater than zero".to_string()); 37 | } 38 | 39 | if output_length > 1024 * 1024 { 40 | return Err("Output length exceeds maximum allowed size (1MB).".to_string()); 41 | } 42 | 43 | // Generate a secure random salt 44 | let mut salt = generate_secure_salt(16); 45 | 46 | // Initialize the output buffer for the derived key 47 | let mut key = vec![0u8; output_length]; 48 | 49 | // Perform the key derivation 50 | pbkdf2_hmac::(password, &salt, self.iterations, &mut key); 51 | 52 | // Zeroize the salt after use 53 | salt.zeroize(); 54 | 55 | Ok(key) 56 | } 57 | } 58 | 59 | // ========================= PBKDF2 Implementation ========================= 60 | #[cfg(feature = "pbkdf")] 61 | impl PBKDF2 { 62 | /// Creates a new `PBKDF2` instance with validation. 63 | pub fn new(iterations: u32) -> Result { 64 | if iterations == 0 { 65 | return Err("Iterations must be greater than zero.".to_string()); 66 | } 67 | Ok(Self { iterations }) 68 | } 69 | } 70 | 71 | // ============================================================================ 72 | -------------------------------------------------------------------------------- /security/data_encryption/src/key_derive/scrypt_key_derive.rs: -------------------------------------------------------------------------------- 1 | // ================================ Data Encryption Module ======================= 2 | // security\data_encryption\src\key_derive\scrypt_key_derive.rs 3 | #[cfg(feature = "scrypt_derive")] 4 | use scrypt::{scrypt, Params}; 5 | #[cfg(feature = "scrypt_derive")] 6 | use crate::{KeyDerivation, utils::generate_secure_salt}; 7 | 8 | // ========================= Scrypt Struct ========================= 9 | #[cfg(feature = "scrypt_derive")] 10 | pub struct Scrypt { 11 | pub params: Params, 12 | } 13 | 14 | // ========================= KeyDerivation Trait Implementation ========================= 15 | #[cfg(feature = "scrypt_derive")] 16 | impl KeyDerivation for Scrypt { 17 | type Error = String; 18 | 19 | fn derive_key( 20 | &self, 21 | password: &[u8], 22 | output_length: usize, 23 | ) -> Result, Self::Error> { 24 | if output_length > 1024 * 1024 { 25 | return Err("Output length exceeds the maximum allowed size (1MB).".to_string()); 26 | } 27 | 28 | let salt = &generate_secure_salt(16); 29 | let mut key = vec![0u8; output_length]; 30 | scrypt(password, salt, &self.params, &mut key).map_err(|e| e.to_string())?; 31 | Ok(key) 32 | } 33 | } 34 | 35 | // ============================================================================ 36 | -------------------------------------------------------------------------------- /security/data_encryption/src/lib.rs: -------------------------------------------------------------------------------- 1 | // security\data_encryption\src\lib.rs 2 | // ================================================ Encryption Traits Interface ============================================== 3 | mod encryption_trait; 4 | pub use encryption_trait::SymmetricEncryption; 5 | 6 | mod encryption_error; 7 | pub use encryption_error::EncryptionError; 8 | 9 | mod key_derivation_trait; 10 | pub use key_derivation_trait::KeyDerivation; 11 | 12 | mod stream_encryption_trait; 13 | pub use stream_encryption_trait::StreamEncryption; 14 | // ================================================= Encryption Public API Interface ========================================= 15 | mod encryption; 16 | pub use encryption::*; 17 | 18 | 19 | // ================================================= Key Derivations API Interface ============================================ 20 | mod key_derive; 21 | pub use key_derive::*; 22 | 23 | 24 | // ================================================ Misc. && Utilities API Interface =========================================== 25 | pub mod utils; // utility Services are both private/public 26 | 27 | // =============================================================== FIN ========================================================= -------------------------------------------------------------------------------- /security/data_encryption/src/stream_encryption_trait.rs: -------------------------------------------------------------------------------- 1 | // security\data_encryption\src\stream_encryption_trait.rs 2 | 3 | pub trait StreamEncryption { 4 | type Error; 5 | 6 | /// Encrypts a stream and outputs the encrypted stream. 7 | fn encrypt_stream( 8 | &self, 9 | input: R, 10 | output: W, 11 | key: &[u8], // Encryption key provided at runtime 12 | nonce: &[u8], // Optional nonce or IV for flexibility 13 | ) -> Result<(), Self::Error>; 14 | 15 | /// Decrypts an encrypted stream and outputs the decrypted stream. 16 | fn decrypt_stream( 17 | &self, 18 | input: R, 19 | output: W, 20 | key: &[u8], // Decryption key provided at runtime 21 | nonce: &[u8], // Optional nonce or IV for flexibility 22 | ) -> Result<(), Self::Error>; 23 | } -------------------------------------------------------------------------------- /security/data_encryption/src/utils.rs: -------------------------------------------------------------------------------- 1 | // security\data_encryption\src\utils.rs 2 | use rand_core::{OsRng, RngCore}; 3 | 4 | /// Generate a secure random salt of the specified length. 5 | pub fn generate_secure_salt(length: usize) -> Vec { 6 | let mut salt = vec![0u8; length]; 7 | OsRng.fill_bytes(&mut salt); // Explicitly use `fill_bytes` for better readability. 8 | salt 9 | } 10 | 11 | /// Generates a secure random key of the given size (in bytes). 12 | pub fn generate_random_key(size: usize) -> Vec { 13 | let mut key = vec![0u8; size]; 14 | OsRng.fill_bytes(&mut key); // Consistency with `generate_secure_salt`. 15 | key 16 | } 17 | 18 | /// Generates a secure random nonce of the given size (in bytes). 19 | pub fn generate_random_nonce(size: usize) -> Vec { 20 | let mut nonce = vec![0u8; size]; 21 | OsRng.fill_bytes(&mut nonce); // Consistency with other functions. 22 | nonce 23 | } -------------------------------------------------------------------------------- /security/data_encryption/tests/argon2_derive_test.rs: -------------------------------------------------------------------------------- 1 | 2 | #[cfg(feature = "argon")] 3 | #[cfg(test)] 4 | mod tests { 5 | use data_encryption::{KeyDerivation,Argon2KeyDerivation}; 6 | 7 | #[test] 8 | fn test_derive_key_basic() { 9 | let derivation = Argon2KeyDerivation::new(65536, 3, 1).expect("Failed to create Argon2 instance"); 10 | let password = b"password"; 11 | let key = derivation 12 | .derive_key(password, 32) 13 | .expect("Key derivation failed"); 14 | assert_eq!(key.len(), 32); 15 | } 16 | 17 | #[test] 18 | fn test_argon2_different_salts() { 19 | let derivation = Argon2KeyDerivation::new(4096, 3, 1).expect("Failed to create Argon2 instance"); 20 | let password = b"password"; 21 | let key1 = derivation.derive_key(password, 32).expect("Key derivation failed"); 22 | let key2 = derivation.derive_key(password, 32).expect("Key derivation failed"); 23 | 24 | assert_ne!(key1, key2); // Keys derived with different salts should be different 25 | } 26 | 27 | #[test] 28 | fn test_derive_key_with_large_output_length() { 29 | let derivation = Argon2KeyDerivation::new(65536, 3, 1).expect("Failed to create Argon2 instance"); 30 | let password = b"password"; 31 | let key = derivation 32 | .derive_key(password, 1024) // Large output length 33 | .expect("Key derivation failed"); 34 | assert_eq!(key.len(), 1024); 35 | } 36 | 37 | #[test] 38 | fn test_argon2_empty_password_and_salt() { 39 | let derivation = Argon2KeyDerivation::new(4096, 3, 1).expect("Failed to create Argon2 instance"); 40 | let password = b""; 41 | let key = derivation.derive_key(password, 32).expect("Key derivation failed"); 42 | 43 | assert_eq!(key.len(), 32); // Ensure output length matches requested size 44 | } 45 | 46 | #[test] 47 | fn test_argon2_large_memory() { 48 | let derivation = Argon2KeyDerivation::new(64 * 1024, 3, 1).expect("Failed to create Argon2 instance"); // 64 MB 49 | let password = b"password"; 50 | let key = derivation.derive_key(password, 32).expect("Key derivation failed"); 51 | 52 | assert_eq!(key.len(), 32); 53 | } 54 | } -------------------------------------------------------------------------------- /security/data_encryption/tests/pbkdf_derive_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature="pbkdf")] 2 | #[cfg(test)] 3 | mod tests { 4 | use data_encryption::{KeyDerivation,PBKDF2}; 5 | #[test] 6 | fn test_derive_key_basic() { 7 | let pbkdf2 = PBKDF2 { iterations: 1000 }; 8 | let password = b"secure_password"; 9 | let derived_key = pbkdf2.derive_key(password, 32).expect("Failed to derive key"); 10 | 11 | assert_eq!(derived_key.len(), 32); 12 | assert_ne!(derived_key, vec![0u8; 32]); // Ensure the derived key is not all zeros 13 | } 14 | 15 | #[test] 16 | fn test_derive_key_with_empty_password() { 17 | let pbkdf2 = PBKDF2 { iterations: 1000 }; 18 | let password = b""; 19 | let derived_key = pbkdf2.derive_key(password, 32).expect("Failed to derive key"); 20 | 21 | assert_eq!(derived_key.len(), 32); 22 | assert_ne!(derived_key, vec![0u8; 32]); 23 | } 24 | 25 | #[test] 26 | fn test_derive_key_with_zero_iterations() { 27 | let pbkdf2 = PBKDF2 { iterations: 0 }; 28 | let password = b"secure_password"; 29 | let result = pbkdf2.derive_key(password, 32); 30 | 31 | assert!(result.is_err(), "Derivation should fail with zero iterations"); 32 | } 33 | 34 | #[test] 35 | fn test_derive_key_with_large_output_length() { 36 | let pbkdf2 = PBKDF2 { iterations: 1000 }; 37 | let password = b"secure_password"; 38 | let derived_key = pbkdf2 39 | .derive_key(password, 1024) 40 | .expect("Failed to derive key"); 41 | 42 | assert_eq!(derived_key.len(), 1024); 43 | assert_ne!(derived_key, vec![0u8; 1024]); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /security/data_encryption/tests/scrypt_derive_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[cfg(feature = "scrypt_derive")] 3 | mod tests { 4 | use scrypt::Params; 5 | use data_encryption::{KeyDerivation,Scrypt}; 6 | #[test] 7 | fn test_derive_key_basic() { 8 | let params = Params::new(15, 8, 1, 32).expect("Failed to create params"); 9 | let scrypt = Scrypt { params }; 10 | let password = b"password"; 11 | let key = scrypt 12 | .derive_key(password, 32) 13 | .expect("Key derivation failed"); 14 | assert_eq!(key.len(), 32); 15 | } 16 | 17 | #[test] 18 | fn test_different_salts() { 19 | let params = Params::new(15, 8, 1, 32).expect("Failed to create params"); 20 | let scrypt = Scrypt { params }; 21 | let password = b"password"; 22 | 23 | let key1 = scrypt 24 | .derive_key(password, 32) 25 | .expect("Key derivation failed"); 26 | let key2 = scrypt 27 | .derive_key(password, 32) 28 | .expect("Key derivation failed"); 29 | 30 | assert_ne!(key1, key2); // Different salts produce different outputs 31 | } 32 | 33 | #[test] 34 | fn test_large_output_length() { 35 | let params = Params::new(15, 8, 1, 32).expect("Failed to create params"); 36 | let scrypt = Scrypt { params }; 37 | let password = b"password"; 38 | // Attempt a valid large output length (1MB) 39 | let result = scrypt.derive_key(password, 1024 * 1024); // 1MB 40 | assert!(result.is_ok(), "Derivation with max allowed output length should succeed"); 41 | 42 | // Attempt an invalid large output length (>1MB) 43 | let result = scrypt.derive_key(password, 10 * 1024 * 1024); // 10MB 44 | assert!( 45 | result.is_err(), 46 | "Derivation with excessively large output length should fail" 47 | ); 48 | } 49 | 50 | #[test] 51 | fn test_empty_password_and_salt() { 52 | let params = Params::new(15, 8, 1, 32).expect("Failed to create params"); 53 | let scrypt = Scrypt { params }; 54 | let password = b""; 55 | 56 | let key = scrypt 57 | .derive_key(password, 32) 58 | .expect("Key derivation failed"); 59 | 60 | assert_eq!(key.len(), 32); // Ensure output length matches requested size 61 | } 62 | 63 | #[test] 64 | fn test_invalid_params() { 65 | // Invalid parameters (e.g., block size too large) 66 | let result = Params::new(15, u32::MAX, 1, 32); 67 | assert!(result.is_err(), "Invalid parameters should result in an error"); 68 | } 69 | } -------------------------------------------------------------------------------- /security/data_encryption/tests/symmetric_suite_test.rs: -------------------------------------------------------------------------------- 1 | 2 | #[cfg(test)] 3 | mod tests { 4 | #[allow(unused_imports)] 5 | use data_encryption::SymmetricCipherSuite; 6 | 7 | #[test] 8 | fn test_name() { 9 | #[cfg(feature = "aes")] 10 | assert_eq!(SymmetricCipherSuite::AES256GCM { priority: 1 }.name(), "AES256-GCM"); 11 | 12 | #[cfg(feature = "blowfish")] 13 | assert_eq!(SymmetricCipherSuite::Blowfish { priority: 1 }.name(), "Blowfish"); 14 | 15 | #[cfg(feature = "chacha20")] 16 | assert_eq!(SymmetricCipherSuite::ChaCha20 { priority: 1 }.name(), "ChaCha20"); 17 | 18 | #[cfg(feature = "3des")] 19 | assert_eq!(SymmetricCipherSuite::TripleDES { priority: 1 }.name(), "TripleDES"); 20 | } 21 | 22 | #[test] 23 | fn test_key_size() { 24 | #[cfg(feature = "aes")] 25 | assert_eq!(SymmetricCipherSuite::AES256GCM { priority: 1 }.key_size(), 32); 26 | 27 | #[cfg(feature = "blowfish")] 28 | assert_eq!(SymmetricCipherSuite::Blowfish { priority: 1 }.key_size(), 16); 29 | 30 | #[cfg(feature = "chacha20")] 31 | assert_eq!(SymmetricCipherSuite::ChaCha20 { priority: 1 }.key_size(), 32); 32 | 33 | #[cfg(feature = "3des")] 34 | assert_eq!(SymmetricCipherSuite::TripleDES { priority: 1 }.key_size(), 24); 35 | } 36 | 37 | #[test] 38 | fn test_nonce_size() { 39 | #[cfg(feature = "aes")] 40 | assert_eq!(SymmetricCipherSuite::AES256GCM { priority: 1 }.nonce_size(), 12); 41 | 42 | #[cfg(feature = "blowfish")] 43 | assert_eq!(SymmetricCipherSuite::Blowfish { priority: 1 }.nonce_size(), 0); 44 | 45 | #[cfg(feature = "chacha20")] 46 | assert_eq!(SymmetricCipherSuite::ChaCha20 { priority: 1 }.nonce_size(), 12); 47 | 48 | #[cfg(feature = "3des")] 49 | assert_eq!(SymmetricCipherSuite::TripleDES { priority: 1 }.nonce_size(), 8); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /transport/tcp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /transport/tcp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tcp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | nautilus_core = {path = "../../core"} 8 | tokio = {version = "1.0.0",features = ["full"]} 9 | async-trait = {version = "0.1.85"} 10 | socket2 = "0.5" 11 | tls = {path = "../../protocols/tls"} 12 | handshake = {path = "../../protocols/handshake"} 13 | [features] 14 | framing = [] 15 | tls_layer = [] 16 | 17 | [dev-dependencies] 18 | futures = {version = "0.3.28"} -------------------------------------------------------------------------------- /transport/tcp/examples/tcp_conn_example.rs: -------------------------------------------------------------------------------- 1 | use nautilus_core::event_bus::EventBus; 2 | use std::sync::Arc; 3 | use tcp::{TcpTransport,TcpEvent}; 4 | use nautilus_core::connection::{Transport,Connection,TransportListener,ConnectionEvent}; 5 | 6 | #[tokio::main] 7 | async fn main() { 8 | let conn_event_bus = Arc::new(EventBus::::new(100)); 9 | let tcp_event_bus = Arc::new(EventBus::::new(100)); 10 | 11 | // Initialize TcpTransport with both event buses 12 | let tcp_transport = TcpTransport::new( 13 | Arc::clone(&conn_event_bus), 14 | Arc::clone(&tcp_event_bus), 15 | ); 16 | // Start listening for connections 17 | let mut listener = tcp_transport.listen("127.0.0.1:8080").await.unwrap(); 18 | println!("Listening on 127.0.0.1:8080"); 19 | 20 | // Spawn a task to handle incoming connections 21 | tokio::spawn(async move { 22 | while let Ok(mut conn) = listener.accept().await { 23 | tokio::spawn(async move { 24 | println!("New connection!"); 25 | let message = conn.receive().await.unwrap(); 26 | println!("Received: {:?}", message); 27 | }); 28 | } 29 | }); 30 | 31 | // Dial a connection 32 | let mut client = tcp_transport.dial("127.0.0.1:8080").await.unwrap(); 33 | println!("Connected to server!"); 34 | 35 | // Send data 36 | client.send(b"Hello, server!").await.unwrap(); 37 | } 38 | -------------------------------------------------------------------------------- /transport/tcp/src/lib.rs: -------------------------------------------------------------------------------- 1 | // transport\tcp\src\lib.rs 2 | mod tcp_conn; 3 | mod tcp_events; 4 | 5 | // ============================================================================================================================== 6 | 7 | pub use tcp_conn::{TcpConnection,TcpTransport,TcpTransportListener}; 8 | pub use tcp_events::TcpEvent; 9 | pub use nautilus_core::connection::Connection; -------------------------------------------------------------------------------- /transport/tcp/src/tcp_events.rs: -------------------------------------------------------------------------------- 1 | // transport\tcp\src\tcp_events.rs 2 | 3 | // transport/tcp/src/tcp_event.rs 4 | #[derive(Debug, Clone)] 5 | pub enum TcpEvent { 6 | DataReceived { peer: String, data: Vec }, 7 | DataSent { peer: String, data: Vec }, 8 | } 9 | -------------------------------------------------------------------------------- /transport/udp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "udp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | nautilus_core = {path = "../../core"} 8 | tokio = {version = "1.0.0",features = ["full"]} 9 | async-trait = {version = "0.1.85"} -------------------------------------------------------------------------------- /transport/udp/examples/udp_example.rs: -------------------------------------------------------------------------------- 1 | use tokio::task; 2 | use tokio::time::Duration; 3 | use std::sync::Arc; 4 | use nautilus_core::connection::{Datagram, ConnectionError}; 5 | use udp::UdpConnection; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<(), ConnectionError> { 9 | // Create the receiver and bind to a local address 10 | let receiver = Arc::new(UdpConnection::new()); 11 | receiver.bind("127.0.0.1:8080").await?; 12 | 13 | // Spawn a task to listen for incoming messages 14 | let receiver_task = { 15 | let receiver = Arc::clone(&receiver); 16 | task::spawn(async move { 17 | if let Ok((data, addr)) = receiver.receive_from().await { 18 | println!("Received '{}' from {}", String::from_utf8_lossy(&data), addr); 19 | } 20 | }) 21 | }; 22 | 23 | // Create the sender and bind to a local address 24 | let sender = UdpConnection::new(); 25 | sender.bind("127.0.0.1:8081").await?; // Bind to a different port for the sender 26 | 27 | // Send a message to the receiver 28 | let message = b"Hello, UdpConnection!"; 29 | sender.send_to(message, "127.0.0.1:8080").await?; 30 | println!("Message sent!"); 31 | 32 | // Give the receiver some time to process the message 33 | tokio::time::sleep(Duration::from_millis(500)).await; 34 | 35 | // Wait for the receiver task to finish 36 | receiver_task.await.unwrap(); 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /transport/udp/src/lib.rs: -------------------------------------------------------------------------------- 1 | // transport\udp\src\lib.rs 2 | mod udp_conn; 3 | 4 | pub use udp_conn::UdpConnection; -------------------------------------------------------------------------------- /transport/udp/src/udp_conn.rs: -------------------------------------------------------------------------------- 1 | // transport\udp\src\udp_conn.rs 2 | use tokio::net::UdpSocket; 3 | use async_trait::async_trait; 4 | use tokio::sync::Mutex; 5 | use std::sync::Arc; 6 | use nautilus_core::connection::{ConnectionError, Datagram}; 7 | 8 | #[derive(Debug,Clone)] 9 | pub struct UdpConnection { 10 | socket: Arc>>, 11 | } 12 | 13 | impl UdpConnection { 14 | pub fn new() -> Self { 15 | UdpConnection { 16 | socket: Arc::new(Mutex::new(None)), 17 | } 18 | } 19 | } 20 | 21 | #[async_trait] 22 | impl Datagram for UdpConnection { 23 | type Error = ConnectionError; 24 | 25 | async fn bind(&self, addr: &str) -> Result<(), Self::Error> { 26 | let socket = UdpSocket::bind(addr).await.map_err(ConnectionError::from)?; 27 | let mut lock = self.socket.lock().await; // Async lock 28 | *lock = Some(socket); 29 | Ok(()) 30 | } 31 | 32 | async fn send_to(&self, data: &[u8], addr: &str) -> Result<(), Self::Error> { 33 | let lock = self.socket.lock().await; 34 | if let Some(ref socket) = *lock { 35 | socket.send_to(data, addr).await.map_err(ConnectionError::from)?; 36 | Ok(()) 37 | } else { 38 | Err(ConnectionError::Generic("Socket not initialized".into())) 39 | } 40 | } 41 | 42 | async fn receive_from(&self) -> Result<(Vec, String), Self::Error> { 43 | let lock = self.socket.lock().await; 44 | if let Some(ref socket) = *lock { 45 | let mut buf = vec![0; 1024]; 46 | let (size, addr) = socket.recv_from(&mut buf).await.map_err(ConnectionError::from)?; 47 | buf.truncate(size); 48 | Ok((buf, addr.to_string())) 49 | } else { 50 | Err(ConnectionError::Generic("Socket not initialized".into())) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /utilities/certificate_parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "certificate_parser" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | x509-parser = "0.16.0" 8 | ring = "0.17.7" 9 | base64 ={ version = "0.22.0"} -------------------------------------------------------------------------------- /utilities/certificate_parser/src/cert_util_trait.rs: -------------------------------------------------------------------------------- 1 | use crate::certificate_type::PublicKeyType; 2 | use crate::certificate_parsing_erorr::CertificateError; 3 | 4 | /// A trait for handling various certificate utilities. 5 | #[allow(dead_code)] 6 | pub trait CertUtilsTrait { 7 | /// Parses a certificate and returns its raw content. 8 | fn parse(cert_data: &[u8]) -> Result, CertificateError>; 9 | 10 | /// Validates a certificate (e.g., signature, expiration). 11 | fn validate(cert_data: &[u8]) -> Result; 12 | 13 | /// Extracts the public key from a certificate. 14 | /// Dynamically determines the key type based on algorithm identifiers. 15 | fn extract_public_key(cert_data: &[u8]) -> Result; 16 | 17 | /// Exports the certificate to a specific format. 18 | fn export(cert_data: &[u8]) -> Result, CertificateError>; 19 | } -------------------------------------------------------------------------------- /utilities/certificate_parser/src/certificate_parsing.rs: -------------------------------------------------------------------------------- 1 | // utilities/certificate_parser/src/certificate_parsing.rs 2 | use crate::certificate_type::{CertificateType, PublicKeyType}; 3 | use crate::certificate_parsing_erorr::CertificateError; 4 | use crate::utils::der_utils::DerUtils; 5 | use crate::utils::pem_utils::PemUtils; 6 | use crate::cert_util_trait::CertUtilsTrait; 7 | /// Parses a certificate and extracts its public key. 8 | /// 9 | /// Supports PEM, DER, PKCS12, and JKS formats (partial support for the latter two). 10 | pub fn convert_certificate_to_public_key( 11 | cert_data: &[u8], 12 | cert_type: CertificateType, 13 | ) -> Result { 14 | let raw_cert = match cert_type { 15 | CertificateType::PEM => PemUtils::parse(cert_data)?, 16 | CertificateType::DER => DerUtils::parse(cert_data)?, 17 | }; 18 | 19 | let (_, parsed_cert) = x509_parser::parse_x509_certificate(&raw_cert) 20 | .map_err(|_| CertificateError::ParseError("Failed to parse certificate".to_string()))?; 21 | 22 | // Extract OID and public key 23 | let public_key_info = parsed_cert.tbs_certificate.subject_pki; 24 | let algorithm_oid = public_key_info.algorithm.algorithm.to_string(); 25 | let public_key_data = public_key_info.subject_public_key.data.to_vec(); 26 | 27 | PublicKeyType::from_oid_and_key(&algorithm_oid, public_key_data) 28 | .map_err(|e| CertificateError::UnsupportedAlgorithm(e)) 29 | } -------------------------------------------------------------------------------- /utilities/certificate_parser/src/certificate_parsing_erorr.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum CertificateError { 3 | // Parsing-related errors 4 | ParseError(String), 5 | UnsupportedFormat(String), 6 | UnsupportedAlgorithm(String), 7 | 8 | // Validation and generation errors 9 | ValidationError(String), 10 | GenerationError(String), 11 | 12 | // I/O and library-related errors 13 | IoError(String), 14 | RcgenError(String), 15 | } -------------------------------------------------------------------------------- /utilities/certificate_parser/src/certificate_type.rs: -------------------------------------------------------------------------------- 1 | pub enum CertificateType { 2 | PEM, 3 | DER, 4 | } 5 | 6 | pub enum PublicKeyType { 7 | RSA(Vec, String), // OID: 1.2.840.113549.1.1.1 8 | ECDSA(Vec, String), // OID: 1.2.840.10045.2.1 9 | SECP256K1(Vec, String), // Example OID 10 | Dilithium(Vec, String), // PQC 11 | SPHINCSPlus(Vec, String), // PQC 12 | Falcon(Vec, String), // PQC 13 | Kyber(Vec, String), // PQC 14 | } 15 | 16 | impl PublicKeyType { 17 | /// Creates a new `PublicKeyType` from the OID and raw key data. 18 | pub fn from_oid_and_key(oid: &str, key_data: Vec) -> Result { 19 | match oid { 20 | "1.2.840.113549.1.1.1" => Ok(PublicKeyType::RSA(key_data, oid.to_string())), 21 | "1.2.840.10045.2.1" => Ok(PublicKeyType::ECDSA(key_data, oid.to_string())), 22 | "1.3.132.0.10" => Ok(PublicKeyType::SECP256K1(key_data, oid.to_string())), // Example OID 23 | "2.16.840.1.101.3.4.3.13" => Ok(PublicKeyType::Dilithium(key_data, oid.to_string())), // Placeholder OID 24 | "1.3.9999.5.5.1.5" => Ok(PublicKeyType::SPHINCSPlus(key_data, oid.to_string())), // Placeholder OID 25 | "1.3.6.1.4.1.11591.4.11" => Ok(PublicKeyType::Falcon(key_data, oid.to_string())), // Placeholder OID 26 | "1.3.6.1.4.1.2.267.11.4.4" => Ok(PublicKeyType::Kyber(key_data, oid.to_string())), // Placeholder OID 27 | _ => Err(format!("Unsupported OID: {}", oid)), 28 | } 29 | } 30 | 31 | /// Returns the OID for the public key type. 32 | pub fn oid(&self) -> &str { 33 | match self { 34 | PublicKeyType::RSA(_, oid) => oid, 35 | PublicKeyType::ECDSA(_, oid) => oid, 36 | PublicKeyType::SECP256K1(_, oid) => oid, 37 | PublicKeyType::Dilithium(_, oid) => oid, 38 | PublicKeyType::SPHINCSPlus(_, oid) => oid, 39 | PublicKeyType::Falcon(_, oid) => oid, 40 | PublicKeyType::Kyber(_, oid) => oid, 41 | } 42 | } 43 | 44 | /// Returns the raw key data. 45 | pub fn key_data(&self) -> &Vec { 46 | match self { 47 | PublicKeyType::RSA(key, _) => key, 48 | PublicKeyType::ECDSA(key, _) => key, 49 | PublicKeyType::SECP256K1(key, _) => key, 50 | PublicKeyType::Dilithium(key, _) => key, 51 | PublicKeyType::SPHINCSPlus(key, _) => key, 52 | PublicKeyType::Falcon(key, _) => key, 53 | PublicKeyType::Kyber(key, _) => key, 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /utilities/certificate_parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | mod certificate_type; 3 | mod certificate_parsing_erorr; 4 | mod certificate_parsing; 5 | mod certificate_builder; 6 | mod cert_util_trait; 7 | pub use certificate_parsing_erorr::CertificateError; 8 | pub use certificate_type::{CertificateType,PublicKeyType}; 9 | 10 | 11 | pub use certificate_parsing::convert_certificate_to_public_key; 12 | pub use certificate_builder::CertificateBuilder; 13 | pub use utils::{der_utils,pem_utils}; -------------------------------------------------------------------------------- /utilities/certificate_parser/src/utils/der_utils.rs: -------------------------------------------------------------------------------- 1 | use x509_parser::parse_x509_certificate; 2 | use crate::{PublicKeyType, CertificateError}; 3 | use crate::cert_util_trait::CertUtilsTrait; 4 | 5 | pub struct DerUtils; 6 | 7 | impl CertUtilsTrait for DerUtils { 8 | fn parse(cert_data: &[u8]) -> Result, CertificateError> { 9 | Ok(cert_data.to_vec()) 10 | } 11 | 12 | fn validate(cert_data: &[u8]) -> Result { 13 | // Basic validation logic 14 | Ok(!cert_data.is_empty()) 15 | } 16 | 17 | fn extract_public_key(cert_data: &[u8]) -> Result { 18 | let raw_cert = Self::parse(cert_data)?; 19 | let (_, cert) = parse_x509_certificate(&raw_cert) 20 | .map_err(|_| CertificateError::ParseError("Failed to parse certificate".to_string()))?; 21 | 22 | // Extract OID and public key 23 | let public_key_info = cert.tbs_certificate.subject_pki; 24 | let algorithm_oid = public_key_info.algorithm.algorithm.to_string(); 25 | let public_key_data = public_key_info.subject_public_key.data.to_vec(); 26 | 27 | PublicKeyType::from_oid_and_key(&algorithm_oid, public_key_data) 28 | .map_err(|e| CertificateError::UnsupportedAlgorithm(e)) 29 | } 30 | 31 | fn export(cert_data: &[u8]) -> Result, CertificateError> { 32 | Ok(cert_data.to_vec()) 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | use std::fs; 40 | 41 | #[test] 42 | fn test_extract_public_key_der_rsa() { 43 | let der_data = fs::read("test_assets/rsa_cert.der").unwrap(); 44 | let result = DerUtils::extract_public_key(&der_data); 45 | assert!(result.is_ok(), "Failed to extract RSA public key from DER"); 46 | 47 | if let Ok(PublicKeyType::RSA(_, oid)) = result { 48 | assert_eq!(oid, "1.2.840.113549.1.1.1", "Incorrect OID for RSA"); 49 | } else { 50 | panic!("Unexpected public key type"); 51 | } 52 | } 53 | 54 | #[test] 55 | fn test_extract_public_key_der_ecdsa() { 56 | let der_data = fs::read("test_assets/ecdsa_cert.der").unwrap(); 57 | let result = DerUtils::extract_public_key(&der_data); 58 | assert!(result.is_ok(), "Failed to extract ECDSA public key from DER"); 59 | 60 | if let Ok(PublicKeyType::ECDSA(_, oid)) = result { 61 | assert_eq!(oid, "1.2.840.10045.2.1", "Incorrect OID for ECDSA"); 62 | } else { 63 | panic!("Unexpected public key type"); 64 | } 65 | } 66 | 67 | #[test] 68 | fn test_invalid_der() { 69 | let invalid_der = vec![0x00, 0x01, 0x02]; 70 | let result = DerUtils::extract_public_key(&invalid_der); 71 | assert!(result.is_err(), "Invalid DER should fail"); 72 | } 73 | } -------------------------------------------------------------------------------- /utilities/certificate_parser/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | // utilities\certificate_parser\src\utils\mod.rs 2 | pub mod pem_utils; 3 | pub mod der_utils; -------------------------------------------------------------------------------- /utilities/certificate_parser/src/utils/pem_utils.rs: -------------------------------------------------------------------------------- 1 | use x509_parser::{parse_x509_certificate, pem::parse_x509_pem}; 2 | use crate::{certificate_type::PublicKeyType, certificate_parsing_erorr::CertificateError}; 3 | use crate::cert_util_trait::CertUtilsTrait; 4 | 5 | pub struct PemUtils; 6 | 7 | impl CertUtilsTrait for PemUtils { 8 | fn parse(cert_data: &[u8]) -> Result, CertificateError> { 9 | let (_, pem) = parse_x509_pem(cert_data) 10 | .map_err(|_| CertificateError::ParseError("Invalid PEM format".to_string()))?; 11 | Ok(pem.contents.to_vec()) 12 | } 13 | 14 | fn validate(cert_data: &[u8]) -> Result { 15 | // Basic validation logic (e.g., check if the certificate data is non-empty) 16 | Ok(!cert_data.is_empty()) 17 | } 18 | 19 | fn extract_public_key(cert_data: &[u8]) -> Result { 20 | let raw_cert = Self::parse(cert_data)?; 21 | let (_, cert) = parse_x509_certificate(&raw_cert) 22 | .map_err(|_| CertificateError::ParseError("Failed to parse certificate".to_string()))?; 23 | 24 | // Extract OID and public key 25 | let public_key_info = cert.tbs_certificate.subject_pki; 26 | let algorithm_oid = public_key_info.algorithm.algorithm.to_string(); 27 | let public_key_data = public_key_info.subject_public_key.data.to_vec(); 28 | 29 | PublicKeyType::from_oid_and_key(&algorithm_oid, public_key_data) 30 | .map_err(|e| CertificateError::UnsupportedAlgorithm(e)) 31 | } 32 | 33 | fn export(cert_data: &[u8]) -> Result, CertificateError> { 34 | Ok(cert_data.to_vec()) 35 | } 36 | } 37 | 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | use super::*; 42 | use std::fs; 43 | 44 | #[test] 45 | fn test_extract_public_key_pem_rsa() { 46 | let pem_data = fs::read("test_assets/rsa_cert.pem").unwrap(); 47 | let result = PemUtils::extract_public_key(&pem_data); 48 | assert!(result.is_ok(), "Failed to extract RSA public key from PEM"); 49 | 50 | if let Ok(PublicKeyType::RSA(_, oid)) = result { 51 | assert_eq!(oid, "1.2.840.113549.1.1.1", "Incorrect OID for RSA"); 52 | } else { 53 | panic!("Unexpected public key type"); 54 | } 55 | } 56 | 57 | #[test] 58 | fn test_extract_public_key_pem_ecdsa() { 59 | let pem_data = fs::read("test_assets/ecdsa_cert.pem").unwrap(); 60 | let result = PemUtils::extract_public_key(&pem_data); 61 | assert!(result.is_ok(), "Failed to extract ECDSA public key from PEM"); 62 | 63 | if let Ok(PublicKeyType::ECDSA(_, oid)) = result { 64 | assert_eq!(oid, "1.2.840.10045.2.1", "Incorrect OID for ECDSA"); 65 | } else { 66 | panic!("Unexpected public key type"); 67 | } 68 | } 69 | 70 | #[test] 71 | fn test_invalid_pem() { 72 | let invalid_pem = b"-----BEGIN CERTIFICATE-----\nInvalid Data\n-----END CERTIFICATE-----"; 73 | let result = PemUtils::extract_public_key(invalid_pem); 74 | assert!(result.is_err(), "Invalid PEM should fail"); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/ecdsa_cert.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Aronnax/Nautilus/a49656e2040feea60ee63acdf390d5475b4e27c7/utilities/certificate_parser/test_assets/ecdsa_cert.der -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/ecdsa_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBUTCB+aADAgECAhRXJZtdlDalK1XgnS5mbxNpNpS7bDAKBggqhkjOPQQDAjAY 3 | MRYwFAYDVQQDDA1FQ0RTQVRlc3RDZXJ0MB4XDTI1MDExMDEwMTE0NVoXDTI2MDEx 4 | MDEwMTE0NVowGDEWMBQGA1UEAwwNRUNEU0FUZXN0Q2VydDBZMBMGByqGSM49AgEG 5 | CCqGSM49AwEHA0IABGUA/bTc0ZpZpKzPYHio/iWd3XS/xfbPyDJrzYd7nn1/atcQ 6 | 8hDzaa/JflZnAWbRJCV3z8UEMDToQCx9QDMpb+mjITAfMB0GA1UdDgQWBBT/NfdP 7 | yq+axagf8YNfaeiJ332hMDAKBggqhkjOPQQDAgNHADBEAiAdP7Ln3DoCQn6cwdRu 8 | s3Y3DorRVUiLQHLlLNU/b89T1AIgMy1U1ebI1qjnG9ENO0fYoVOc7dJOelTn74HH 9 | KaWM2Pg= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/ecdsa_csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIHSMHoCAQAwGDEWMBQGA1UEAwwNRUNEU0FUZXN0Q2VydDBZMBMGByqGSM49AgEG 3 | CCqGSM49AwEHA0IABGUA/bTc0ZpZpKzPYHio/iWd3XS/xfbPyDJrzYd7nn1/atcQ 4 | 8hDzaa/JflZnAWbRJCV3z8UEMDToQCx9QDMpb+mgADAKBggqhkjOPQQDAgNIADBF 5 | AiAzMD7P2TfY2NSYyductRL6Wdl5/q2ihgrGjsv1jqi73wIhAOiAxgULM9wuqDCD 6 | IJUdMxVeYFJ12HxjP0Bn2xlmxwvI 7 | -----END CERTIFICATE REQUEST----- 8 | -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/ecdsa_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIIu3dRpU5RbZbcwZCGJDBAtTRj+j55JBuyi7JsANKzYaoAoGCCqGSM49 3 | AwEHoUQDQgAEZQD9tNzRmlmkrM9geKj+JZ3ddL/F9s/IMmvNh3uefX9q1xDyEPNp 4 | r8l+VmcBZtEkJXfPxQQwNOhALH1AMylv6Q== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/rsa_cert.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Aronnax/Nautilus/a49656e2040feea60ee63acdf390d5475b4e27c7/utilities/certificate_parser/test_assets/rsa_cert.der -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/rsa_cert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Aronnax/Nautilus/a49656e2040feea60ee63acdf390d5475b4e27c7/utilities/certificate_parser/test_assets/rsa_cert.p12 -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/rsa_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC4zCCAcugAwIBAgIURf/BWHO3KENrFQg3zK1tLzv0mPYwDQYJKoZIhvcNAQEL 3 | BQAwGjEYMBYGA1UEAwwPVGVzdENlcnRpZmljYXRlMB4XDTI1MDExMDEwMzEzMFoX 4 | DTI2MDExMDEwMzEzMFowGjEYMBYGA1UEAwwPVGVzdENlcnRpZmljYXRlMIIBIjAN 5 | BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl9EZ4Qitq816SIG+9zWGr4zWf5/o 6 | ro8d8mKS0Hjp45PPEvGUNh6h6RAvJco8x+WQhi1ndsXVJLlxsdUc5T3CP+rlL1jZ 7 | ibgE/G0wjS1vhvCVku9Lz0wdTxlZuOTR3m/iTiRCAvDs61Q4AERaPogoad+u5uRI 8 | pVq7D7H2nu/Fre9TdWZ2BsTAp7/hC2BLTNkHsFa26Zc59Mmsk/YifDicvyk9IHhF 9 | fIibGMrY3CWc/3rIk3CXY36/I/CzDQATnR+JrV3a/KYS2yUPBBDpA9E78eeeWoxC 10 | wlgs9/FJS93l3v/WJfPXILt+w9yYnElYt/lWRMOIgh5bUvI9+Oc/TYSetQIDAQAB 11 | oyEwHzAdBgNVHQ4EFgQUUqhIw7E2Zmj4DAB4ZnSOqiN26XgwDQYJKoZIhvcNAQEL 12 | BQADggEBADeqaEjGbPrx4KyFdU+qH+49Ef8LS41LQVCIa193ROy6Sy9YlT1rvJiC 13 | zxrrSEbLRdX/J5c3ms/jxrcBHKFQYuoWxIjPcQFnTmcXGimVGv+7BDMJo9cQuog2 14 | Fjl35AhpsCAqaVjkyQHZc9WadKHt3pdpjEFUSwpR1HofQTffG6FFD7JFmFYkRsZp 15 | Fha01Vo3TNYVsSzO0N3yK/KjbyXpzKBCqvK9suyuEdlIztskL2W5TZKe1Uv0jsVL 16 | g5gScd6jDeiH4+AWXsay9+I6ikGI4HrNGJsre+QUZx5OaRCRDotE9ys9SJPa37TJ 17 | tUttstbs1uZZDF/sXjkxlfc1fxFvALY= 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/rsa_csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICXzCCAUcCAQAwGjEYMBYGA1UEAwwPVGVzdENlcnRpZmljYXRlMIIBIjANBgkq 3 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl9EZ4Qitq816SIG+9zWGr4zWf5/oro8d 4 | 8mKS0Hjp45PPEvGUNh6h6RAvJco8x+WQhi1ndsXVJLlxsdUc5T3CP+rlL1jZibgE 5 | /G0wjS1vhvCVku9Lz0wdTxlZuOTR3m/iTiRCAvDs61Q4AERaPogoad+u5uRIpVq7 6 | D7H2nu/Fre9TdWZ2BsTAp7/hC2BLTNkHsFa26Zc59Mmsk/YifDicvyk9IHhFfIib 7 | GMrY3CWc/3rIk3CXY36/I/CzDQATnR+JrV3a/KYS2yUPBBDpA9E78eeeWoxCwlgs 8 | 9/FJS93l3v/WJfPXILt+w9yYnElYt/lWRMOIgh5bUvI9+Oc/TYSetQIDAQABoAAw 9 | DQYJKoZIhvcNAQELBQADggEBADcWM1pw800SKWkSvW96yzpUgSmj0VnMKR5Fdq/q 10 | ubY07OQ5T26cKqvT3V61IoimCCwyQwcCcpvU+CriAOERC+rKEwaMtLnqkakPHbe0 11 | pVS/nXanMyq0M8h7yrw5M0Aovk12Sf9v1BRt5XnqzWeW5Yax4F/iYAfHWeTfHBTo 12 | croE25MNacQMRBqdy8/Sm1rvMbDtjxmj6jR59s36nM0AsWQUHqLROe9E+Yk6Vlrc 13 | CYjRdhkF30oJV3C3pcN99TNnrkTWLkBHrvieexMG2M5kt5/3FhVGuGhmDlbh1/0y 14 | XUjNoH9x8fhtooX6pXA0WeH+ZwgAat5LoT809MIiEKJP+So= 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /utilities/certificate_parser/test_assets/rsa_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCX0RnhCK2rzXpI 3 | gb73NYavjNZ/n+iujx3yYpLQeOnjk88S8ZQ2HqHpEC8lyjzH5ZCGLWd2xdUkuXGx 4 | 1RzlPcI/6uUvWNmJuAT8bTCNLW+G8JWS70vPTB1PGVm45NHeb+JOJEIC8OzrVDgA 5 | RFo+iChp367m5EilWrsPsfae78Wt71N1ZnYGxMCnv+ELYEtM2QewVrbplzn0yayT 6 | 9iJ8OJy/KT0geEV8iJsYytjcJZz/esiTcJdjfr8j8LMNABOdH4mtXdr8phLbJQ8E 7 | EOkD0Tvx555ajELCWCz38UlL3eXe/9Yl89cgu37D3JicSVi3+VZEw4iCHltS8j34 8 | 5z9NhJ61AgMBAAECggEABrbiSKAcIj2Pu3Y1jnRpFQmLERDu9w1wWyB98sduPLj0 9 | TxetIOmg6pBjLW4w2vxttGl8qnWcdCfZfe55fTAKj84hPcNVt7ASVGG15ICObq7k 10 | Rp/bv/rTTwTFgZZpK8fR//e9jai/KbdLVMOxJBPcibYxY5fUGLAXFt4dlLDxEryx 11 | /IcKc6phzFJloHUOpw1fny3UFE4VNXgp6bRPsUmlWbWI6a/RrcHdpx0iOtcSn8EP 12 | MuTc8MFAJ34qcdC5CZ43TkWhtdN7MH9GH8l5Fvah2KyCZn5fSxhXac3dImHnA8Rr 13 | t034AHq22FaB//mcWY+jqcj8F7YT73GHWCaq0LccrwKBgQDUyTwJ9bYl6iYQxnO8 14 | Bhz1YdSZ5Own60r4A4tsrgAiJb1hsGtl1fuzZJxq4lpHR0nNY077dE3jWnyqscxP 15 | llJHc+8JGlM009VDVd6s7l/LYBf4qH5j0gZkL5EUDzFjih1aKAzeQHKyCmxP3Hha 16 | DVI5dcMtdXQGDtdBFQVR7TGBGwKBgQC2phGxpVHGtO2XUJD15XI4JdcwN2UHvjZK 17 | xD3lZvFeLC19zBribQyU8saqY9d7crXSKrGtGjsRVRsCkqdCVybpawftRHnnb8yg 18 | nkouRF3OEzcFZENS/wNaogUQyFnktPCpUzRb458a+P+7kZ1MSTwt8xyhHYUMZhzF 19 | 5OWvE2csbwKBgFW7YoJMyn/KMF4P5y95cw92xDbhIjkPNxxoKTO3g3w+omgKsarN 20 | JWVpAxCZdHBFi7nB6l+rw3n3ykTn7/6c/TemwUd0oNOnCcTggyA/xr7lT6MzGAFV 21 | 7JdPBvADEw4c5qidfFnLs6qCQpOGq8L0nQ1joijdqsqKDjY9pvAUVh7DAoGAciMR 22 | B4whsCS8HESycZcyazfqYy0aq14IG2zKqSJNTuYYf3YejsRKioGz+CJwM6qZSH55 23 | jYzIwKoIOPFgKCrCHDdQOssBysiofO6tuH/shqynQ21GTQUjGW4J8FTjJ/KSOZ3P 24 | KGTzti1H0fTK8TMKskQr7dq+m+LrtBa4GVHstW0CgYEA0M7AOb8PDFpNJDXc/Kkm 25 | ias5Xd6sDaDJp4K+bNw6xbuhc3cBnJbupTEua9c3BAtIxjYgqD3A8p6jAS/LakI2 26 | 05hitfl3RthIjJ6CBt7LMfBiXhywKvkgbJGXVJc3XrbGxVALF81Re9s100MRrYhS 27 | B2KIpAPf0/NXZoK12KHaBs8= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /utilities/registry/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /utilities/registry/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "registry" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde = {version = "1.0.0",features = ["derive"]} 8 | async-trait = {version = "0.1.85"} 9 | tokio = {version = "1.0.0",features = ["full"]} 10 | serde_json = "1.0" 11 | futures ={version = "0.3.31"} 12 | redis = { version = "0.27.6", optional = true, features = ["tokio-comp"] } 13 | deadpool-redis = { version = "0.18.0", optional = true } 14 | [dev-dependencies] 15 | 16 | 17 | [features] 18 | default = [] 19 | redis_registry = ["redis", "deadpool-redis"] -------------------------------------------------------------------------------- /utilities/registry/src/data_source_traits.rs: -------------------------------------------------------------------------------- 1 | // utilities\registry\src\data_source_traits.rs 2 | use std::pin::Pin; 3 | use futures::stream::Stream; 4 | use async_trait::async_trait; 5 | use crate::{Record, RegistryError}; 6 | 7 | #[async_trait] 8 | pub trait DataSource: Send + Sync { 9 | /// Generates or provides records for the pipeline. 10 | async fn provide(&self) -> Result, RegistryError>; 11 | } 12 | 13 | 14 | #[async_trait] 15 | pub trait Destination: Send + Sync { 16 | /// Accepts a record from the pipeline. 17 | async fn accept(&self, record: R) -> Result<(), RegistryError>; 18 | } 19 | 20 | 21 | #[async_trait] 22 | pub trait Pipeline: Send + Sync { 23 | /// Processes data from a source and sends it to a destination. 24 | async fn process(&self, source: Box>, destination: Box>) -> Result<(), RegistryError>; 25 | } 26 | 27 | 28 | #[async_trait] 29 | pub trait DataSourceStream: Send + Sync { 30 | async fn stream(&self) -> Pin> + Send>>; 31 | } -------------------------------------------------------------------------------- /utilities/registry/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod record_trait; 2 | mod registry_record_error; 3 | mod registry_traits; 4 | 5 | pub use record_trait::{Record,RecordType}; 6 | pub use registry_record_error::RegistryError; 7 | pub use registry_traits::Registry; 8 | 9 | // ====================================================================================================================================== 10 | 11 | mod registry_mods; 12 | pub use registry_mods::InMemoryRegistry; 13 | 14 | #[cfg(feature = "redis_registry")] 15 | pub use registry_mods::RedisRegistry; 16 | // ============================================ In development ========================================================================== 17 | mod data_source_traits; 18 | 19 | pub use data_source_traits::{DataSource,Destination,Pipeline,DataSourceStream}; 20 | 21 | mod hashring_shard; 22 | 23 | pub use hashring_shard::{HashRing,ShardManager}; -------------------------------------------------------------------------------- /utilities/registry/src/record_trait.rs: -------------------------------------------------------------------------------- 1 | // utilities\registry\src\record_trait.rs 2 | // src/traits/record.rs 3 | use serde::{Serialize, Deserialize}; 4 | use std::time::SystemTime; 5 | 6 | /// A generic trait representing a record in the registry. 7 | /// 8 | /// This trait provides methods to uniquely identify records, manage their expiration, 9 | /// and determine if they have expired. 10 | pub trait Record: Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone { 11 | /// Returns the unique identifier for the record. 12 | /// 13 | /// # Example 14 | /// * For `NodeRecord`, this might be an IP address. 15 | /// * For `ServiceRecord`, this might be an instance name. 16 | fn identifier(&self) -> String; 17 | 18 | /// Returns the expiration time of the record, if it has one. 19 | /// 20 | /// # Returns 21 | /// * `Some(SystemTime)` - The expiration time of the record. 22 | /// * `None` - If the record does not expire. 23 | fn expires_at(&self) -> Option; 24 | 25 | /// Determines if the record is expired based on the current system time. 26 | /// 27 | /// # Returns 28 | /// * `true` - If the record is expired. 29 | /// * `false` - If the record is still valid or has no expiration. 30 | fn is_expired(&self) -> bool { 31 | match self.expires_at() { 32 | Some(time) => SystemTime::now() > time, 33 | None => false, 34 | } 35 | } 36 | } 37 | 38 | /// Represents the type of a record, which can be static or dynamic. 39 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 40 | pub enum RecordType { 41 | /// Static records do not change frequently. 42 | Static, 43 | 44 | /// Dynamic records are updated regularly or periodically. 45 | Dynamic, 46 | } 47 | 48 | impl Default for RecordType { 49 | /// Sets the default `RecordType` to `Dynamic`. 50 | fn default() -> Self { 51 | RecordType::Dynamic 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /utilities/registry/src/registry_mods/mod.rs: -------------------------------------------------------------------------------- 1 | // utilities\registry\src\registry_mods\mod.rs 2 | 3 | mod in_memory_registry; 4 | 5 | pub use in_memory_registry::InMemoryRegistry; 6 | 7 | #[cfg(feature = "redis_registry")] 8 | mod redis_registry; 9 | #[cfg(feature = "redis_registry")] 10 | pub use redis_registry::RedisRegistry; 11 | 12 | -------------------------------------------------------------------------------- /utilities/registry/src/registry_record_error.rs: -------------------------------------------------------------------------------- 1 | // utilities\registry\src\registry_record_error.rs 2 | 3 | use std::fmt; 4 | 5 | /// Defines errors related to registry operations. 6 | #[derive(Debug)] 7 | pub enum RegistryError { 8 | /// An error occurred while serializing or deserializing a record. 9 | SerializationError(String), 10 | 11 | /// An error occurred during a backend operation, such as database access. 12 | BackendError(String), 13 | 14 | /// The requested record was not found. 15 | RecordNotFound(String), 16 | 17 | /// The registry has reached its maximum capacity. 18 | CapacityExceeded, 19 | 20 | /// A generic error occurred. 21 | GenericError(String), 22 | 23 | Custom(String), // Add this variant 24 | } 25 | 26 | impl fmt::Display for RegistryError { 27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 28 | match self { 29 | RegistryError::SerializationError(msg) => write!(f, "Serialization error: {}", msg), 30 | RegistryError::BackendError(msg) => write!(f, "Backend error: {}", msg), 31 | RegistryError::RecordNotFound(id) => write!(f, "Record not found: {}", id), 32 | RegistryError::CapacityExceeded => write!(f, "Registry capacity exceeded"), 33 | RegistryError::GenericError(msg) => write!(f, "Error: {}", msg), 34 | RegistryError::Custom(msg)=>write!(f,"User-Defined Error : {}",msg) 35 | } 36 | } 37 | } 38 | 39 | impl std::error::Error for RegistryError {} 40 | -------------------------------------------------------------------------------- /utilities/registry/src/registry_traits.rs: -------------------------------------------------------------------------------- 1 | // utilities/registry/src/registry_traits.rs 2 | use crate::{Record, RegistryError}; 3 | use async_trait::async_trait; 4 | 5 | /// A generic trait for managing records in a registry. 6 | #[async_trait] 7 | pub trait Registry: Send + Sync { 8 | /// Adds or updates a record in the registry. 9 | /// 10 | /// # Arguments 11 | /// * `record` - The record to add or update. 12 | /// 13 | /// # Returns 14 | /// * `Ok(())` - If the record is successfully added or updated. 15 | /// * `Err(RegistryError)` - If an error occurs during the operation. 16 | async fn add(&self, record: R) -> Result<(), RegistryError>; 17 | 18 | /// Retrieves a record by its unique identifier. 19 | /// 20 | /// # Arguments 21 | /// * `identifier` - The unique identifier of the record to retrieve. 22 | /// 23 | /// # Returns 24 | /// * `Some(R)` - If a record with the given identifier exists. 25 | /// * `None` - If no record is found with the given identifier. 26 | async fn get(&self, identifier: &str) -> Option; 27 | 28 | /// Lists all records in the registry. 29 | /// 30 | /// # Returns 31 | /// * `Vec` - A vector containing all records in the registry. 32 | async fn list(&self) -> Vec; 33 | 34 | /// Removes a record by its unique identifier. 35 | /// 36 | /// # Arguments 37 | /// * `identifier` - The unique identifier of the record to remove. 38 | /// 39 | /// # Returns 40 | /// * `Ok(())` - If the record is successfully removed. 41 | /// * `Err(RegistryError)` - If an error occurs during the removal. 42 | async fn remove(&self, identifier: &str) -> Result<(), RegistryError>; 43 | 44 | /// Sets the maximum capacity of the registry. 45 | /// 46 | /// # Arguments 47 | /// * `capacity` - The maximum number of records the registry can hold. 48 | async fn set_capacity(&self, capacity: usize); 49 | 50 | /// Gets the current capacity of the registry. 51 | /// 52 | /// # Returns 53 | /// * `usize` - The current capacity of the registry. 54 | async fn get_capacity(&self) -> usize; 55 | 56 | /// Implements a LRU -> Last-Recent-Update Policy into the Registry 57 | async fn remove_lru(&self) -> Result<(), RegistryError>; 58 | } 59 | --------------------------------------------------------------------------------