diff --git a/Cargo.toml b/Cargo.toml index ba6a00e..dea5c3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,14 @@ path = "src/main.rs" required-features = ["daemon"] [features] -default = [] +default = ["client"] +client = [ + "dep:aes-gcm", + "dep:rand_core", + "dep:zeroize", +] daemon = [ + "client", "dep:anyhow", "dep:chrono", "dep:cultcache-rs", @@ -30,14 +36,14 @@ daemon = [ ] [dependencies] -aes-gcm = "0.10" +aes-gcm = { version = "0.10", optional = true } anyhow = { version = "1", optional = true } base64 = "0.23" chrono = { version = "0.4", features = ["serde"], optional = true } cultcache-rs = { git = "https://github.com/GameCult/cultcache-rs.git", rev = "fdbf3bf0a4b3f6b617c224d7439feb8e69d4474c", optional = true } cultnet-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "75c180782aeba7cfd22d6412877397708a4ed28f", optional = true } ed25519-dalek = { version = "2.2", optional = true } -rand_core = { version = "0.6", features = ["getrandom"] } +rand_core = { version = "0.6", features = ["getrandom"], optional = true } rmp-serde = "1" serde = { version = "1", features = ["derive"] } serde_bytes = { version = "0.11", optional = true } @@ -46,4 +52,4 @@ sha2 = "0.10" thiserror = "2" ureq = { version = "3.4", default-features = false, features = ["rustls"], optional = true } uuid = { version = "1", features = ["v4"], optional = true } -zeroize = "1" +zeroize = { version = "1", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 5cd7b68..3769b5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,9 @@ use std::collections::HashSet; +#[cfg(feature = "client")] use std::io::{Read, Write}; +#[cfg(feature = "client")] use std::net::{SocketAddr, TcpStream}; +#[cfg(feature = "client")] use std::time::Duration; #[cfg(feature = "daemon")] @@ -8,14 +11,18 @@ use std::collections::HashMap; #[cfg(feature = "daemon")] use std::path::Path; +#[cfg(feature = "client")] use aes_gcm::aead::{Aead, Payload}; +#[cfg(feature = "client")] use aes_gcm::{Aes256Gcm, KeyInit, Nonce}; #[cfg(feature = "daemon")] use cultcache_rs::{CultCache, DatabaseEntry, OwnedRedbMessagePackBackingStore}; +#[cfg(feature = "client")] use rand_core::{OsRng, RngCore}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; +#[cfg(feature = "client")] use zeroize::{Zeroize, Zeroizing}; #[cfg(feature = "daemon")] @@ -59,9 +66,11 @@ pub const RESULT_SCHEMA_ID: &str = "gamecult.codex.transport_result.v2"; pub const RECEIPT_SCHEMA_ID: &str = "gamecult.codex.transport_receipt.v2"; pub const ENVELOPE_SCHEMA_ID: &str = "gamecult.codex.transport_envelope.v2"; +#[cfg(feature = "client")] #[derive(Clone, PartialEq, Eq)] pub struct CodexTransportKey([u8; 32]); +#[cfg(feature = "client")] impl CodexTransportKey { pub fn from_connection_secret(secret: &str) -> Result { if secret.trim().is_empty() || secret.trim() != secret { @@ -71,6 +80,7 @@ impl CodexTransportKey { } } +#[cfg(feature = "client")] impl Drop for CodexTransportKey { fn drop(&mut self) { self.0.zeroize(); @@ -634,12 +644,14 @@ pub fn canonical_invocation_bytes( rmp_serde::to_vec(invocation).map_err(|_| ContractError::Encoding) } +#[cfg(feature = "client")] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum CodexEnvelopeKind { Invocation, Result, } +#[cfg(feature = "client")] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct CodexTransportEnvelope { pub schema_id: String, @@ -650,6 +662,7 @@ pub struct CodexTransportEnvelope { pub ciphertext: Vec, } +#[cfg(feature = "client")] pub fn encrypt_invocation( invocation: &CodexTransportInvocation, security: &CodexTransportKey, @@ -664,6 +677,7 @@ pub fn encrypt_invocation( ) } +#[cfg(feature = "client")] pub fn decrypt_invocation( envelope: &CodexTransportEnvelope, security: &CodexTransportKey, @@ -680,6 +694,7 @@ pub fn decrypt_invocation( Ok(invocation) } +#[cfg(feature = "client")] pub fn encrypt_result( result: &CodexTransportResult, security: &CodexTransportKey, @@ -694,6 +709,7 @@ pub fn encrypt_result( ) } +#[cfg(feature = "client")] pub fn decrypt_result( envelope: &CodexTransportEnvelope, security: &CodexTransportKey, @@ -712,6 +728,7 @@ pub fn decrypt_result( Ok(result) } +#[cfg(feature = "client")] #[derive(Clone)] pub struct CodexConnectorClient { endpoint: SocketAddr, @@ -720,6 +737,7 @@ pub struct CodexConnectorClient { response_timeout: Option, } +#[cfg(feature = "client")] impl CodexConnectorClient { pub fn new( endpoint: SocketAddr, @@ -770,6 +788,7 @@ impl CodexConnectorClient { } } +#[cfg(feature = "client")] #[derive(Debug, Error)] pub enum CodexConnectorClientError { #[error("invalid connector client configuration")] @@ -784,12 +803,14 @@ pub enum CodexConnectorClientError { Transport(#[from] ServiceError), } +#[cfg(feature = "client")] #[derive(Debug)] enum TransportFrameError { Connection(std::io::Error), Size, } +#[cfg(feature = "client")] fn client_frame_error(error: TransportFrameError) -> CodexConnectorClientError { match error { TransportFrameError::Connection(error) => CodexConnectorClientError::Connection(error), @@ -797,6 +818,7 @@ fn client_frame_error(error: TransportFrameError) -> CodexConnectorClientError { } } +#[cfg(feature = "client")] fn read_transport_frame( reader: &mut impl Read, max_frame_bytes: usize, @@ -816,6 +838,7 @@ fn read_transport_frame( Ok(payload) } +#[cfg(feature = "client")] fn write_transport_frame( writer: &mut impl Write, payload: &[u8], @@ -831,6 +854,7 @@ fn write_transport_frame( .map_err(TransportFrameError::Connection) } +#[cfg(feature = "client")] fn encrypt_message( caller_runtime_id: &str, request_id: &str, @@ -868,6 +892,7 @@ fn encrypt_message( }) } +#[cfg(feature = "client")] fn decrypt_message( envelope: &CodexTransportEnvelope, security: &CodexTransportKey, @@ -890,6 +915,7 @@ fn decrypt_message( .map_err(|_| ServiceError::Encryption) } +#[cfg(feature = "client")] fn envelope_aad( schema_id: &str, caller_runtime_id: &str, @@ -900,6 +926,7 @@ fn envelope_aad( .map_err(|_| ServiceError::Encoding) } +#[cfg(feature = "client")] fn validate_envelope( envelope: &CodexTransportEnvelope, expected_kind: CodexEnvelopeKind, @@ -1643,6 +1670,7 @@ mod tests { .unwrap() } + #[cfg(feature = "client")] fn security(key: &str) -> CodexTransportKey { CodexTransportKey::from_connection_secret(key).unwrap() } @@ -1654,6 +1682,7 @@ mod tests { CodexTransportInvocation::new(caller_runtime_id, 2_000, [7; 32], request).unwrap() } + #[cfg(feature = "client")] #[test] fn encrypted_invocation_hides_content_and_binds_outer_identity() { let invocation = invocation(); @@ -1682,6 +1711,7 @@ mod tests { ); } + #[cfg(feature = "client")] #[test] fn shared_client_transports_one_exact_encrypted_request_and_result() { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();