⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/crypto/
constant_time.rs

1//! Constant-Time Cryptographic Primitives
2//!
3//! Side-channel resistant operations for cryptographic implementations.
4//!
5//! ## Security Considerations
6// Allow dead_code: cryptographic primitives for future subsystem use
7#![allow(dead_code)]
8//! - All operations must execute in constant time to prevent timing attacks
9//! - No data-dependent branches or memory accesses
10//! - Compiler optimizations must not introduce timing variations
11//! - Use volatile reads/writes where necessary to prevent optimization
12
13use core::sync::atomic::{compiler_fence, Ordering};
14
15/// Constant-time byte comparison
16///
17/// Returns 1 if equal, 0 otherwise. Runs in constant time regardless of input.
18#[inline(never)]
19pub(crate) fn ct_eq_bytes(a: &[u8], b: &[u8]) -> u8 {
20    if a.len() != b.len() {
21        return 0;
22    }
23
24    let mut result = 0u8;
25
26    // Process all bytes regardless of intermediate results
27    for i in 0..a.len() {
28        result |= a[i] ^ b[i];
29    }
30
31    // Prevent compiler optimization
32    compiler_fence(Ordering::SeqCst);
33
34    // Convert to 0 or 1
35    ((!result & (result.wrapping_sub(1))) >> 7) & 1
36}
37
38/// Constant-time conditional select
39///
40/// Returns `a` if `condition` is 1, `b` if `condition` is 0.
41/// Condition must be 0 or 1.
42#[inline(always)]
43pub(crate) fn ct_select_u8(condition: u8, a: u8, b: u8) -> u8 {
44    let mask = condition.wrapping_neg();
45    (a & mask) | (b & !mask)
46}
47
48/// Constant-time conditional select for u32
49#[inline(always)]
50pub(crate) fn ct_select_u32(condition: u8, a: u32, b: u32) -> u32 {
51    let mask = (condition as u32).wrapping_neg();
52    (a & mask) | (b & !mask)
53}
54
55/// Constant-time conditional select for u64
56#[inline(always)]
57pub(crate) fn ct_select_u64(condition: u8, a: u64, b: u64) -> u64 {
58    let mask = (condition as u64).wrapping_neg();
59    (a & mask) | (b & !mask)
60}
61
62/// Constant-time conditional copy
63///
64/// Copies `src` to `dst` if `condition` is 1.
65/// Always reads from `src` and writes to `dst` to maintain constant time.
66#[inline(never)]
67pub(crate) fn ct_copy(dst: &mut [u8], src: &[u8], condition: u8) {
68    assert_eq!(dst.len(), src.len());
69
70    for i in 0..dst.len() {
71        dst[i] = ct_select_u8(condition, src[i], dst[i]);
72    }
73
74    compiler_fence(Ordering::SeqCst);
75}
76
77/// Constant-time array zeroing
78///
79/// Zeros an array in constant time, preventing compiler optimization.
80#[inline(never)]
81pub(crate) fn ct_zero(data: &mut [u8]) {
82    for byte in data.iter_mut() {
83        // SAFETY: `byte` is a valid mutable reference from the `data` slice, so the
84        // pointer derived from it is aligned, non-null, and points to initialized
85        // memory. write_volatile is used intentionally to prevent the compiler
86        // from optimizing away the zeroing of sensitive cryptographic data.
87        unsafe {
88            core::ptr::write_volatile(byte, 0);
89        }
90    }
91
92    compiler_fence(Ordering::SeqCst);
93}
94
95/// Constant-time less-than comparison for u32
96///
97/// Returns 1 if a < b, 0 otherwise
98#[inline(always)]
99pub(crate) fn ct_lt_u32(a: u32, b: u32) -> u8 {
100    let diff = a ^ ((a ^ b) | ((a.wrapping_sub(b)) ^ b));
101    ((diff >> 31) & 1) as u8
102}
103
104/// Constant-time byte array comparison
105///
106/// Returns -1 if a < b, 0 if a == b, 1 if a > b
107#[inline(never)]
108pub(crate) fn ct_cmp_bytes(a: &[u8], b: &[u8]) -> i8 {
109    assert_eq!(a.len(), b.len());
110
111    let mut greater = 0u8;
112    let mut less = 0u8;
113
114    for i in 0..a.len() {
115        let gt = ct_lt_u32(b[i] as u32, a[i] as u32);
116        let lt = ct_lt_u32(a[i] as u32, b[i] as u32);
117
118        greater |= gt & !less & !greater;
119        less |= lt & !greater & !less;
120    }
121
122    compiler_fence(Ordering::SeqCst);
123
124    if greater != 0 {
125        1
126    } else if less != 0 {
127        -1
128    } else {
129        0
130    }
131}
132
133/// Memory barrier to prevent reordering
134#[inline(always)]
135pub(crate) fn memory_barrier() {
136    compiler_fence(Ordering::SeqCst);
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_ct_eq_bytes() {
145        let a = [1u8, 2, 3, 4];
146        let b = [1u8, 2, 3, 4];
147        let c = [1u8, 2, 3, 5];
148
149        assert_eq!(ct_eq_bytes(&a, &b), 1);
150        assert_eq!(ct_eq_bytes(&a, &c), 0);
151    }
152
153    #[test]
154    fn test_ct_select() {
155        assert_eq!(ct_select_u8(1, 0xAA, 0x55), 0xAA);
156        assert_eq!(ct_select_u8(0, 0xAA, 0x55), 0x55);
157
158        assert_eq!(ct_select_u32(1, 0x12345678, 0xABCDEF00), 0x12345678);
159        assert_eq!(ct_select_u32(0, 0x12345678, 0xABCDEF00), 0xABCDEF00);
160    }
161
162    #[test]
163    fn test_ct_copy() {
164        let mut dst = [0u8; 4];
165        let src = [1u8, 2, 3, 4];
166
167        ct_copy(&mut dst, &src, 1);
168        assert_eq!(dst, [1, 2, 3, 4]);
169
170        ct_copy(&mut dst, &[5, 6, 7, 8], 0);
171        assert_eq!(dst, [1, 2, 3, 4]); // Should not change
172    }
173
174    #[test]
175    fn test_ct_cmp() {
176        let a = [1u8, 2, 3];
177        let b = [1u8, 2, 3];
178        let c = [1u8, 2, 4];
179
180        assert_eq!(ct_cmp_bytes(&a, &b), 0);
181        assert_eq!(ct_cmp_bytes(&a, &c), -1);
182        assert_eq!(ct_cmp_bytes(&c, &a), 1);
183    }
184}