Skip to main content

origin_crypto_sdk/primitives/
tier.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Memory tier configuration for Argon2id KDF.
4//!
5//! Three tiers prove viability across the device spectrum:
6//! - **Nano**: IoT / edge / RPi Zero (8 MB, 2 iter, 1 thread)
7//! - **Standard**: Mid-tier laptop (64 MB, 3 iter, 2 threads)
8//! - **Sovereign**: Datacenter / high-security (256 MB, 5 iter, 4 threads)
9//!
10//! The quantum pipeline (SHAKE-256 + KMAC-256 + hybrid keys) is identical
11//! across all tiers. Only the Argon2id password-to-seed cost changes.
12
13use argon2::Params;
14
15/// Device memory tier — gates only the Argon2id KDF cost.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum MemoryTier {
18    /// IoT / Edge / RPi Zero: 8 MB, 2 iterations, 1 lane.
19    Nano,
20    /// Mid-tier laptop / server: 64 MB, 3 iterations, 2 lanes.
21    #[default]
22    Standard,
23    /// Datacenter / high-security: 256 MB, 5 iterations, 4 lanes.
24    Sovereign,
25}
26
27impl MemoryTier {
28    /// Return Argon2id parameters for this tier.
29    pub fn argon2_params(self, output_len: usize) -> Params {
30        let (m_cost, t_cost, p_cost) = match self {
31            MemoryTier::Nano => (8 * 1024, 2, 1),        // 8 MB
32            MemoryTier::Standard => (64 * 1024, 3, 2),   // 64 MB
33            MemoryTier::Sovereign => (256 * 1024, 5, 4), // 256 MB
34        };
35        Params::new(m_cost, t_cost, p_cost, Some(output_len)).expect("valid Argon2 params")
36    }
37
38    /// Human-readable label.
39    pub fn label(self) -> &'static str {
40        match self {
41            MemoryTier::Nano => "nano",
42            MemoryTier::Standard => "standard",
43            MemoryTier::Sovereign => "sovereign",
44        }
45    }
46
47    /// Get the recommended buffer size for this tier
48    pub fn buffer_size(&self) -> usize {
49        match self {
50            MemoryTier::Nano => 1024,
51            MemoryTier::Standard => 4096,
52            MemoryTier::Sovereign => 4096,
53        }
54    }
55
56    /// Whether memory should be locked (mlock)
57    pub fn should_lock(&self) -> bool {
58        matches!(self, MemoryTier::Sovereign)
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn nano_params_valid() {
68        let p = MemoryTier::Nano.argon2_params(32);
69        assert_eq!(p.m_cost(), 8 * 1024);
70        assert_eq!(p.t_cost(), 2);
71        assert_eq!(p.p_cost(), 1);
72    }
73
74    #[test]
75    fn standard_params_valid() {
76        let p = MemoryTier::Standard.argon2_params(32);
77        assert_eq!(p.m_cost(), 64 * 1024);
78    }
79
80    #[test]
81    fn sovereign_params_valid() {
82        let p = MemoryTier::Sovereign.argon2_params(32);
83        assert_eq!(p.m_cost(), 256 * 1024);
84        assert_eq!(p.t_cost(), 5);
85        assert_eq!(p.p_cost(), 4);
86    }
87
88    #[test]
89    fn default_tier_is_standard() {
90        let tier: MemoryTier = Default::default();
91        assert_eq!(tier, MemoryTier::Standard);
92    }
93
94    #[test]
95    fn tier_labels() {
96        assert_eq!(MemoryTier::Nano.label(), "nano");
97        assert_eq!(MemoryTier::Standard.label(), "standard");
98        assert_eq!(MemoryTier::Sovereign.label(), "sovereign");
99    }
100
101    #[test]
102    fn buffer_sizes() {
103        assert_eq!(MemoryTier::Nano.buffer_size(), 1024);
104        assert_eq!(MemoryTier::Standard.buffer_size(), 4096);
105        assert_eq!(MemoryTier::Sovereign.buffer_size(), 4096);
106    }
107
108    #[test]
109    fn should_lock_only_for_sovereign() {
110        assert!(!MemoryTier::Nano.should_lock());
111        assert!(!MemoryTier::Standard.should_lock());
112        assert!(MemoryTier::Sovereign.should_lock());
113    }
114
115    #[test]
116    fn tier_equality() {
117        assert_eq!(MemoryTier::Nano, MemoryTier::Nano);
118        assert_ne!(MemoryTier::Nano, MemoryTier::Standard);
119    }
120
121    #[test]
122    fn argon2_params_different_output_lens() {
123        for &len in &[16, 32, 64] {
124            let p = MemoryTier::Nano.argon2_params(len);
125            assert_eq!(p.output_len(), Some(len));
126        }
127    }
128}