1 | use crate::engine::{general_purpose::STANDARD, DecodeEstimate, Engine}; |
2 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
3 | use alloc::vec::Vec; |
4 | use core::fmt; |
5 | #[cfg (any(feature = "std" , test))] |
6 | use std::error; |
7 | |
8 | /// Errors that can occur while decoding. |
9 | #[derive (Clone, Debug, PartialEq, Eq)] |
10 | pub enum DecodeError { |
11 | /// An invalid byte was found in the input. The offset and offending byte are provided. |
12 | /// Padding characters (`=`) interspersed in the encoded form will be treated as invalid bytes. |
13 | InvalidByte(usize, u8), |
14 | /// The length of the input is invalid. |
15 | /// A typical cause of this is stray trailing whitespace or other separator bytes. |
16 | /// In the case where excess trailing bytes have produced an invalid length *and* the last byte |
17 | /// is also an invalid base64 symbol (as would be the case for whitespace, etc), `InvalidByte` |
18 | /// will be emitted instead of `InvalidLength` to make the issue easier to debug. |
19 | InvalidLength, |
20 | /// The last non-padding input symbol's encoded 6 bits have nonzero bits that will be discarded. |
21 | /// This is indicative of corrupted or truncated Base64. |
22 | /// Unlike `InvalidByte`, which reports symbols that aren't in the alphabet, this error is for |
23 | /// symbols that are in the alphabet but represent nonsensical encodings. |
24 | InvalidLastSymbol(usize, u8), |
25 | /// The nature of the padding was not as configured: absent or incorrect when it must be |
26 | /// canonical, or present when it must be absent, etc. |
27 | InvalidPadding, |
28 | } |
29 | |
30 | impl fmt::Display for DecodeError { |
31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
32 | match *self { |
33 | Self::InvalidByte(index: usize, byte: u8) => write!(f, "Invalid byte {}, offset {}." , byte, index), |
34 | Self::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder." ), |
35 | Self::InvalidLastSymbol(index: usize, byte: u8) => { |
36 | write!(f, "Invalid last symbol {}, offset {}." , byte, index) |
37 | } |
38 | Self::InvalidPadding => write!(f, "Invalid padding" ), |
39 | } |
40 | } |
41 | } |
42 | |
43 | #[cfg (any(feature = "std" , test))] |
44 | impl error::Error for DecodeError { |
45 | fn cause(&self) -> Option<&dyn error::Error> { |
46 | None |
47 | } |
48 | } |
49 | |
50 | /// Errors that can occur while decoding into a slice. |
51 | #[derive (Clone, Debug, PartialEq, Eq)] |
52 | pub enum DecodeSliceError { |
53 | /// A [DecodeError] occurred |
54 | DecodeError(DecodeError), |
55 | /// The provided slice _may_ be too small. |
56 | /// |
57 | /// The check is conservative (assumes the last triplet of output bytes will all be needed). |
58 | OutputSliceTooSmall, |
59 | } |
60 | |
61 | impl fmt::Display for DecodeSliceError { |
62 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
63 | match self { |
64 | Self::DecodeError(e: &DecodeError) => write!(f, "DecodeError: {}" , e), |
65 | Self::OutputSliceTooSmall => write!(f, "Output slice too small" ), |
66 | } |
67 | } |
68 | } |
69 | |
70 | #[cfg (any(feature = "std" , test))] |
71 | impl error::Error for DecodeSliceError { |
72 | fn cause(&self) -> Option<&dyn error::Error> { |
73 | match self { |
74 | DecodeSliceError::DecodeError(e: &DecodeError) => Some(e), |
75 | DecodeSliceError::OutputSliceTooSmall => None, |
76 | } |
77 | } |
78 | } |
79 | |
80 | impl From<DecodeError> for DecodeSliceError { |
81 | fn from(e: DecodeError) -> Self { |
82 | DecodeSliceError::DecodeError(e) |
83 | } |
84 | } |
85 | |
86 | /// Decode base64 using the [`STANDARD` engine](STANDARD). |
87 | /// |
88 | /// See [Engine::decode]. |
89 | #[deprecated (since = "0.21.0" , note = "Use Engine::decode" )] |
90 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
91 | pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> { |
92 | STANDARD.decode(input) |
93 | } |
94 | |
95 | /// Decode from string reference as octets using the specified [Engine]. |
96 | /// |
97 | /// See [Engine::decode]. |
98 | ///Returns a `Result` containing a `Vec<u8>`. |
99 | #[deprecated (since = "0.21.0" , note = "Use Engine::decode" )] |
100 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
101 | pub fn decode_engine<E: Engine, T: AsRef<[u8]>>( |
102 | input: T, |
103 | engine: &E, |
104 | ) -> Result<Vec<u8>, DecodeError> { |
105 | engine.decode(input) |
106 | } |
107 | |
108 | /// Decode from string reference as octets. |
109 | /// |
110 | /// See [Engine::decode_vec]. |
111 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
112 | #[deprecated (since = "0.21.0" , note = "Use Engine::decode_vec" )] |
113 | pub fn decode_engine_vec<E: Engine, T: AsRef<[u8]>>( |
114 | input: T, |
115 | buffer: &mut Vec<u8>, |
116 | engine: &E, |
117 | ) -> Result<(), DecodeError> { |
118 | engine.decode_vec(input, buffer) |
119 | } |
120 | |
121 | /// Decode the input into the provided output slice. |
122 | /// |
123 | /// See [Engine::decode_slice]. |
124 | #[deprecated (since = "0.21.0" , note = "Use Engine::decode_slice" )] |
125 | pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>( |
126 | input: T, |
127 | output: &mut [u8], |
128 | engine: &E, |
129 | ) -> Result<usize, DecodeSliceError> { |
130 | engine.decode_slice(input, output) |
131 | } |
132 | |
133 | /// Returns a conservative estimate of the decoded size of `encoded_len` base64 symbols (rounded up |
134 | /// to the next group of 3 decoded bytes). |
135 | /// |
136 | /// The resulting length will be a safe choice for the size of a decode buffer, but may have up to |
137 | /// 2 trailing bytes that won't end up being needed. |
138 | /// |
139 | /// # Examples |
140 | /// |
141 | /// ``` |
142 | /// use base64::decoded_len_estimate; |
143 | /// |
144 | /// assert_eq!(3, decoded_len_estimate(1)); |
145 | /// assert_eq!(3, decoded_len_estimate(2)); |
146 | /// assert_eq!(3, decoded_len_estimate(3)); |
147 | /// assert_eq!(3, decoded_len_estimate(4)); |
148 | /// // start of the next quad of encoded symbols |
149 | /// assert_eq!(6, decoded_len_estimate(5)); |
150 | /// ``` |
151 | pub fn decoded_len_estimate(encoded_len: usize) -> usize { |
152 | STANDARD |
153 | .internal_decoded_len_estimate(input_len:encoded_len) |
154 | .decoded_len_estimate() |
155 | } |
156 | |
157 | #[cfg (test)] |
158 | mod tests { |
159 | use super::*; |
160 | use crate::{ |
161 | alphabet, |
162 | engine::{general_purpose, Config, GeneralPurpose}, |
163 | tests::{assert_encode_sanity, random_engine}, |
164 | }; |
165 | use rand::{ |
166 | distributions::{Distribution, Uniform}, |
167 | Rng, SeedableRng, |
168 | }; |
169 | |
170 | #[test ] |
171 | fn decode_into_nonempty_vec_doesnt_clobber_existing_prefix() { |
172 | let mut orig_data = Vec::new(); |
173 | let mut encoded_data = String::new(); |
174 | let mut decoded_with_prefix = Vec::new(); |
175 | let mut decoded_without_prefix = Vec::new(); |
176 | let mut prefix = Vec::new(); |
177 | |
178 | let prefix_len_range = Uniform::new(0, 1000); |
179 | let input_len_range = Uniform::new(0, 1000); |
180 | |
181 | let mut rng = rand::rngs::SmallRng::from_entropy(); |
182 | |
183 | for _ in 0..10_000 { |
184 | orig_data.clear(); |
185 | encoded_data.clear(); |
186 | decoded_with_prefix.clear(); |
187 | decoded_without_prefix.clear(); |
188 | prefix.clear(); |
189 | |
190 | let input_len = input_len_range.sample(&mut rng); |
191 | |
192 | for _ in 0..input_len { |
193 | orig_data.push(rng.gen()); |
194 | } |
195 | |
196 | let engine = random_engine(&mut rng); |
197 | engine.encode_string(&orig_data, &mut encoded_data); |
198 | assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len); |
199 | |
200 | let prefix_len = prefix_len_range.sample(&mut rng); |
201 | |
202 | // fill the buf with a prefix |
203 | for _ in 0..prefix_len { |
204 | prefix.push(rng.gen()); |
205 | } |
206 | |
207 | decoded_with_prefix.resize(prefix_len, 0); |
208 | decoded_with_prefix.copy_from_slice(&prefix); |
209 | |
210 | // decode into the non-empty buf |
211 | engine |
212 | .decode_vec(&encoded_data, &mut decoded_with_prefix) |
213 | .unwrap(); |
214 | // also decode into the empty buf |
215 | engine |
216 | .decode_vec(&encoded_data, &mut decoded_without_prefix) |
217 | .unwrap(); |
218 | |
219 | assert_eq!( |
220 | prefix_len + decoded_without_prefix.len(), |
221 | decoded_with_prefix.len() |
222 | ); |
223 | assert_eq!(orig_data, decoded_without_prefix); |
224 | |
225 | // append plain decode onto prefix |
226 | prefix.append(&mut decoded_without_prefix); |
227 | |
228 | assert_eq!(prefix, decoded_with_prefix); |
229 | } |
230 | } |
231 | |
232 | #[test ] |
233 | fn decode_slice_doesnt_clobber_existing_prefix_or_suffix() { |
234 | do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| { |
235 | e.decode_slice(input, output).unwrap() |
236 | }) |
237 | } |
238 | |
239 | #[test ] |
240 | fn decode_slice_unchecked_doesnt_clobber_existing_prefix_or_suffix() { |
241 | do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| { |
242 | e.decode_slice_unchecked(input, output).unwrap() |
243 | }) |
244 | } |
245 | |
246 | #[test ] |
247 | fn decode_engine_estimation_works_for_various_lengths() { |
248 | let engine = GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD); |
249 | for num_prefix_quads in 0..100 { |
250 | for suffix in &["AA" , "AAA" , "AAAA" ] { |
251 | let mut prefix = "AAAA" .repeat(num_prefix_quads); |
252 | prefix.push_str(suffix); |
253 | // make sure no overflow (and thus a panic) occurs |
254 | let res = engine.decode(prefix); |
255 | assert!(res.is_ok()); |
256 | } |
257 | } |
258 | } |
259 | |
260 | #[test ] |
261 | fn decode_slice_output_length_errors() { |
262 | for num_quads in 1..100 { |
263 | let input = "AAAA" .repeat(num_quads); |
264 | let mut vec = vec![0; (num_quads - 1) * 3]; |
265 | assert_eq!( |
266 | DecodeSliceError::OutputSliceTooSmall, |
267 | STANDARD.decode_slice(&input, &mut vec).unwrap_err() |
268 | ); |
269 | vec.push(0); |
270 | assert_eq!( |
271 | DecodeSliceError::OutputSliceTooSmall, |
272 | STANDARD.decode_slice(&input, &mut vec).unwrap_err() |
273 | ); |
274 | vec.push(0); |
275 | assert_eq!( |
276 | DecodeSliceError::OutputSliceTooSmall, |
277 | STANDARD.decode_slice(&input, &mut vec).unwrap_err() |
278 | ); |
279 | vec.push(0); |
280 | // now it works |
281 | assert_eq!( |
282 | num_quads * 3, |
283 | STANDARD.decode_slice(&input, &mut vec).unwrap() |
284 | ); |
285 | } |
286 | } |
287 | |
288 | fn do_decode_slice_doesnt_clobber_existing_prefix_or_suffix< |
289 | F: Fn(&GeneralPurpose, &[u8], &mut [u8]) -> usize, |
290 | >( |
291 | call_decode: F, |
292 | ) { |
293 | let mut orig_data = Vec::new(); |
294 | let mut encoded_data = String::new(); |
295 | let mut decode_buf = Vec::new(); |
296 | let mut decode_buf_copy: Vec<u8> = Vec::new(); |
297 | |
298 | let input_len_range = Uniform::new(0, 1000); |
299 | |
300 | let mut rng = rand::rngs::SmallRng::from_entropy(); |
301 | |
302 | for _ in 0..10_000 { |
303 | orig_data.clear(); |
304 | encoded_data.clear(); |
305 | decode_buf.clear(); |
306 | decode_buf_copy.clear(); |
307 | |
308 | let input_len = input_len_range.sample(&mut rng); |
309 | |
310 | for _ in 0..input_len { |
311 | orig_data.push(rng.gen()); |
312 | } |
313 | |
314 | let engine = random_engine(&mut rng); |
315 | engine.encode_string(&orig_data, &mut encoded_data); |
316 | assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len); |
317 | |
318 | // fill the buffer with random garbage, long enough to have some room before and after |
319 | for _ in 0..5000 { |
320 | decode_buf.push(rng.gen()); |
321 | } |
322 | |
323 | // keep a copy for later comparison |
324 | decode_buf_copy.extend(decode_buf.iter()); |
325 | |
326 | let offset = 1000; |
327 | |
328 | // decode into the non-empty buf |
329 | let decode_bytes_written = |
330 | call_decode(&engine, encoded_data.as_bytes(), &mut decode_buf[offset..]); |
331 | |
332 | assert_eq!(orig_data.len(), decode_bytes_written); |
333 | assert_eq!( |
334 | orig_data, |
335 | &decode_buf[offset..(offset + decode_bytes_written)] |
336 | ); |
337 | assert_eq!(&decode_buf_copy[0..offset], &decode_buf[0..offset]); |
338 | assert_eq!( |
339 | &decode_buf_copy[offset + decode_bytes_written..], |
340 | &decode_buf[offset + decode_bytes_written..] |
341 | ); |
342 | } |
343 | } |
344 | } |
345 | |