Skip to main content

phantom_protocol/transport/
device_profile.rs

1//! Device Profile System
2//!
3//! Три тира устройств с адаптивными параметрами:
4//! - Constrained: ESP32, дроны, IoT (520KB RAM, нет HW AES)
5//! - Standard: смартфоны, SBC, RPi (1-4GB RAM)
6//! - Performance: серверы, десктопы (8+ GB RAM, HW AES)
7//!
8//! PQ-безопасность обязательна для ВСЕХ tier'ов.
9
10use crate::crypto::adaptive_crypto::{CipherSuite, HwCaps};
11
12/// Device tier classification
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum DeviceTier {
15    /// IoT, drones, ESP32, STM32, old mobile phones
16    /// ~520KB RAM, no HW AES, limited CPU
17    Constrained,
18    /// Smartphones, Raspberry Pi, SBCs
19    /// 1-4GB RAM, may or may not have HW AES
20    Standard,
21    /// Servers, desktops, modern laptops
22    /// 8+GB RAM, HW AES available
23    Performance,
24}
25
26/// PQ KEM security level
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum PqKemLevel {
29    /// Kyber512 — NIST Level 1 (~21KB RAM), for Constrained
30    Kyber512,
31    /// Kyber768 — NIST Level 3 (~29KB RAM), for Standard/Performance
32    Kyber768,
33}
34
35/// PQ Signature level
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum PqSignLevel {
38    /// Dilithium2 — NIST Level 2 (~40KB RAM)
39    Dilithium2,
40    /// Dilithium3 — NIST Level 3 (~70KB RAM)
41    Dilithium3,
42}
43
44/// Complete device profile — all transport parameters
45#[derive(Debug, Clone)]
46pub struct DeviceProfile {
47    /// Device classification
48    pub tier: DeviceTier,
49    /// Preferred cipher suite
50    pub cipher: CipherSuite,
51    /// PQ KEM level (always present — PQ is mandatory)
52    pub pq_kem: PqKemLevel,
53    /// PQ signature level (always present)
54    pub pq_sign: PqSignLevel,
55    /// Buffer size for I/O operations
56    pub buffer_size: usize,
57    /// Maximum concurrent streams
58    pub max_streams: u16,
59    /// Enable UDP packet coalescing
60    pub coalescing: bool,
61    /// Maximum coalesced datagram size
62    pub max_datagram_size: usize,
63    /// Enable compression
64    pub compression: bool,
65    /// Maximum packet payload size
66    pub max_payload: usize,
67}
68
69impl DeviceProfile {
70    /// Auto-detect the optimal profile for this device
71    pub fn auto_detect() -> Self {
72        let caps = HwCaps::detect();
73        let available_ram = Self::estimate_available_ram();
74
75        let tier = if available_ram < 1_048_576 {
76            // < 1MB
77            DeviceTier::Constrained
78        } else if available_ram < 512_000_000 {
79            // < 512MB
80            DeviceTier::Standard
81        } else {
82            DeviceTier::Performance
83        };
84
85        Self::for_tier(tier, &caps)
86    }
87
88    /// Create a profile for a specific tier with given HW caps
89    pub fn for_tier(tier: DeviceTier, caps: &HwCaps) -> Self {
90        match tier {
91            DeviceTier::Constrained => Self {
92                tier,
93                cipher: CipherSuite::ChaCha20Poly1305, // Always ChaCha20 for constrained
94                pq_kem: PqKemLevel::Kyber512,          // Light PQ — 21KB RAM
95                pq_sign: PqSignLevel::Dilithium2,      // Light PQ sig — 40KB RAM
96                buffer_size: 2 * 1024,                 // 2KB buffers
97                max_streams: 4,
98                coalescing: false,      // No coalescing overhead
99                max_datagram_size: 512, // Tiny datagrams
100                compression: false,     // CPU more precious than bandwidth
101                max_payload: 256,
102            },
103            DeviceTier::Standard => Self {
104                tier,
105                cipher: caps.recommended_cipher(), // Auto: AES if HW, else ChaCha
106                pq_kem: PqKemLevel::Kyber768,      // Full PQ — 29KB RAM
107                pq_sign: PqSignLevel::Dilithium3,  // Full PQ sig
108                buffer_size: 16 * 1024,            // 16KB buffers
109                max_streams: 64,
110                coalescing: true,
111                max_datagram_size: 4096,
112                compression: true, // Zstd-1
113                max_payload: 1400, // Standard MTU
114            },
115            DeviceTier::Performance => Self {
116                tier,
117                cipher: CipherSuite::Aes256Gcm, // Always AES-GCM (HW)
118                pq_kem: PqKemLevel::Kyber768,   // Full PQ
119                pq_sign: PqSignLevel::Dilithium3, // Full PQ sig
120                buffer_size: 64 * 1024,         // 64KB buffers
121                max_streams: 256,
122                coalescing: true,
123                max_datagram_size: 8192, // Jumbo-like
124                compression: true,       // LZ4 (ultra-fast)
125                max_payload: 8192,
126            },
127        }
128    }
129
130    /// Create a specific named profile
131    pub fn constrained() -> Self {
132        Self::for_tier(DeviceTier::Constrained, &HwCaps { has_hw_aes: false })
133    }
134
135    pub fn standard() -> Self {
136        Self::for_tier(DeviceTier::Standard, &HwCaps::detect())
137    }
138
139    pub fn performance() -> Self {
140        Self::for_tier(DeviceTier::Performance, &HwCaps { has_hw_aes: true })
141    }
142
143    /// Estimate available RAM (platform-specific)
144    fn estimate_available_ram() -> usize {
145        // On std targets, use sysinfo-like heuristics
146        // For now, use a simple approach based on pointer size
147        #[cfg(target_pointer_width = "64")]
148        {
149            8_000_000_000
150        } // 64-bit → assume Performance-class
151
152        #[cfg(target_pointer_width = "32")]
153        {
154            512_000
155        } // 32-bit → assume Constrained-class
156
157        #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
158        {
159            256_000
160        } // 16-bit → definitely Constrained
161    }
162
163    /// Whether this profile supports the full PQ handshake (Kyber768)
164    pub fn is_full_pq(&self) -> bool {
165        matches!(self.pq_kem, PqKemLevel::Kyber768)
166    }
167
168    /// Byte for handshake encoding
169    pub fn tier_byte(&self) -> u8 {
170        match self.tier {
171            DeviceTier::Constrained => 0,
172            DeviceTier::Standard => 1,
173            DeviceTier::Performance => 2,
174        }
175    }
176}
177
178impl std::fmt::Display for DeviceProfile {
179    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180        write!(
181            f,
182            "{:?} [cipher={:?}, kem={:?}, sign={:?}, buf={}KB, streams={}]",
183            self.tier,
184            self.cipher,
185            self.pq_kem,
186            self.pq_sign,
187            self.buffer_size / 1024,
188            self.max_streams,
189        )
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn constrained_profile() {
199        let p = DeviceProfile::constrained();
200        assert_eq!(p.tier, DeviceTier::Constrained);
201        assert_eq!(p.cipher, CipherSuite::ChaCha20Poly1305);
202        assert_eq!(p.pq_kem, PqKemLevel::Kyber512);
203        assert_eq!(p.pq_sign, PqSignLevel::Dilithium2);
204        assert_eq!(p.buffer_size, 2048);
205        assert!(!p.coalescing);
206        assert!(!p.compression);
207        eprintln!("Constrained: {}", p);
208    }
209
210    #[test]
211    fn standard_profile() {
212        let p = DeviceProfile::standard();
213        assert_eq!(p.tier, DeviceTier::Standard);
214        assert_eq!(p.pq_kem, PqKemLevel::Kyber768);
215        assert!(p.coalescing);
216        assert!(p.compression);
217        eprintln!("Standard: {}", p);
218    }
219
220    #[test]
221    fn performance_profile() {
222        let p = DeviceProfile::performance();
223        assert_eq!(p.tier, DeviceTier::Performance);
224        assert_eq!(p.cipher, CipherSuite::Aes256Gcm);
225        assert_eq!(p.pq_kem, PqKemLevel::Kyber768);
226        assert_eq!(p.buffer_size, 65536);
227        eprintln!("Performance: {}", p);
228    }
229
230    #[test]
231    fn auto_detect_profile() {
232        let p = DeviceProfile::auto_detect();
233        eprintln!("Auto: {}", p);
234        // On 64-bit host, should be Performance
235        #[cfg(target_pointer_width = "64")]
236        assert_eq!(p.tier, DeviceTier::Performance);
237    }
238
239    #[test]
240    fn all_tiers_have_pq() {
241        for tier in [
242            DeviceTier::Constrained,
243            DeviceTier::Standard,
244            DeviceTier::Performance,
245        ] {
246            let p = DeviceProfile::for_tier(tier, &HwCaps::detect());
247            // PQ KEM is always present
248            assert!(matches!(
249                p.pq_kem,
250                PqKemLevel::Kyber512 | PqKemLevel::Kyber768
251            ));
252            // PQ Signature is always present
253            assert!(matches!(
254                p.pq_sign,
255                PqSignLevel::Dilithium2 | PqSignLevel::Dilithium3
256            ));
257            eprintln!("{:?}: PQ KEM={:?}, PQ Sign={:?}", tier, p.pq_kem, p.pq_sign);
258        }
259    }
260}