/rust/registry/src/index.crates.io-6f17d22bba15001f/base64-0.22.1/src/decode.rs
Line | Count | Source (jump to first uncovered line) |
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 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
34 | 0 | match *self { |
35 | 0 | Self::InvalidByte(index, byte) => { |
36 | 0 | write!(f, "Invalid symbol {}, offset {}.", byte, index) |
37 | | } |
38 | 0 | Self::InvalidLength(len) => write!(f, "Invalid input length: {}", len), |
39 | 0 | Self::InvalidLastSymbol(index, byte) => { |
40 | 0 | write!(f, "Invalid last symbol {}, offset {}.", byte, index) |
41 | | } |
42 | 0 | Self::InvalidPadding => write!(f, "Invalid padding"), |
43 | | } |
44 | 0 | } |
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 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
61 | 0 | match self { |
62 | 0 | Self::DecodeError(e) => write!(f, "DecodeError: {}", e), |
63 | 0 | Self::OutputSliceTooSmall => write!(f, "Output slice too small"), |
64 | | } |
65 | 0 | } |
66 | | } |
67 | | |
68 | | #[cfg(any(feature = "std", test))] |
69 | | impl error::Error for DecodeSliceError { |
70 | 0 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
71 | 0 | match self { |
72 | 0 | DecodeSliceError::DecodeError(e) => Some(e), |
73 | 0 | DecodeSliceError::OutputSliceTooSmall => None, |
74 | | } |
75 | 0 | } |
76 | | } |
77 | | |
78 | | impl From<DecodeError> for DecodeSliceError { |
79 | 23 | fn from(e: DecodeError) -> Self { |
80 | 23 | DecodeSliceError::DecodeError(e) |
81 | 23 | } |
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 | 0 | pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> { |
90 | 0 | STANDARD.decode(input) |
91 | 0 | } |
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 | 0 | pub fn decode_engine<E: Engine, T: AsRef<[u8]>>( |
100 | 0 | input: T, |
101 | 0 | engine: &E, |
102 | 0 | ) -> Result<Vec<u8>, DecodeError> { |
103 | 0 | engine.decode(input) |
104 | 0 | } |
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 | 0 | pub fn decode_engine_vec<E: Engine, T: AsRef<[u8]>>( |
112 | 0 | input: T, |
113 | 0 | buffer: &mut Vec<u8>, |
114 | 0 | engine: &E, |
115 | 0 | ) -> Result<(), DecodeError> { |
116 | 0 | engine.decode_vec(input, buffer) |
117 | 0 | } |
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 | 0 | pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>( |
124 | 0 | input: T, |
125 | 0 | output: &mut [u8], |
126 | 0 | engine: &E, |
127 | 0 | ) -> Result<usize, DecodeSliceError> { |
128 | 0 | engine.decode_slice(input, output) |
129 | 0 | } |
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 | 0 | pub fn decoded_len_estimate(encoded_len: usize) -> usize { |
150 | 0 | STANDARD |
151 | 0 | .internal_decoded_len_estimate(encoded_len) |
152 | 0 | .decoded_len_estimate() |
153 | 0 | } |
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 | | } |