Coverage Report

Created: 2025-07-01 06:28

/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
}