1use zeroize::Zeroize;
7
8pub fn to_hex(data: &[u8]) -> String {
19 hex::encode(data)
20}
21
22pub fn to_hex_upper(data: &[u8]) -> String {
24 hex::encode_upper(data)
25}
26
27pub fn from_hex(s: &str) -> Result<Vec<u8>, hex::FromHexError> {
38 hex::decode(s)
39}
40
41pub fn to_base64(data: &[u8]) -> String {
52 use base64::{Engine, engine::general_purpose::STANDARD};
53 STANDARD.encode(data)
54}
55
56pub fn from_base64(s: &str) -> Result<Vec<u8>, base64::DecodeError> {
67 use base64::{Engine, engine::general_purpose::STANDARD};
68 STANDARD.decode(s)
69}
70
71pub fn to_base64url(data: &[u8]) -> String {
73 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
74 URL_SAFE_NO_PAD.encode(data)
75}
76
77pub fn from_base64url(s: &str) -> Result<Vec<u8>, base64::DecodeError> {
79 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
80 URL_SAFE_NO_PAD.decode(s)
81}
82
83pub fn secure_zero(data: &mut [u8]) {
98 data.zeroize();
99}
100
101pub fn secure_clear(data: &mut Vec<u8>) {
103 data.zeroize();
104 data.clear();
105}
106
107pub fn secure_compare(a: &[u8], b: &[u8]) -> bool {
122 if a.len() != b.len() {
123 return false;
124 }
125
126 let mut result = 0u8;
127 for (x, y) in a.iter().zip(b.iter()) {
128 result |= x ^ y;
129 }
130 result == 0
131}
132
133pub fn concat(slices: &[&[u8]]) -> Vec<u8> {
144 let total_len: usize = slices.iter().map(|s| s.len()).sum();
145 let mut result = Vec::with_capacity(total_len);
146 for slice in slices {
147 result.extend_from_slice(slice);
148 }
149 result
150}
151
152pub fn xor_bytes(a: &[u8], b: &[u8]) -> Vec<u8> {
169 assert_eq!(a.len(), b.len(), "Byte slices must have equal length");
170 a.iter().zip(b.iter()).map(|(x, y)| x ^ y).collect()
171}
172
173pub fn try_xor_bytes(a: &[u8], b: &[u8]) -> Result<Vec<u8>, &'static str> {
175 if a.len() != b.len() {
176 return Err("Byte slices must have equal length");
177 }
178 Ok(xor_bytes(a, b))
179}
180
181pub fn pad_pkcs7(data: &[u8], block_size: usize) -> Vec<u8> {
198 let padding_len = block_size - (data.len() % block_size);
199 let mut result = Vec::with_capacity(data.len() + padding_len);
200 result.extend_from_slice(data);
201 result.extend(std::iter::repeat(padding_len as u8).take(padding_len));
202 result
203}
204
205pub fn unpad_pkcs7(data: &[u8]) -> Result<Vec<u8>, &'static str> {
221 if data.is_empty() {
222 return Err("Data is empty");
223 }
224
225 let padding_len = *data.last().unwrap() as usize;
226 if padding_len == 0 || padding_len > data.len() {
227 return Err("Invalid padding length");
228 }
229
230 for &byte in &data[data.len() - padding_len..] {
232 if byte as usize != padding_len {
233 return Err("Invalid padding bytes");
234 }
235 }
236
237 Ok(data[..data.len() - padding_len].to_vec())
238}
239
240pub fn int_to_bytes_be(value: u64, length: usize) -> Vec<u8> {
242 let bytes = value.to_be_bytes();
243 if length >= 8 {
244 let mut result = vec![0u8; length - 8];
245 result.extend_from_slice(&bytes);
246 result
247 } else {
248 bytes[8 - length..].to_vec()
249 }
250}
251
252pub fn int_to_bytes_le(value: u64, length: usize) -> Vec<u8> {
254 let bytes = value.to_le_bytes();
255 let mut result = bytes[..length.min(8)].to_vec();
256 result.resize(length, 0);
257 result
258}
259
260pub fn bytes_to_int_be(data: &[u8]) -> u64 {
262 let mut bytes = [0u8; 8];
263 let start = 8usize.saturating_sub(data.len());
264 let copy_len = data.len().min(8);
265 bytes[start..start + copy_len].copy_from_slice(&data[..copy_len]);
266 u64::from_be_bytes(bytes)
267}
268
269pub fn bytes_to_int_le(data: &[u8]) -> u64 {
271 let mut bytes = [0u8; 8];
272 let copy_len = data.len().min(8);
273 bytes[..copy_len].copy_from_slice(&data[..copy_len]);
274 u64::from_le_bytes(bytes)
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_hex_roundtrip() {
283 let data = vec![0xde, 0xad, 0xbe, 0xef];
284 let hex = to_hex(&data);
285 assert_eq!(hex, "deadbeef");
286 let decoded = from_hex(&hex).unwrap();
287 assert_eq!(decoded, data);
288 }
289
290 #[test]
291 fn test_base64_roundtrip() {
292 let data = b"Hello, World!";
293 let b64 = to_base64(data);
294 let decoded = from_base64(&b64).unwrap();
295 assert_eq!(decoded, data);
296 }
297
298 #[test]
299 fn test_base64url_roundtrip() {
300 let data = vec![0xff, 0xfe, 0xfd];
301 let b64 = to_base64url(&data);
302 assert!(!b64.contains('+'));
303 assert!(!b64.contains('/'));
304 let decoded = from_base64url(&b64).unwrap();
305 assert_eq!(decoded, data);
306 }
307
308 #[test]
309 fn test_secure_zero() {
310 let mut data = vec![0x42u8; 32];
311 secure_zero(&mut data);
312 assert!(data.iter().all(|&b| b == 0));
313 }
314
315 #[test]
316 fn test_secure_compare() {
317 assert!(secure_compare(b"hello", b"hello"));
318 assert!(!secure_compare(b"hello", b"world"));
319 assert!(!secure_compare(b"hello", b"helloworld"));
320 }
321
322 #[test]
323 fn test_concat() {
324 let result = concat(&[b"hello", b" ", b"world"]);
325 assert_eq!(result, b"hello world");
326 }
327
328 #[test]
329 fn test_xor_bytes() {
330 let a = [0x00, 0xFF, 0x00, 0xFF];
331 let b = [0xFF, 0x00, 0xFF, 0x00];
332 let result = xor_bytes(&a, &b);
333 assert_eq!(result, vec![0xFF, 0xFF, 0xFF, 0xFF]);
334 }
335
336 #[test]
337 fn test_pkcs7_padding() {
338 let padded = pad_pkcs7(b"hello", 16);
339 assert_eq!(padded.len(), 16);
340 assert_eq!(padded[5..], vec![11u8; 11]);
341
342 let unpadded = unpad_pkcs7(&padded).unwrap();
343 assert_eq!(unpadded, b"hello");
344 }
345
346 #[test]
347 fn test_int_bytes_conversion() {
348 let value = 0x0102u64;
349 let bytes = int_to_bytes_be(value, 2);
350 assert_eq!(bytes, vec![0x01, 0x02]);
351 assert_eq!(bytes_to_int_be(&bytes), value);
352
353 let bytes_le = int_to_bytes_le(value, 2);
354 assert_eq!(bytes_le, vec![0x02, 0x01]);
355 assert_eq!(bytes_to_int_le(&bytes_le), value);
356 }
357}