1#![warn(missing_debug_implementations)]
9#![cfg_attr(docsrs, feature(doc_cfg))]
10
11pub use aocl_error::{Error, Result};
12use aocl_securerng_sys as sys;
13
14const SECRNG_SUCCESS: i32 = 2;
15const SECRNG_SUPPORTED: i32 = 1;
16const SECRNG_NOT_SUPPORTED: i32 = -1;
17const SECRNG_FAILURE: i32 = -2;
18const SECRNG_INVALID_INPUT: i32 = -3;
19
20pub const DEFAULT_RETRY_COUNT: u32 = 10;
23
24fn check_status(component: &'static str, raw: i32) -> Result<()> {
25 if raw == SECRNG_SUCCESS {
26 return Ok(());
27 }
28 let message = match raw {
29 SECRNG_NOT_SUPPORTED => "instruction not supported on this CPU",
30 SECRNG_FAILURE => "RNG instruction failed after all retries",
31 SECRNG_INVALID_INPUT => "invalid input",
32 _ => "unknown SecureRNG status",
33 }
34 .to_string();
35 Err(Error::Status {
36 component,
37 code: raw as i64,
38 message,
39 })
40}
41
42pub fn is_rdrand_supported() -> bool {
44 unsafe { sys::is_RDRAND_supported() == SECRNG_SUPPORTED }
45}
46
47pub fn is_rdseed_supported() -> bool {
49 unsafe { sys::is_RDSEED_supported() == SECRNG_SUPPORTED }
50}
51
52pub fn rdrand_u16(retry_count: u32) -> Result<u16> {
54 let mut v: u16 = 0;
55 let raw = unsafe { sys::get_rdrand16u(&mut v, retry_count) };
56 check_status("securerng", raw)?;
57 Ok(v)
58}
59
60pub fn rdrand_u32(retry_count: u32) -> Result<u32> {
62 let mut v: u32 = 0;
63 let raw = unsafe { sys::get_rdrand32u(&mut v, retry_count) };
64 check_status("securerng", raw)?;
65 Ok(v)
66}
67
68pub fn rdrand_u64(retry_count: u32) -> Result<u64> {
70 let mut v: u64 = 0;
71 let raw = unsafe { sys::get_rdrand64u(&mut v, retry_count) };
72 check_status("securerng", raw)?;
73 Ok(v)
74}
75
76pub fn rdseed_u16(retry_count: u32) -> Result<u16> {
78 let mut v: u16 = 0;
79 let raw = unsafe { sys::get_rdseed16u(&mut v, retry_count) };
80 check_status("securerng", raw)?;
81 Ok(v)
82}
83
84pub fn rdseed_u32(retry_count: u32) -> Result<u32> {
86 let mut v: u32 = 0;
87 let raw = unsafe { sys::get_rdseed32u(&mut v, retry_count) };
88 check_status("securerng", raw)?;
89 Ok(v)
90}
91
92pub fn rdseed_u64(retry_count: u32) -> Result<u64> {
94 let mut v: u64 = 0;
95 let raw = unsafe { sys::get_rdseed64u(&mut v, retry_count) };
96 check_status("securerng", raw)?;
97 Ok(v)
98}
99
100pub fn rdrand_bytes(out: &mut [u8], retry_count: u32) -> Result<()> {
102 if out.is_empty() {
103 return Ok(());
104 }
105 let n: u32 = out.len().try_into().map_err(|_| {
106 Error::InvalidArgument(format!(
107 "rdrand_bytes: length {} exceeds u32::MAX",
108 out.len()
109 ))
110 })?;
111 let raw = unsafe { sys::get_rdrand_bytes_arr(out.as_mut_ptr(), n, retry_count) };
112 check_status("securerng", raw)
113}
114
115pub fn rdseed_bytes(out: &mut [u8], retry_count: u32) -> Result<()> {
117 if out.is_empty() {
118 return Ok(());
119 }
120 let n: u32 = out.len().try_into().map_err(|_| {
121 Error::InvalidArgument(format!(
122 "rdseed_bytes: length {} exceeds u32::MAX",
123 out.len()
124 ))
125 })?;
126 let raw = unsafe { sys::get_rdseed_bytes_arr(out.as_mut_ptr(), n, retry_count) };
127 check_status("securerng", raw)
128}
129
130pub fn rdrand_u32_array(out: &mut [u32], retry_count: u32) -> Result<()> {
132 if out.is_empty() {
133 return Ok(());
134 }
135 let n: u32 = out.len().try_into().map_err(|_| {
136 Error::InvalidArgument(format!(
137 "rdrand_u32_array: length {} exceeds u32::MAX",
138 out.len()
139 ))
140 })?;
141 let raw = unsafe { sys::get_rdrand32u_arr(out.as_mut_ptr(), n, retry_count) };
142 check_status("securerng", raw)
143}
144
145pub fn rdrand_u64_array(out: &mut [u64], retry_count: u32) -> Result<()> {
147 if out.is_empty() {
148 return Ok(());
149 }
150 let n: u32 = out.len().try_into().map_err(|_| {
151 Error::InvalidArgument(format!(
152 "rdrand_u64_array: length {} exceeds u32::MAX",
153 out.len()
154 ))
155 })?;
156 let raw = unsafe { sys::get_rdrand64u_arr(out.as_mut_ptr(), n, retry_count) };
157 check_status("securerng", raw)
158}
159
160pub fn version() -> Option<String> {
162 unsafe {
163 let p = sys::get_secrngversion();
164 if p.is_null() {
165 return None;
166 }
167 Some(std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned())
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn cpu_support_query_is_consistent() {
177 let _ = is_rdrand_supported();
178 let _ = is_rdseed_supported();
179 }
180
181 #[test]
182 fn rdrand_yields_distinct_values_when_supported() {
183 if !is_rdrand_supported() {
184 eprintln!("RDRAND not supported on this CPU; skipping");
185 return;
186 }
187 let a = rdrand_u64(DEFAULT_RETRY_COUNT).unwrap();
188 let b = rdrand_u64(DEFAULT_RETRY_COUNT).unwrap();
189 assert_ne!(a, b, "RDRAND returned the same u64 twice");
190 }
191
192 #[test]
193 fn rdrand_bytes_fills_buffer() {
194 if !is_rdrand_supported() {
195 return;
196 }
197 let mut buf = [0u8; 32];
198 rdrand_bytes(&mut buf, DEFAULT_RETRY_COUNT).unwrap();
199 assert!(buf.iter().any(|&b| b != 0));
200 }
201
202 #[test]
203 fn empty_inputs_are_ok() {
204 let mut empty: [u8; 0] = [];
205 rdrand_bytes(&mut empty, DEFAULT_RETRY_COUNT).unwrap();
206 let mut empty32: [u32; 0] = [];
207 rdrand_u32_array(&mut empty32, DEFAULT_RETRY_COUNT).unwrap();
208 }
209
210 #[test]
211 fn version_string_is_non_empty() {
212 if let Some(v) = version() {
213 assert!(!v.is_empty());
214 }
215 }
216}