1use crate::engine::{general_purpose::STANDARD, DecodeEstimate, Engine};
2#[cfg(any(feature = "alloc", feature = "std", test))]
3use alloc::vec::Vec;
4use core::fmt;
5#[cfg(any(feature = "std", test))]
6use std::error;
7
8/// Errors that can occur while decoding.
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub 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
30impl 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))]
44impl 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)]
52pub 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
61impl 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))]
71impl 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
80impl 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))]
91pub 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))]
101pub 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")]
113pub 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")]
125pub 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/// ```
151pub 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)]
158mod 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