cypheron_core/platform/
mod.rs

1// Copyright 2025 Cypheron Labs, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[cfg(target_os = "windows")]
16pub mod windows;
17
18#[cfg(target_os = "windows")]
19pub mod windows_rand;
20
21#[cfg(target_os = "macos")]
22pub mod macos;
23
24#[cfg(target_os = "linux")]
25pub mod linux;
26
27#[cfg(all(target_os = "linux", feature = "seccomp-bpf"))]
28pub use linux::enable_production_security;
29
30use std::io::Error;
31
32pub fn secure_random_bytes(buffer: &mut [u8]) -> Result<(), Error> {
33    #[cfg(target_os = "windows")]
34    return windows::secure_random_bytes(buffer);
35
36    #[cfg(target_os = "macos")]
37    return macos::secure_random_bytes(buffer);
38
39    #[cfg(target_os = "linux")]
40    return linux::platform::secure_random_bytes(buffer);
41
42    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
43    {
44        use rand::RngCore;
45        let mut rng = rand::thread_rng();
46        rng.fill_bytes(buffer);
47        Ok(())
48    }
49}
50
51pub fn secure_zero(buffer: &mut [u8]) {
52    #[cfg(target_os = "windows")]
53    windows::secure_zero(buffer);
54
55    #[cfg(target_os = "macos")]
56    macos::secure_zero(buffer);
57
58    #[cfg(target_os = "linux")]
59    linux::platform::secure_zero(buffer);
60
61    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
62    {
63        use zeroize::Zeroize;
64        buffer.zeroize();
65    }
66}
67
68pub fn get_platform_info() -> PlatformInfo {
69    PlatformInfo {
70        os: get_os_name(),
71        arch: std::env::consts::ARCH,
72        cpu_features: get_cpu_features(),
73        has_hardware_rng: has_hardware_rng(),
74        has_aes_ni: has_aes_ni(),
75        has_avx2: has_avx2(),
76    }
77}
78
79#[derive(Debug, Clone)]
80pub struct PlatformInfo {
81    pub os: &'static str,
82    pub arch: &'static str,
83    pub cpu_features: Vec<String>,
84    pub has_hardware_rng: bool,
85    pub has_aes_ni: bool,
86    pub has_avx2: bool,
87}
88
89fn get_os_name() -> &'static str {
90    #[cfg(target_os = "windows")]
91    return "Windows";
92
93    #[cfg(target_os = "macos")]
94    return "macOS";
95
96    #[cfg(target_os = "linux")]
97    return "Linux";
98
99    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
100    return "Unknown";
101}
102
103fn get_cpu_features() -> Vec<String> {
104    let mut features = Vec::new();
105
106    #[cfg(target_arch = "x86_64")]
107    {
108        if is_x86_feature_detected!("aes") {
109            features.push("AES-NI".to_string());
110        }
111        if is_x86_feature_detected!("avx2") {
112            features.push("AVX2".to_string());
113        }
114        if is_x86_feature_detected!("rdrand") {
115            features.push("RDRAND".to_string());
116        }
117        if is_x86_feature_detected!("rdseed") {
118            features.push("RDSEED".to_string());
119        }
120    }
121
122    #[cfg(target_arch = "aarch64")]
123    {
124        features.push("ARM64".to_string());
125
126        #[cfg(target_os = "macos")]
127        {
128            features.push("Apple Silicon".to_string());
129            if std::arch::is_aarch64_feature_detected!("aes") {
130                features.push("ARM64-AES".to_string());
131            }
132            if std::arch::is_aarch64_feature_detected!("sha2") {
133                features.push("ARM64-SHA2".to_string());
134            }
135            if std::arch::is_aarch64_feature_detected!("sha3") {
136                features.push("ARM64-SHA3".to_string());
137            }
138        }
139
140        #[cfg(not(target_os = "macos"))]
141        {
142            if std::arch::is_aarch64_feature_detected!("aes") {
143                features.push("ARM64-AES".to_string());
144            }
145            if std::arch::is_aarch64_feature_detected!("sha2") {
146                features.push("ARM64-SHA2".to_string());
147            }
148            if std::arch::is_aarch64_feature_detected!("neon") {
149                features.push("ARM64-NEON".to_string());
150            }
151        }
152    }
153
154    features
155}
156
157#[allow(clippy::nonminimal_bool)]
158fn has_hardware_rng() -> bool {
159    #[cfg(target_arch = "x86_64")]
160    {
161        is_x86_feature_detected!("rdrand") || is_x86_feature_detected!("rdseed")
162    }
163
164    #[cfg(target_arch = "aarch64")]
165    {
166        std::arch::is_aarch64_feature_detected!("rand")
167    }
168
169    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
170    {
171        false
172    }
173}
174
175fn has_aes_ni() -> bool {
176    #[cfg(target_arch = "x86_64")]
177    return is_x86_feature_detected!("aes");
178
179    #[cfg(target_arch = "aarch64")]
180    return std::arch::is_aarch64_feature_detected!("aes");
181
182    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
183    return false;
184}
185
186fn has_avx2() -> bool {
187    #[cfg(target_arch = "x86_64")]
188    return is_x86_feature_detected!("avx2");
189
190    #[cfg(not(target_arch = "x86_64"))]
191    return false;
192}