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