/rust/registry/src/index.crates.io-1949cf8c6b5b557f/base64-0.23.1/src/decode.rs
Line | Count | Source |
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, 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 | | /// |
27 | | /// See [`crate::engine::GeneralPurposeConfig::with_decode_allow_trailing_bits`] to control |
28 | | /// whether to detect this encoding error and produce this variant. |
29 | | InvalidLastSymbol { |
30 | | /// Offset in the input |
31 | | offset: usize, |
32 | | /// The offending symbol |
33 | | symbol: u8, |
34 | | /// The bits the symbol corresponds to. |
35 | | /// |
36 | | /// Since this error is being reported, this value has high bits erroneously set. |
37 | | /// For a 2-symbol suffix, only the first 2 bits may be set (6 + 2 = 8 bits, |
38 | | /// 1 byte), and for a 3 symbol, only the first 4 (6 + 6 + 4 = 16, 2 bytes). |
39 | | symbol_value: u8, |
40 | | }, |
41 | | /// The nature of the padding was not as configured: absent or incorrect when it must be |
42 | | /// canonical, or present when it must be absent, etc. |
43 | | InvalidPadding, |
44 | | } |
45 | | |
46 | | impl fmt::Display for DecodeError { |
47 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
48 | 0 | match *self { |
49 | 0 | Self::InvalidByte(index, byte) => { |
50 | 0 | write!(f, "Invalid symbol {}, offset {}.", byte, index) |
51 | | } |
52 | 0 | Self::InvalidLength(len) => write!(f, "Invalid input length: {}", len), |
53 | | Self::InvalidLastSymbol { |
54 | 0 | offset, |
55 | 0 | symbol, |
56 | 0 | symbol_value, |
57 | | } => { |
58 | 0 | write!( |
59 | 0 | f, |
60 | | "Invalid last symbol {:#4x} ('{}') at offset {}, decoded as {:#010b}.", |
61 | | symbol, |
62 | | // To have been decoded at all, it must have been ascii, but rather than have a |
63 | | // panicking code path, replacement char seems reasonable. |
64 | | // Can't use `char::from_u32` as that's 1.52+, so we make a 1-byte str. |
65 | 0 | core::str::from_utf8(&[symbol]) |
66 | 0 | .ok() |
67 | 0 | .and_then(|s| s.chars().next()) |
68 | | // associated const is also 1.52+ |
69 | 0 | .unwrap_or(core::char::REPLACEMENT_CHARACTER), |
70 | | offset, |
71 | | symbol_value |
72 | | ) |
73 | | } |
74 | 0 | Self::InvalidPadding => write!(f, "Invalid padding"), |
75 | | } |
76 | 0 | } |
77 | | } |
78 | | |
79 | | impl fmt::Debug for DecodeError { |
80 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
81 | | // 1.48.0 can't handle {self} |
82 | 0 | write!(f, "{}", self) |
83 | 0 | } |
84 | | } |
85 | | |
86 | | #[cfg(any(feature = "std", test))] |
87 | | impl error::Error for DecodeError {} |
88 | | |
89 | | /// Errors that can occur while decoding into a slice. |
90 | | #[derive(Clone, Debug, PartialEq, Eq)] |
91 | | pub enum DecodeSliceError { |
92 | | /// A [`DecodeError`] occurred |
93 | | DecodeError(DecodeError), |
94 | | /// The provided slice is too small. |
95 | | OutputSliceTooSmall, |
96 | | } |
97 | | |
98 | | impl fmt::Display for DecodeSliceError { |
99 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
100 | 0 | match self { |
101 | 0 | Self::DecodeError(e) => write!(f, "DecodeError: {}", e), |
102 | 0 | Self::OutputSliceTooSmall => write!(f, "Output slice too small"), |
103 | | } |
104 | 0 | } |
105 | | } |
106 | | |
107 | | #[cfg(any(feature = "std", test))] |
108 | | impl error::Error for DecodeSliceError { |
109 | 0 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
110 | 0 | match self { |
111 | 0 | DecodeSliceError::DecodeError(e) => Some(e), |
112 | 0 | DecodeSliceError::OutputSliceTooSmall => None, |
113 | | } |
114 | 0 | } |
115 | | } |
116 | | |
117 | | impl From<DecodeError> for DecodeSliceError { |
118 | 0 | fn from(e: DecodeError) -> Self { |
119 | 0 | DecodeSliceError::DecodeError(e) |
120 | 0 | } |
121 | | } |
122 | | |
123 | | /// Decode base64 using the [`STANDARD` engine](STANDARD). |
124 | | /// |
125 | | /// See [`Engine::decode`]. |
126 | | #[deprecated(since = "0.21.0", note = "Use Engine::decode")] |
127 | | #[cfg(any(feature = "alloc", test))] |
128 | 0 | pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> { |
129 | 0 | STANDARD.decode(input) |
130 | 0 | } |
131 | | |
132 | | /// Decode from string reference as octets using the specified [Engine]. |
133 | | /// |
134 | | /// See [`Engine::decode`]. |
135 | | ///Returns a `Result` containing a `Vec<u8>`. |
136 | | #[deprecated(since = "0.21.0", note = "Use Engine::decode")] |
137 | | #[cfg(any(feature = "alloc", test))] |
138 | 0 | pub fn decode_engine<E: Engine, T: AsRef<[u8]>>( |
139 | 0 | input: T, |
140 | 0 | engine: &E, |
141 | 0 | ) -> Result<Vec<u8>, DecodeError> { |
142 | 0 | engine.decode(input) |
143 | 0 | } |
144 | | |
145 | | /// Decode from string reference as octets. |
146 | | /// |
147 | | /// See [`Engine::decode_vec`]. |
148 | | #[cfg(any(feature = "alloc", test))] |
149 | | #[deprecated(since = "0.21.0", note = "Use Engine::decode_vec")] |
150 | 0 | pub fn decode_engine_vec<E: Engine, T: AsRef<[u8]>>( |
151 | 0 | input: T, |
152 | 0 | buffer: &mut Vec<u8>, |
153 | 0 | engine: &E, |
154 | 0 | ) -> Result<(), DecodeError> { |
155 | 0 | engine.decode_vec(input, buffer) |
156 | 0 | } |
157 | | |
158 | | /// Decode the input into the provided output slice. |
159 | | /// |
160 | | /// See [`Engine::decode_slice`]. |
161 | | #[deprecated(since = "0.21.0", note = "Use Engine::decode_slice")] |
162 | 0 | pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>( |
163 | 0 | input: T, |
164 | 0 | output: &mut [u8], |
165 | 0 | engine: &E, |
166 | 0 | ) -> Result<usize, DecodeSliceError> { |
167 | 0 | engine.decode_slice(input, output) |
168 | 0 | } |
169 | | |
170 | | /// Returns a conservative estimate of the decoded size of `encoded_len` base64 symbols (rounded up |
171 | | /// to the next group of 3 decoded bytes). |
172 | | /// |
173 | | /// The resulting length will be a safe choice for the size of a decode buffer, but may have up to |
174 | | /// 2 trailing bytes that won't end up being needed. |
175 | | /// |
176 | | /// # Examples |
177 | | /// |
178 | | /// ``` |
179 | | /// use base64::decoded_len_estimate; |
180 | | /// |
181 | | /// assert_eq!(3, decoded_len_estimate(1)); |
182 | | /// assert_eq!(3, decoded_len_estimate(2)); |
183 | | /// assert_eq!(3, decoded_len_estimate(3)); |
184 | | /// assert_eq!(3, decoded_len_estimate(4)); |
185 | | /// // start of the next quad of encoded symbols |
186 | | /// assert_eq!(6, decoded_len_estimate(5)); |
187 | | /// ``` |
188 | | #[must_use] |
189 | 0 | pub fn decoded_len_estimate(encoded_len: usize) -> usize { |
190 | 0 | STANDARD |
191 | 0 | .internal_decoded_len_estimate(encoded_len) |
192 | 0 | .decoded_len_estimate() |
193 | 0 | } |
194 | | |
195 | | #[cfg(test)] |
196 | | mod tests { |
197 | | use super::*; |
198 | | use crate::{ |
199 | | alphabet, |
200 | | engine::{general_purpose, GeneralPurpose}, |
201 | | tests::{assert_encode_sanity, random_engine}, |
202 | | }; |
203 | | use rand::distr::{Distribution, Uniform}; |
204 | | use rand::{rngs, RngExt}; |
205 | | |
206 | | #[test] |
207 | | fn decode_into_nonempty_vec_doesnt_clobber_existing_prefix() { |
208 | | let mut orig_data = Vec::new(); |
209 | | let mut encoded_data = String::new(); |
210 | | let mut decoded_with_prefix = Vec::new(); |
211 | | let mut decoded_without_prefix = Vec::new(); |
212 | | let mut prefix = Vec::new(); |
213 | | |
214 | | let prefix_len_range = Uniform::new(0, 1000).unwrap(); |
215 | | let input_len_range = Uniform::new(0, 1000).unwrap(); |
216 | | |
217 | | let mut rng = rand::make_rng::<rngs::SmallRng>(); |
218 | | |
219 | | for _ in 0..10_000 { |
220 | | orig_data.clear(); |
221 | | encoded_data.clear(); |
222 | | decoded_with_prefix.clear(); |
223 | | decoded_without_prefix.clear(); |
224 | | prefix.clear(); |
225 | | |
226 | | let input_len = input_len_range.sample(&mut rng); |
227 | | |
228 | | for _ in 0..input_len { |
229 | | orig_data.push(rng.random()); |
230 | | } |
231 | | |
232 | | let engine = random_engine(&mut rng); |
233 | | engine.encode_string(&orig_data, &mut encoded_data); |
234 | | assert_encode_sanity(&encoded_data, &engine, input_len); |
235 | | |
236 | | let prefix_len = prefix_len_range.sample(&mut rng); |
237 | | |
238 | | // fill the buf with a prefix |
239 | | for _ in 0..prefix_len { |
240 | | prefix.push(rng.random()); |
241 | | } |
242 | | |
243 | | decoded_with_prefix.resize(prefix_len, 0); |
244 | | decoded_with_prefix.copy_from_slice(&prefix); |
245 | | |
246 | | // decode into the non-empty buf |
247 | | engine |
248 | | .decode_vec(&encoded_data, &mut decoded_with_prefix) |
249 | | .unwrap(); |
250 | | // also decode into the empty buf |
251 | | engine |
252 | | .decode_vec(&encoded_data, &mut decoded_without_prefix) |
253 | | .unwrap(); |
254 | | |
255 | | assert_eq!( |
256 | | prefix_len + decoded_without_prefix.len(), |
257 | | decoded_with_prefix.len() |
258 | | ); |
259 | | assert_eq!(orig_data, decoded_without_prefix); |
260 | | |
261 | | // append plain decode onto prefix |
262 | | prefix.append(&mut decoded_without_prefix); |
263 | | |
264 | | assert_eq!(prefix, decoded_with_prefix); |
265 | | } |
266 | | } |
267 | | |
268 | | #[test] |
269 | | fn decode_slice_doesnt_clobber_existing_prefix_or_suffix() { |
270 | | do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| { |
271 | | e.decode_slice(input, output).unwrap() |
272 | | }) |
273 | | } |
274 | | |
275 | | #[test] |
276 | | fn decode_slice_unchecked_doesnt_clobber_existing_prefix_or_suffix() { |
277 | | do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| { |
278 | | e.decode_slice_unchecked(input, output).unwrap() |
279 | | }) |
280 | | } |
281 | | |
282 | | #[test] |
283 | | fn decode_engine_estimation_works_for_various_lengths() { |
284 | | let engine = GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD); |
285 | | for num_prefix_quads in 0..100 { |
286 | | for suffix in &["AA", "AAA", "AAAA"] { |
287 | | let mut prefix = "AAAA".repeat(num_prefix_quads); |
288 | | prefix.push_str(suffix); |
289 | | // make sure no overflow (and thus a panic) occurs |
290 | | let res = engine.decode(prefix); |
291 | | assert!(res.is_ok()); |
292 | | } |
293 | | } |
294 | | } |
295 | | |
296 | | #[test] |
297 | | fn decode_slice_output_length_errors() { |
298 | | for num_quads in 1..100 { |
299 | | let input = "AAAA".repeat(num_quads); |
300 | | let mut vec = vec![0; (num_quads - 1) * 3]; |
301 | | assert_eq!( |
302 | | DecodeSliceError::OutputSliceTooSmall, |
303 | | STANDARD.decode_slice(&input, &mut vec).unwrap_err() |
304 | | ); |
305 | | vec.push(0); |
306 | | assert_eq!( |
307 | | DecodeSliceError::OutputSliceTooSmall, |
308 | | STANDARD.decode_slice(&input, &mut vec).unwrap_err() |
309 | | ); |
310 | | vec.push(0); |
311 | | assert_eq!( |
312 | | DecodeSliceError::OutputSliceTooSmall, |
313 | | STANDARD.decode_slice(&input, &mut vec).unwrap_err() |
314 | | ); |
315 | | vec.push(0); |
316 | | // now it works |
317 | | assert_eq!( |
318 | | num_quads * 3, |
319 | | STANDARD.decode_slice(&input, &mut vec).unwrap() |
320 | | ); |
321 | | } |
322 | | } |
323 | | |
324 | | #[test] |
325 | | fn invalid_last_symbol_debug() { |
326 | | let err = DecodeError::InvalidLastSymbol { |
327 | | offset: 100, |
328 | | symbol: b'W', |
329 | | symbol_value: 0x16, |
330 | | }; |
331 | | |
332 | | assert_eq!( |
333 | | "Invalid last symbol 0x57 ('W') at offset 100, decoded as 0b00010110.", |
334 | | format!("{:?}", err) |
335 | | ); |
336 | | } |
337 | | |
338 | | fn do_decode_slice_doesnt_clobber_existing_prefix_or_suffix< |
339 | | F: Fn(&GeneralPurpose, &[u8], &mut [u8]) -> usize, |
340 | | >( |
341 | | call_decode: F, |
342 | | ) { |
343 | | let mut orig_data = Vec::new(); |
344 | | let mut encoded_data = String::new(); |
345 | | let mut decode_buf = Vec::new(); |
346 | | let mut decode_buf_copy: Vec<u8> = Vec::new(); |
347 | | |
348 | | let input_len_range = Uniform::new(0, 1000).unwrap(); |
349 | | |
350 | | let mut rng = rand::make_rng::<rngs::SmallRng>(); |
351 | | |
352 | | for _ in 0..10_000 { |
353 | | orig_data.clear(); |
354 | | encoded_data.clear(); |
355 | | decode_buf.clear(); |
356 | | decode_buf_copy.clear(); |
357 | | |
358 | | let input_len = input_len_range.sample(&mut rng); |
359 | | |
360 | | for _ in 0..input_len { |
361 | | orig_data.push(rng.random()); |
362 | | } |
363 | | |
364 | | let engine = random_engine(&mut rng); |
365 | | engine.encode_string(&orig_data, &mut encoded_data); |
366 | | assert_encode_sanity(&encoded_data, &engine, input_len); |
367 | | |
368 | | // fill the buffer with random garbage, long enough to have some room before and after |
369 | | for _ in 0..5000 { |
370 | | decode_buf.push(rng.random()); |
371 | | } |
372 | | |
373 | | // keep a copy for later comparison |
374 | | decode_buf_copy.extend(decode_buf.iter()); |
375 | | |
376 | | let offset = 1000; |
377 | | |
378 | | // decode into the non-empty buf |
379 | | let decode_bytes_written = |
380 | | call_decode(&engine, encoded_data.as_bytes(), &mut decode_buf[offset..]); |
381 | | |
382 | | assert_eq!(orig_data.len(), decode_bytes_written); |
383 | | assert_eq!( |
384 | | orig_data, |
385 | | &decode_buf[offset..(offset + decode_bytes_written)] |
386 | | ); |
387 | | assert_eq!(&decode_buf_copy[0..offset], &decode_buf[0..offset]); |
388 | | assert_eq!( |
389 | | &decode_buf_copy[offset + decode_bytes_written..], |
390 | | &decode_buf[offset + decode_bytes_written..] |
391 | | ); |
392 | | } |
393 | | } |
394 | | } |
395 | | |
396 | | #[allow(deprecated)] |
397 | | #[cfg(test)] |
398 | | mod coverage_gaming { |
399 | | use super::*; |
400 | | use std::error::Error; |
401 | | |
402 | | #[test] |
403 | | fn decode_error() { |
404 | | let _ = format!("{:?}", DecodeError::InvalidPadding.clone()); |
405 | | let _ = format!( |
406 | | "{} {} {} {}", |
407 | | DecodeError::InvalidByte(0, 0), |
408 | | DecodeError::InvalidLength(0), |
409 | | DecodeError::InvalidLastSymbol { |
410 | | offset: 0, |
411 | | symbol: 0, |
412 | | symbol_value: 0, |
413 | | }, |
414 | | DecodeError::InvalidPadding, |
415 | | ); |
416 | | } |
417 | | |
418 | | #[test] |
419 | | fn decode_slice_error() { |
420 | | let _ = format!("{:?}", DecodeSliceError::OutputSliceTooSmall.clone()); |
421 | | let _ = format!( |
422 | | "{} {}", |
423 | | DecodeSliceError::OutputSliceTooSmall, |
424 | | DecodeSliceError::DecodeError(DecodeError::InvalidPadding) |
425 | | ); |
426 | | let _ = DecodeSliceError::OutputSliceTooSmall.source(); |
427 | | let _ = DecodeSliceError::DecodeError(DecodeError::InvalidPadding).source(); |
428 | | } |
429 | | |
430 | | #[test] |
431 | | fn deprecated_fns() { |
432 | | let _ = decode(""); |
433 | | let _ = decode_engine("", &crate::prelude::BASE64_STANDARD); |
434 | | let _ = decode_engine_vec("", &mut Vec::new(), &crate::prelude::BASE64_STANDARD); |
435 | | let _ = decode_engine_slice("", &mut [], &crate::prelude::BASE64_STANDARD); |
436 | | } |
437 | | |
438 | | #[test] |
439 | | fn decoded_len_est() { |
440 | | assert_eq!(3, decoded_len_estimate(4)); |
441 | | } |
442 | | } |