byte_array_ops/security/
vec.rs

1//! Vec-like operations for ByteArray
2//!
3//! Provides explicit access to length, emptiness, and indexed access
4
5use crate::model::ByteArray;
6use zeroize::Zeroize;
7
8impl ByteArray {
9    /// Returns the number of bytes in the array
10    pub fn len(&self) -> usize {
11        self.bytes.len()
12    }
13
14    /// Returns true if there are no bytes in the array
15    pub fn is_empty(&self) -> bool {
16        self.bytes.is_empty()
17    }
18
19    /// Returns a reference to the byte at the given index, or None if out of bounds
20    pub fn get(&self, index: usize) -> Option<&u8> {
21        self.bytes.get(index)
22    }
23
24    /// Truncate function similar to [`alloc::vec::Vec::truncate`] but zeroizes the discarded
25    /// portions before truncating to avoid leaking sensitive data. the `len` represents the
26    /// new [`ByteArray`] length in bytes.
27    #[inline]
28    pub fn truncate(&mut self, len: usize) {
29        if len >= self.len() {
30            return;
31        }
32        // we zeroize the rest of the bytes before truncating
33        self.bytes[len..].zeroize();
34
35        self.bytes.truncate(len);
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::try_hex;
43    use alloc::vec;
44    use core::ops::Range;
45
46    fn util_validate_zero(ptr: *const u8, bounds: Range<usize>) {
47        #[allow(unsafe_code)]
48        unsafe {
49            for i in bounds {
50                assert_eq!(*ptr.add(i), 0u8, "Failed zero validation at index {i}");
51            }
52        }
53    }
54    #[test]
55    fn test_len() {
56        let arr = ByteArray::from_hex("ffab12345bc").expect("error converting");
57
58        assert_eq!(arr.len(), 6);
59    }
60
61    #[test]
62    fn test_is_empty() {
63        let arr = ByteArray::default();
64        assert_eq!(arr.is_empty(), true);
65
66        let arr = ByteArray::from_hex("bb").expect("error converting");
67
68        assert_eq!(arr.is_empty(), false);
69    }
70
71    #[test]
72    fn test_get() {
73        let arr: ByteArray = vec![0xaa, 0xbb, 0xcc].into();
74
75        assert_eq!(arr.get(0), Some(&0xaa));
76        assert_eq!(arr.get(1), Some(&0xbb));
77        assert_eq!(arr.get(2), Some(&0xcc));
78        assert_eq!(arr.get(3), None);
79    }
80
81    /// function to avoid repetive code for truncate tests
82    /// - `len`: if None then uses the bytes length else uses the passed length
83    /// - `expected`: if None uses the same byte array else uses the passed array
84    fn bootstrap_truncate(len: Option<usize>, expected: Option<ByteArray>) {
85        let mut bytes = try_hex!("deadbeefdeadbeefdeadbeef").unwrap();
86        let exp_b = bytes.clone();
87
88        bytes.truncate(len.unwrap_or(bytes.len()));
89
90        if let Some(start) = len {
91            util_validate_zero(bytes.bytes.as_ptr(), start..bytes.len());
92        }
93
94        assert_eq!(bytes, expected.unwrap_or(exp_b));
95    }
96    #[test]
97    fn test_truncate_zero_length() {
98        bootstrap_truncate(Some(0), Some(ByteArray::default()));
99    }
100
101    #[test]
102    fn test_truncate_equal_length() {
103        bootstrap_truncate(None, None);
104    }
105
106    #[test]
107    fn test_truncate_normal_length() {
108        let expected = try_hex!("deadbe").unwrap();
109        bootstrap_truncate(Some(3), Some(expected));
110    }
111}