Skip to main content

crypto/
bytes.rs

1use constant_time_eq::constant_time_eq;
2
3use crate::{HashError, MAX_HASH_OUTPUT_SIZE};
4
5/// A fixed-capacity, stack-allocated bytes buffer of capacity `N`.
6/// Use [`Self::as_ref`] to get the bytes as a `&[u8]` and [`Self::as_mut`] to get the bytes as a `&mut [u8]`.
7/// Comparing `Bytes` is a constant-time operation.
8#[derive(Copy, Clone)]
9#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
10pub(crate) struct Bytes<const N: usize> {
11    pub(crate) bytes: [u8; N],
12    pub(crate) length: u16,
13}
14
15impl<const N: usize> Bytes<N> {
16    #[inline]
17    pub(crate) fn new() -> Bytes<N> {
18        assert!(N <= u16::MAX as usize);
19        return Bytes {
20            bytes: [0u8; N],
21            length: 0,
22        };
23    }
24
25    #[inline]
26    pub fn len(&self) -> usize {
27        return self.length as usize;
28    }
29
30    #[inline]
31    pub(crate) fn with_length(length: usize) -> Bytes<N> {
32        assert!(N <= u16::MAX as usize && length <= u16::MAX as usize);
33        assert!(length <= N, "length exceeds capacity");
34        return Bytes {
35            bytes: [0u8; N],
36            length: length as u16,
37        };
38    }
39
40    #[inline]
41    pub(crate) fn push(&mut self, byte: u8) {
42        assert!(self.length as usize + 1 <= N);
43        self.bytes[self.length as usize] = byte;
44        self.length += 1;
45    }
46
47    #[inline]
48    pub(crate) fn append(&mut self, data: &[u8]) {
49        assert!(self.length as usize + data.len() <= N);
50        self.bytes[self.length as usize..data.len() + self.length as usize].copy_from_slice(data);
51        self.length += data.len() as u16;
52    }
53}
54
55impl<const N: usize, const L: usize> From<[u8; L]> for Bytes<N> {
56    #[inline]
57    fn from(data: [u8; L]) -> Self {
58        const {
59            assert!(L <= N);
60        }
61
62        let mut bytes = [0u8; N];
63        bytes[..L].copy_from_slice(&data);
64
65        Bytes {
66            bytes,
67            length: L as u16,
68        }
69    }
70}
71
72impl<const N: usize, const L: usize> From<&[u8; L]> for Bytes<N> {
73    #[inline]
74    fn from(data: &[u8; L]) -> Self {
75        const {
76            assert!(L <= N);
77        }
78
79        let mut bytes = [0u8; N];
80        bytes[..L].copy_from_slice(data);
81
82        Bytes {
83            bytes,
84            length: L as u16,
85        }
86    }
87}
88
89impl<const N: usize> PartialEq for Bytes<N> {
90    #[inline]
91    fn eq(&self, other: &Self) -> bool {
92        constant_time_eq(self, other)
93    }
94}
95
96impl<const N: usize> Eq for Bytes<N> {}
97
98impl<const N: usize> core::ops::Deref for Bytes<N> {
99    type Target = [u8];
100    fn deref(&self) -> &[u8] {
101        &self.bytes[..self.length as usize]
102    }
103}
104
105impl<const N: usize> AsMut<[u8]> for Bytes<N> {
106    #[inline]
107    fn as_mut(&mut self) -> &mut [u8] {
108        &mut self.bytes[..self.length as usize]
109    }
110}
111
112/// A stack-allocated bytes buffer.
113/// Use [`Self::as_ref`] to get the bytes as a `&[u8]` and [`Self::as_mut`] to get the bytes as a `&mut [u8]`.
114/// Comparing `Hash` is a constant-time operation.
115#[derive(Copy, Clone)]
116#[repr(transparent)]
117#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
118pub struct Hash(pub(crate) Bytes<MAX_HASH_OUTPUT_SIZE>);
119
120/// implement the required public methods for `Type` to be used as a bytes buffer.
121macro_rules! impl_bytes {
122    ($name:ident($inner:ty)) => {
123        impl $name {
124            #[inline]
125            pub fn len(&self) -> usize {
126                self.0.len()
127            }
128        }
129
130        impl AsRef<[u8]> for $name {
131            #[inline]
132            fn as_ref(&self) -> &[u8] {
133                self.0.as_ref()
134            }
135        }
136
137        impl core::ops::Deref for $name {
138            type Target = [u8];
139            fn deref(&self) -> &[u8] {
140                &self.0
141            }
142        }
143
144        impl AsMut<[u8]> for $name {
145            #[inline]
146            fn as_mut(&mut self) -> &mut [u8] {
147                self.0.as_mut()
148            }
149        }
150
151        impl PartialEq for $name {
152            #[inline]
153            fn eq(&self, other: &Self) -> bool {
154                self.0 == other.0
155            }
156        }
157
158        impl Eq for $name {}
159    };
160}
161
162impl_bytes!(Hash(Bytes<MAX_HASH_OUTPUT_SIZE>));
163
164impl<const L: usize> From<[u8; L]> for Hash {
165    #[inline]
166    fn from(bytes: [u8; L]) -> Self {
167        const {
168            assert!(L <= MAX_HASH_OUTPUT_SIZE);
169        }
170
171        return Hash(Bytes::<MAX_HASH_OUTPUT_SIZE>::from(bytes));
172    }
173}
174
175impl<const L: usize> From<&[u8; L]> for Hash {
176    #[inline]
177    fn from(bytes: &[u8; L]) -> Self {
178        const {
179            assert!(L <= MAX_HASH_OUTPUT_SIZE);
180        }
181
182        return Hash(Bytes::<MAX_HASH_OUTPUT_SIZE>::from(bytes));
183    }
184}
185
186impl TryFrom<&[u8]> for Hash {
187    type Error = HashError;
188
189    #[inline]
190    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
191        if bytes.len() > MAX_HASH_OUTPUT_SIZE {
192            return Err(HashError::TooLong);
193        }
194
195        let mut buffer = Bytes::<MAX_HASH_OUTPUT_SIZE>::new();
196        buffer.append(bytes);
197        return Ok(Hash(buffer));
198    }
199}