shield_core/
lib.rs

1//! # Shield Core
2//!
3//! EXPTIME-secure encryption library - Rust implementation.
4//!
5//! ## Security Model
6//!
7//! Shield uses only symmetric primitives with proven exponential-time security.
8//! Breaking requires 2^256 operations - no shortcut exists.
9//! - PBKDF2-SHA256 for key derivation
10//! - AES-256-CTR-like stream cipher (SHA256-based keystream)
11//! - HMAC-SHA256 for authentication
12//!
13//! ## Usage
14//!
15//! ```rust
16//! use shield_core::Shield;
17//!
18//! let shield = Shield::new("password", "service.com");
19//! let ciphertext = shield.encrypt(b"secret data").unwrap();
20//! let plaintext = shield.decrypt(&ciphertext).unwrap();
21//! ```
22//!
23//! ## Error Handling
24//!
25//! All fallible operations return `Result<T, ShieldError>`. Common errors:
26//! - [`ShieldError::AuthenticationFailed`] - MAC verification failed (tampered/wrong key)
27//! - [`ShieldError::CiphertextTooShort`] - Input too small to be valid ciphertext
28//! - [`ShieldError::RandomFailed`] - System RNG failure (extremely rare)
29//! - [`ShieldError::InvalidFormat`] - Malformed input data
30//!
31//! ## Panics
32//!
33//! Functions that may panic are documented, but panics are rare and indicate:
34//! - Internal invariant violations (should never happen in correct usage)
35//! - System-level failures (e.g., time going backwards)
36
37#![forbid(unsafe_code)]
38#![warn(clippy::all, clippy::pedantic)]
39#![allow(clippy::module_name_repetitions)]
40// Error/panic docs are centralized above; individual function docs are concise
41#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)]
42
43mod error;
44mod shield;
45mod stream;
46mod ratchet;
47mod totp;
48mod signatures;
49mod exchange;
50mod rotation;
51mod group;
52mod identity;
53pub mod password;
54pub mod channel;
55#[cfg(feature = "async")]
56pub mod channel_async;
57#[cfg(feature = "wasm")]
58mod wasm;
59
60pub use error::{ShieldError, Result};
61pub use shield::Shield;
62pub use stream::StreamCipher;
63pub use ratchet::RatchetSession;
64pub use totp::{TOTP, RecoveryCodes};
65pub use signatures::{SymmetricSignature, LamportSignature};
66pub use exchange::{PAKEExchange, QRExchange, KeySplitter};
67pub use rotation::KeyRotationManager;
68pub use group::{GroupEncryption, BroadcastEncryption, EncryptedGroupMessage, EncryptedBroadcast};
69pub use identity::{IdentityProvider, Identity, Session, SecureSession};
70pub use channel::{ShieldChannel, ChannelConfig, ShieldListener};
71#[cfg(feature = "async")]
72pub use channel_async::AsyncShieldChannel;
73
74#[cfg(feature = "wasm")]
75pub use wasm::*;
76
77/// Quick encrypt with pre-shared key (no password derivation)
78pub fn quick_encrypt(key: &[u8; 32], data: &[u8]) -> Result<Vec<u8>> {
79    Shield::encrypt_with_key(key, data)
80}
81
82/// Quick decrypt with pre-shared key
83pub fn quick_decrypt(key: &[u8; 32], encrypted: &[u8]) -> Result<Vec<u8>> {
84    Shield::decrypt_with_key(key, encrypted)
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_roundtrip() {
93        let shield = Shield::new("test_password", "test.service");
94        let plaintext = b"Hello, EXPTIME-secure world!";
95
96        let encrypted = shield.encrypt(plaintext).unwrap();
97        let decrypted = shield.decrypt(&encrypted).unwrap();
98
99        assert_eq!(plaintext.as_slice(), decrypted.as_slice());
100    }
101
102    #[test]
103    fn test_quick_functions() {
104        let key = [0x42u8; 32];
105        let data = b"Quick test message";
106
107        let encrypted = quick_encrypt(&key, data).unwrap();
108        let decrypted = quick_decrypt(&key, &encrypted).unwrap();
109
110        assert_eq!(data.as_slice(), decrypted.as_slice());
111    }
112
113    #[test]
114    fn test_tamper_detection() {
115        let shield = Shield::new("password", "service");
116        let mut encrypted = shield.encrypt(b"data").unwrap();
117
118        // Tamper with ciphertext
119        encrypted[20] ^= 0xFF;
120
121        assert!(shield.decrypt(&encrypted).is_err());
122    }
123
124    #[test]
125    fn test_wrong_password() {
126        let shield1 = Shield::new("password1", "service");
127        let shield2 = Shield::new("password2", "service");
128
129        let encrypted = shield1.encrypt(b"secret").unwrap();
130
131        assert!(shield2.decrypt(&encrypted).is_err());
132    }
133
134    #[test]
135    fn test_different_services() {
136        let shield1 = Shield::new("password", "service1");
137        let shield2 = Shield::new("password", "service2");
138
139        let encrypted = shield1.encrypt(b"secret").unwrap();
140
141        assert!(shield2.decrypt(&encrypted).is_err());
142    }
143}