1use native_ossl_sys as sys;
4
5#[must_use]
9pub fn hex_encode<T: AsRef<[u8]>>(b: T) -> String {
10 b.as_ref().iter().fold(String::new(), |mut s, x| {
11 use std::fmt::Write;
12 write!(s, "{x:02x}").unwrap();
13 s
14 })
15}
16
17#[must_use]
23pub fn hex_decode(s: &str) -> Vec<u8> {
24 (0..s.len())
25 .step_by(2)
26 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
27 .collect()
28}
29
30#[must_use]
43pub fn ct_eq(a: &[u8], b: &[u8]) -> bool {
44 if a.len() != b.len() {
45 return false;
46 }
47 if a.is_empty() {
48 return true;
49 }
50 let rc = unsafe {
53 sys::CRYPTO_memcmp(
54 a.as_ptr().cast::<std::ffi::c_void>(),
55 b.as_ptr().cast::<std::ffi::c_void>(),
56 a.len(),
57 )
58 };
59 rc == 0
60}
61
62pub struct SecretBuf {
81 data: Vec<u8>,
82}
83
84impl SecretBuf {
85 #[must_use]
88 pub fn new(data: Vec<u8>) -> Self {
89 SecretBuf { data }
90 }
91
92 #[must_use]
94 pub fn with_len(len: usize) -> Self {
95 SecretBuf {
96 data: vec![0u8; len],
97 }
98 }
99
100 #[must_use]
102 pub fn from_slice(data: &[u8]) -> Self {
103 SecretBuf {
104 data: data.to_vec(),
105 }
106 }
107
108 #[must_use]
110 pub fn len(&self) -> usize {
111 self.data.len()
112 }
113
114 #[must_use]
116 pub fn is_empty(&self) -> bool {
117 self.data.is_empty()
118 }
119
120 pub fn as_mut_slice(&mut self) -> &mut [u8] {
124 &mut self.data
125 }
126}
127
128impl AsRef<[u8]> for SecretBuf {
129 fn as_ref(&self) -> &[u8] {
130 &self.data
131 }
132}
133
134impl Drop for SecretBuf {
135 fn drop(&mut self) {
136 if !self.data.is_empty() {
137 unsafe {
138 sys::OPENSSL_cleanse(
139 self.data.as_mut_ptr().cast::<std::ffi::c_void>(),
140 self.data.len(),
141 );
142 }
143 }
144 }
145}
146
147unsafe impl Send for SecretBuf {}
149unsafe impl Sync for SecretBuf {}
150
151#[cfg(test)]
152mod tests {
153 use super::{ct_eq, SecretBuf};
154
155 #[test]
156 fn ct_eq_equal_slices() {
157 assert!(ct_eq(b"hello", b"hello"));
158 }
159
160 #[test]
161 fn ct_eq_different_values() {
162 assert!(!ct_eq(b"hello", b"world"));
163 }
164
165 #[test]
166 fn ct_eq_different_lengths() {
167 assert!(!ct_eq(b"hi", b"hii"));
168 }
169
170 #[test]
171 fn ct_eq_empty_slices() {
172 assert!(ct_eq(b"", b""));
173 }
174
175 #[test]
176 fn ct_eq_one_byte_differ() {
177 assert!(!ct_eq(&[0u8; 32], &{
178 let mut v = [0u8; 32];
179 v[31] = 1;
180 v
181 }));
182 }
183
184 #[test]
185 fn with_len_creates_correct_size() {
186 let buf = SecretBuf::with_len(32);
187 assert_eq!(buf.len(), 32);
188 assert!(!buf.is_empty());
189 }
190
191 #[test]
192 fn from_slice_copies_data() {
193 let src = b"secret key material";
194 let buf = SecretBuf::from_slice(src);
195 assert_eq!(buf.as_ref(), src);
196 }
197
198 #[test]
199 fn new_wraps_ownership() {
200 let v = vec![1u8, 2, 3];
201 let buf = SecretBuf::new(v);
202 assert_eq!(buf.as_ref(), &[1, 2, 3]);
203 }
204
205 #[test]
206 fn empty_buf_is_empty() {
207 let buf = SecretBuf::new(vec![]);
208 assert!(buf.is_empty());
209 }
211
212 #[test]
213 fn as_mut_slice_writes_through() {
214 let mut buf = SecretBuf::with_len(4);
215 buf.as_mut_slice().copy_from_slice(&[10, 20, 30, 40]);
216 assert_eq!(buf.as_ref(), &[10, 20, 30, 40]);
217 }
218}