use crate::engine::{general_purpose::STANDARD, DecodeEstimate, Engine}; #[cfg(any(feature = "alloc", feature = "std", test))] use alloc::vec::Vec; use core::fmt; #[cfg(any(feature = "std", test))] use std::error; /// Errors that can occur while decoding. #[derive(Clone, Debug, PartialEq, Eq)] pub enum DecodeError { /// An invalid byte was found in the input. The offset and offending byte are provided. /// Padding characters (`=`) interspersed in the encoded form will be treated as invalid bytes. InvalidByte(usize, u8), /// The length of the input is invalid. /// A typical cause of this is stray trailing whitespace or other separator bytes. /// In the case where excess trailing bytes have produced an invalid length *and* the last byte /// is also an invalid base64 symbol (as would be the case for whitespace, etc), `InvalidByte` /// will be emitted instead of `InvalidLength` to make the issue easier to debug. InvalidLength, /// The last non-padding input symbol's encoded 6 bits have nonzero bits that will be discarded. /// This is indicative of corrupted or truncated Base64. /// Unlike `InvalidByte`, which reports symbols that aren't in the alphabet, this error is for /// symbols that are in the alphabet but represent nonsensical encodings. InvalidLastSymbol(usize, u8), /// The nature of the padding was not as configured: absent or incorrect when it must be /// canonical, or present when it must be absent, etc. InvalidPadding, } impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Self::InvalidByte(index, byte) => write!(f, "Invalid byte {}, offset {}.", byte, index), Self::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."), Self::InvalidLastSymbol(index, byte) => { write!(f, "Invalid last symbol {}, offset {}.", byte, index) } Self::InvalidPadding => write!(f, "Invalid padding"), } } } #[cfg(any(feature = "std", test))] impl error::Error for DecodeError { fn cause(&self) -> Option<&dyn error::Error> { None } } /// Errors that can occur while decoding into a slice. #[derive(Clone, Debug, PartialEq, Eq)] pub enum DecodeSliceError { /// A [DecodeError] occurred DecodeError(DecodeError), /// The provided slice _may_ be too small. /// /// The check is conservative (assumes the last triplet of output bytes will all be needed). OutputSliceTooSmall, } impl fmt::Display for DecodeSliceError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::DecodeError(e) => write!(f, "DecodeError: {}", e), Self::OutputSliceTooSmall => write!(f, "Output slice too small"), } } } #[cfg(any(feature = "std", test))] impl error::Error for DecodeSliceError { fn cause(&self) -> Option<&dyn error::Error> { match self { DecodeSliceError::DecodeError(e) => Some(e), DecodeSliceError::OutputSliceTooSmall => None, } } } impl From for DecodeSliceError { fn from(e: DecodeError) -> Self { DecodeSliceError::DecodeError(e) } } /// Decode base64 using the [`STANDARD` engine](STANDARD). /// /// See [Engine::decode]. #[deprecated(since = "0.21.0", note = "Use Engine::decode")] #[cfg(any(feature = "alloc", feature = "std", test))] pub fn decode>(input: T) -> Result, DecodeError> { STANDARD.decode(input) } /// Decode from string reference as octets using the specified [Engine]. /// /// See [Engine::decode]. ///Returns a `Result` containing a `Vec`. #[deprecated(since = "0.21.0", note = "Use Engine::decode")] #[cfg(any(feature = "alloc", feature = "std", test))] pub fn decode_engine>( input: T, engine: &E, ) -> Result, DecodeError> { engine.decode(input) } /// Decode from string reference as octets. /// /// See [Engine::decode_vec]. #[cfg(any(feature = "alloc", feature = "std", test))] #[deprecated(since = "0.21.0", note = "Use Engine::decode_vec")] pub fn decode_engine_vec>( input: T, buffer: &mut Vec, engine: &E, ) -> Result<(), DecodeError> { engine.decode_vec(input, buffer) } /// Decode the input into the provided output slice. /// /// See [Engine::decode_slice]. #[deprecated(since = "0.21.0", note = "Use Engine::decode_slice")] pub fn decode_engine_slice>( input: T, output: &mut [u8], engine: &E, ) -> Result { engine.decode_slice(input, output) } /// Returns a conservative estimate of the decoded size of `encoded_len` base64 symbols (rounded up /// to the next group of 3 decoded bytes). /// /// The resulting length will be a safe choice for the size of a decode buffer, but may have up to /// 2 trailing bytes that won't end up being needed. /// /// # Examples /// /// ``` /// use base64::decoded_len_estimate; /// /// assert_eq!(3, decoded_len_estimate(1)); /// assert_eq!(3, decoded_len_estimate(2)); /// assert_eq!(3, decoded_len_estimate(3)); /// assert_eq!(3, decoded_len_estimate(4)); /// // start of the next quad of encoded symbols /// assert_eq!(6, decoded_len_estimate(5)); /// ``` /// /// # Panics /// /// Panics if decoded length estimation overflows. /// This would happen for sizes within a few bytes of the maximum value of `usize`. pub fn decoded_len_estimate(encoded_len: usize) -> usize { STANDARD .internal_decoded_len_estimate(encoded_len) .decoded_len_estimate() } #[cfg(test)] mod tests { use super::*; use crate::{ alphabet, engine::{general_purpose, Config, GeneralPurpose}, tests::{assert_encode_sanity, random_engine}, }; use rand::{ distributions::{Distribution, Uniform}, Rng, SeedableRng, }; #[test] fn decode_into_nonempty_vec_doesnt_clobber_existing_prefix() { let mut orig_data = Vec::new(); let mut encoded_data = String::new(); let mut decoded_with_prefix = Vec::new(); let mut decoded_without_prefix = Vec::new(); let mut prefix = Vec::new(); let prefix_len_range = Uniform::new(0, 1000); let input_len_range = Uniform::new(0, 1000); let mut rng = rand::rngs::SmallRng::from_entropy(); for _ in 0..10_000 { orig_data.clear(); encoded_data.clear(); decoded_with_prefix.clear(); decoded_without_prefix.clear(); prefix.clear(); let input_len = input_len_range.sample(&mut rng); for _ in 0..input_len { orig_data.push(rng.gen()); } let engine = random_engine(&mut rng); engine.encode_string(&orig_data, &mut encoded_data); assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len); let prefix_len = prefix_len_range.sample(&mut rng); // fill the buf with a prefix for _ in 0..prefix_len { prefix.push(rng.gen()); } decoded_with_prefix.resize(prefix_len, 0); decoded_with_prefix.copy_from_slice(&prefix); // decode into the non-empty buf engine .decode_vec(&encoded_data, &mut decoded_with_prefix) .unwrap(); // also decode into the empty buf engine .decode_vec(&encoded_data, &mut decoded_without_prefix) .unwrap(); assert_eq!( prefix_len + decoded_without_prefix.len(), decoded_with_prefix.len() ); assert_eq!(orig_data, decoded_without_prefix); // append plain decode onto prefix prefix.append(&mut decoded_without_prefix); assert_eq!(prefix, decoded_with_prefix); } } #[test] fn decode_slice_doesnt_clobber_existing_prefix_or_suffix() { do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| { e.decode_slice(input, output).unwrap() }) } #[test] fn decode_slice_unchecked_doesnt_clobber_existing_prefix_or_suffix() { do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| { e.decode_slice_unchecked(input, output).unwrap() }) } #[test] fn decode_engine_estimation_works_for_various_lengths() { let engine = GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD); for num_prefix_quads in 0..100 { for suffix in &["AA", "AAA", "AAAA"] { let mut prefix = "AAAA".repeat(num_prefix_quads); prefix.push_str(suffix); // make sure no overflow (and thus a panic) occurs let res = engine.decode(prefix); assert!(res.is_ok()); } } } #[test] fn decode_slice_output_length_errors() { for num_quads in 1..100 { let input = "AAAA".repeat(num_quads); let mut vec = vec![0; (num_quads - 1) * 3]; assert_eq!( DecodeSliceError::OutputSliceTooSmall, STANDARD.decode_slice(&input, &mut vec).unwrap_err() ); vec.push(0); assert_eq!( DecodeSliceError::OutputSliceTooSmall, STANDARD.decode_slice(&input, &mut vec).unwrap_err() ); vec.push(0); assert_eq!( DecodeSliceError::OutputSliceTooSmall, STANDARD.decode_slice(&input, &mut vec).unwrap_err() ); vec.push(0); // now it works assert_eq!( num_quads * 3, STANDARD.decode_slice(&input, &mut vec).unwrap() ); } } fn do_decode_slice_doesnt_clobber_existing_prefix_or_suffix< F: Fn(&GeneralPurpose, &[u8], &mut [u8]) -> usize, >( call_decode: F, ) { let mut orig_data = Vec::new(); let mut encoded_data = String::new(); let mut decode_buf = Vec::new(); let mut decode_buf_copy: Vec = Vec::new(); let input_len_range = Uniform::new(0, 1000); let mut rng = rand::rngs::SmallRng::from_entropy(); for _ in 0..10_000 { orig_data.clear(); encoded_data.clear(); decode_buf.clear(); decode_buf_copy.clear(); let input_len = input_len_range.sample(&mut rng); for _ in 0..input_len { orig_data.push(rng.gen()); } let engine = random_engine(&mut rng); engine.encode_string(&orig_data, &mut encoded_data); assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len); // fill the buffer with random garbage, long enough to have some room before and after for _ in 0..5000 { decode_buf.push(rng.gen()); } // keep a copy for later comparison decode_buf_copy.extend(decode_buf.iter()); let offset = 1000; // decode into the non-empty buf let decode_bytes_written = call_decode(&engine, encoded_data.as_bytes(), &mut decode_buf[offset..]); assert_eq!(orig_data.len(), decode_bytes_written); assert_eq!( orig_data, &decode_buf[offset..(offset + decode_bytes_written)] ); assert_eq!(&decode_buf_copy[0..offset], &decode_buf[0..offset]); assert_eq!( &decode_buf_copy[offset + decode_bytes_written..], &decode_buf[offset + decode_bytes_written..] ); } } }