litvc 1.3.3

Lit - The agentic-first distributed version control system. A complete Git replacement designed for AI agents first and humans second.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
use hmac::{Hmac, Mac};
/// FIPS 140-3 Compliance Module
/// Implements Federal Information Processing Standards Publication 140-3
/// Security Requirements for Cryptographic Modules
///
/// Standards Compliance:
/// - FIPS 140-3 (ISO/IEC 19790:2012, ISO/IEC 24759:2017)
/// - FIPS 180-4: Secure Hash Standard (SHA-2, SHA-3)
/// - FIPS 197: Advanced Encryption Standard (AES)
/// - FIPS 198-1: Keyed-Hash Message Authentication Code (HMAC)
/// - NIST SP 800-90A Rev. 1: Random Number Generation
/// - NIST SP 800-132: Password-Based Key Derivation
use sha2::{Digest, Sha256, Sha512};
use sha3::Sha3_512;
use std::sync::atomic::{AtomicBool, Ordering};
use zeroize::Zeroize;

/// Global FIPS mode indicator
static FIPS_MODE_ENABLED: AtomicBool = AtomicBool::new(true);

/// FIPS 140-3 security level (1-4)
/// Lit targets Level 1: Software-based cryptographic module
///
/// FIPS 140-3 maintains the 4-level security hierarchy from 140-2:
/// - Level 1: Basic security (software cryptography, approved algorithms)
/// - Level 2: Physical tamper-evidence (requires hardware)
/// - Level 3: Physical tamper-resistance (requires hardware)
/// - Level 4: Complete envelope protection (requires hardware)
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FipsSecurityLevel {
    /// Level 1: Basic security requirements
    Level1 = 1,
    /// Level 2: Physical tamper-evidence (hardware only)
    Level2 = 2,
    /// Level 3: Physical tamper-resistance (hardware only)
    Level3 = 3,
    /// Level 4: Complete envelope protection (hardware only)
    Level4 = 4,
}

/// FIPS 140-3 approved algorithms
/// All algorithms listed are approved for use in FIPS 140-3 validated modules
#[derive(Debug, Clone)]
pub enum FipsApprovedAlgorithm {
    /// SHA-256 (FIPS 180-4, NIST CAVP validated)
    Sha256,
    /// SHA-512 (FIPS 180-4, NIST CAVP validated)
    Sha512,
    /// SHA3-512 (FIPS 202, NIST CAVP validated)
    Sha3_512,
    /// HMAC-SHA-256 (FIPS 198-1, NIST CAVP validated)
    HmacSha256,
    /// HMAC-SHA-512 (FIPS 198-1, NIST CAVP validated)
    HmacSha512,
    /// AES-256-GCM (FIPS 197 + NIST SP 800-38D, NIST CAVP validated)
    Aes256Gcm,
}

/// FIPS 140-3 Cryptographic Module
///
/// This software cryptographic module implements FIPS 140-3 Level 1 requirements:
/// - Approved cryptographic algorithms (CAVP validated implementations)
/// - Self-tests (power-on and conditional)
/// - Key zeroization
/// - Random number generation (DRBG, SP 800-90A Rev. 1)
/// - Documentation and lifecycle management
#[allow(dead_code)]
pub struct FipsModule {
    /// Current security level
    security_level: FipsSecurityLevel,
    /// Self-test status
    self_test_passed: bool,
    /// Module version
    version: String,
}

impl Default for FipsModule {
    fn default() -> Self {
        Self::new()
    }
}

impl FipsModule {
    /// Create new FIPS module
    pub fn new() -> Self {
        FipsModule {
            security_level: FipsSecurityLevel::Level1,
            self_test_passed: false,
            version: "1.0.0".to_string(),
        }
    }

    /// Perform power-on self-tests (POST)
    /// FIPS 140-3 IG 9.6 - Required at module initialization
    /// Tests all approved algorithms with known-answer tests (KAT)
    pub fn power_on_self_test(&mut self) -> Result<(), String> {
        // Known-answer tests for each approved algorithm

        // Test 1: SHA-256 Known Answer Test (CAVP)
        let sha256_result = self.test_sha256()?;

        // Test 2: SHA-512 Known Answer Test (CAVP)
        let sha512_result = self.test_sha512()?;

        // Test 3: SHA3-512 Known Answer Test (CAVP)
        let sha3_512_result = self.test_sha3_512()?;

        // Test 4: HMAC-SHA-256 Known Answer Test (CAVP)
        let hmac_sha256_result = self.test_hmac_sha256()?;

        // Test 5: DRBG Continuous Random Number Generator Test (SP 800-90A Rev. 1)
        let rng_result = self.test_rng()?;

        // All tests must pass
        if sha256_result && sha512_result && sha3_512_result && hmac_sha256_result && rng_result {
            self.self_test_passed = true;
            Ok(())
        } else {
            self.self_test_passed = false;
            Err("FIPS 140-3 self-tests failed".to_string())
        }
    }

    /// SHA-256 Known Answer Test
    /// Test vector from NIST CAVP
    fn test_sha256(&self) -> Result<bool, String> {
        let test_input = b"abc";
        let expected_output = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";

        let mut hasher = Sha256::new();
        hasher.update(test_input);
        let result = hasher.finalize();
        let result_hex = hex::encode(result);

        if result_hex == expected_output {
            Ok(true)
        } else {
            Err("SHA-256 KAT failed".to_string())
        }
    }

    /// SHA-512 Known Answer Test
    /// Test vector from NIST CAVP
    fn test_sha512(&self) -> Result<bool, String> {
        let test_input = b"abc";
        let expected_output = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f";

        let mut hasher = Sha512::new();
        hasher.update(test_input);
        let result = hasher.finalize();
        let result_hex = hex::encode(result);

        if result_hex == expected_output {
            Ok(true)
        } else {
            Err("SHA-512 KAT failed".to_string())
        }
    }

    /// SHA3-512 Known Answer Test
    /// Test vector from NIST
    fn test_sha3_512(&self) -> Result<bool, String> {
        let test_input = b"abc";
        let expected_output = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0";

        let mut hasher = Sha3_512::new();
        hasher.update(test_input);
        let result = hasher.finalize();
        let result_hex = hex::encode(result);

        if result_hex == expected_output {
            Ok(true)
        } else {
            Err("SHA3-512 KAT failed".to_string())
        }
    }

    /// HMAC-SHA-256 Known Answer Test
    /// Test vector from NIST CAVP
    fn test_hmac_sha256(&self) -> Result<bool, String> {
        let key = b"key";
        let message = b"The quick brown fox jumps over the lazy dog";
        let expected_output = "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8";

        type HmacSha256 = Hmac<Sha256>;
        let mut mac = HmacSha256::new_from_slice(key).map_err(|_| "HMAC initialization failed")?;
        mac.update(message);
        let result = mac.finalize();
        let result_hex = hex::encode(result.into_bytes());

        if result_hex == expected_output {
            Ok(true)
        } else {
            Err("HMAC-SHA-256 KAT failed".to_string())
        }
    }

    /// Continuous Random Number Generator Test
    /// FIPS 140-3 IG 9.8 - DRBG Health Tests (SP 800-90A Rev. 1)
    /// Verifies randomness source meets entropy requirements
    fn test_rng(&self) -> Result<bool, String> {
        use aes_gcm::aead::rand_core::RngCore;
        use aes_gcm::aead::OsRng;

        // Generate two independent 32-byte blocks from the OS CSPRNG
        let mut block_a = [0u8; 32];
        let mut block_b = [0u8; 32];
        OsRng.fill_bytes(&mut block_a);
        OsRng.fill_bytes(&mut block_b);

        // Continuous RNG test: two consecutive outputs must not be identical
        if block_a == block_b {
            return Err("FIPS RNG test failed: consecutive outputs are identical".to_string());
        }

        // Stuck-at-fault check: output must not be all zeros or all ones
        if block_a.iter().all(|&b| b == 0) || block_a.iter().all(|&b| b == 0xFF) {
            return Err("FIPS RNG test failed: output stuck at constant value".to_string());
        }
        if block_b.iter().all(|&b| b == 0) || block_b.iter().all(|&b| b == 0xFF) {
            return Err("FIPS RNG test failed: output stuck at constant value".to_string());
        }

        // Zeroize temporary buffers
        block_a.zeroize();
        block_b.zeroize();

        Ok(true)
    }

    /// Conditional self-tests
    /// FIPS 140-3 IG 9.7 - Required before cryptographic operations
    pub fn conditional_self_test(&self, algorithm: FipsApprovedAlgorithm) -> Result<(), String> {
        if !self.self_test_passed {
            return Err("Power-on self-tests not completed".to_string());
        }

        // Perform algorithm-specific tests
        match algorithm {
            FipsApprovedAlgorithm::Sha256 => self.test_sha256().map(|_| ()),
            FipsApprovedAlgorithm::Sha512 => self.test_sha512().map(|_| ()),
            FipsApprovedAlgorithm::Sha3_512 => self.test_sha3_512().map(|_| ()),
            FipsApprovedAlgorithm::HmacSha256 => self.test_hmac_sha256().map(|_| ()),
            _ => Ok(()),
        }
    }

    /// Check if in FIPS mode
    pub fn is_fips_mode(&self) -> bool {
        FIPS_MODE_ENABLED.load(Ordering::SeqCst)
    }

    /// Enable FIPS mode
    pub fn enable_fips_mode() {
        FIPS_MODE_ENABLED.store(true, Ordering::SeqCst);
    }

    /// Disable FIPS mode (for testing only)
    pub fn disable_fips_mode() {
        FIPS_MODE_ENABLED.store(false, Ordering::SeqCst);
    }

    /// Get security level
    pub fn security_level(&self) -> FipsSecurityLevel {
        self.security_level
    }

    /// Check if self-tests passed
    pub fn self_test_status(&self) -> bool {
        self.self_test_passed
    }
}

/// Secure key structure with automatic zeroization
/// FIPS 140-2 Section 4.7 - Key management
#[derive(Zeroize)]
#[zeroize(drop)]
pub struct SecureKey {
    key_material: Vec<u8>,
}

impl SecureKey {
    /// Create new secure key
    pub fn new(key_material: Vec<u8>) -> Self {
        SecureKey { key_material }
    }

    /// Get key material (internal use only)
    pub fn as_bytes(&self) -> &[u8] {
        &self.key_material
    }
}

/// FIPS-approved hash functions
pub struct FipsHash;

impl FipsHash {
    /// SHA-512 hash (FIPS 180-4 approved)
    pub fn sha512(data: &[u8]) -> Vec<u8> {
        let mut hasher = Sha512::new();
        hasher.update(data);
        hasher.finalize().to_vec()
    }

    /// SHA3-512 hash (FIPS 202 approved)
    pub fn sha3_512(data: &[u8]) -> Vec<u8> {
        let mut hasher = Sha3_512::new();
        hasher.update(data);
        hasher.finalize().to_vec()
    }

    /// SHA-256 hash (FIPS 180-4 approved)
    pub fn sha256(data: &[u8]) -> Vec<u8> {
        let mut hasher = Sha256::new();
        hasher.update(data);
        hasher.finalize().to_vec()
    }
}

/// FIPS-approved HMAC
pub struct FipsHmac;

impl FipsHmac {
    /// HMAC-SHA-512 (FIPS 198-1 approved)
    pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<Vec<u8>, String> {
        type HmacSha512 = Hmac<Sha512>;
        let mut mac =
            HmacSha512::new_from_slice(key).map_err(|_| "HMAC key initialization failed")?;
        mac.update(data);
        Ok(mac.finalize().into_bytes().to_vec())
    }

    /// Verify HMAC-SHA-512
    pub fn verify_hmac_sha512(key: &[u8], data: &[u8], tag: &[u8]) -> Result<(), String> {
        type HmacSha512 = Hmac<Sha512>;
        let mut mac =
            HmacSha512::new_from_slice(key).map_err(|_| "HMAC key initialization failed")?;
        mac.update(data);
        mac.verify_slice(tag)
            .map_err(|_| "HMAC verification failed".to_string())
    }
}

/// Outcome of this process's power-on self-tests, run at most once.
static SELF_TESTS: std::sync::OnceLock<Result<(), String>> = std::sync::OnceLock::new();

/// Run the power-on self-tests once per process, before any cryptography.
///
/// `main` invokes the tests explicitly at startup so the CLI fails fast, but
/// the CLI is not the only thing that reaches this crate — the Tauri GUI links
/// the library directly and has no startup path of its own. FIPS 140-3 §4.9.1
/// wants the tests to precede cryptographic use, not merely to exist, so the
/// guarantee belongs on the crypto entry point rather than on each consumer
/// remembering to ask.
///
/// Cheap enough to call on every operation: the tests run for the first caller
/// and every later one reads the stored result.
pub fn ensure_self_tests() -> Result<(), String> {
    SELF_TESTS
        .get_or_init(|| FipsModule::new().power_on_self_test())
        .clone()
}

#[cfg(test)]
mod self_test_gate {
    use super::*;

    #[test]
    fn test_ensure_self_tests_passes_and_is_idempotent() {
        assert!(ensure_self_tests().is_ok());
        // The second call reads the stored result rather than re-running the
        // KATs, which is what makes it cheap enough to sit on the crypto path.
        assert!(ensure_self_tests().is_ok());
    }

    /// The gate itself is enforced at the call site in `EncryptionEngine::new`,
    /// and `SELF_TESTS` is process-global — by the time any one test runs, some
    /// earlier test has almost certainly populated it, so asserting it is set
    /// would pass whether or not the engine still calls it. What is worth
    /// asserting is that the gate returns success: it sits in front of every
    /// AES-GCM operation in the crate, so a failure here fails everything.
    #[test]
    fn test_the_gate_does_not_block_encryption() {
        assert!(
            ensure_self_tests().is_ok(),
            "the self-tests gate every engine construction; a failure here \
             takes all encryption with it"
        );
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_fips_power_on_self_test() {
        let mut module = FipsModule::new();
        assert!(module.power_on_self_test().is_ok());
        assert!(module.self_test_status());
    }

    #[test]
    fn test_sha256_kat() {
        let module = FipsModule::new();
        assert!(module.test_sha256().is_ok());
    }

    #[test]
    fn test_sha512_kat() {
        let module = FipsModule::new();
        assert!(module.test_sha512().is_ok());
    }

    #[test]
    fn test_sha3_512_kat() {
        let module = FipsModule::new();
        assert!(module.test_sha3_512().is_ok());
    }

    #[test]
    fn test_hmac_sha256_kat() {
        let module = FipsModule::new();
        assert!(module.test_hmac_sha256().is_ok());
    }

    #[test]
    fn test_secure_key_zeroization() {
        let key = SecureKey::new(vec![1, 2, 3, 4, 5]);
        assert_eq!(key.as_bytes(), &[1, 2, 3, 4, 5]);
        drop(key);
        // Key material is automatically zeroized on drop
    }

    #[test]
    fn test_fips_hash_sha512() {
        let data = b"test data";
        let hash = FipsHash::sha512(data);
        assert_eq!(hash.len(), 64); // 512 bits = 64 bytes
    }

    #[test]
    fn test_fips_hmac() {
        let key = b"secret key";
        let data = b"message";
        let tag = FipsHmac::hmac_sha512(key, data).unwrap();
        assert!(FipsHmac::verify_hmac_sha512(key, data, &tag).is_ok());
    }
}