diff options
Diffstat (limited to 'vendor/base64/src/engine/tests.rs')
| -rw-r--r-- | vendor/base64/src/engine/tests.rs | 1579 |
1 files changed, 0 insertions, 1579 deletions
diff --git a/vendor/base64/src/engine/tests.rs b/vendor/base64/src/engine/tests.rs deleted file mode 100644 index 72bbf4bb..00000000 --- a/vendor/base64/src/engine/tests.rs +++ /dev/null @@ -1,1579 +0,0 @@ -// rstest_reuse template functions have unused variables -#![allow(unused_variables)] - -use rand::{ - self, - distributions::{self, Distribution as _}, - rngs, Rng as _, SeedableRng as _, -}; -use rstest::rstest; -use rstest_reuse::{apply, template}; -use std::{collections, fmt, io::Read as _}; - -use crate::{ - alphabet::{Alphabet, STANDARD}, - encode::add_padding, - encoded_len, - engine::{ - general_purpose, naive, Config, DecodeEstimate, DecodeMetadata, DecodePaddingMode, Engine, - }, - read::DecoderReader, - tests::{assert_encode_sanity, random_alphabet, random_config}, - DecodeError, DecodeSliceError, PAD_BYTE, -}; - -// the case::foo syntax includes the "foo" in the generated test method names -#[template] -#[rstest(engine_wrapper, -case::general_purpose(GeneralPurposeWrapper {}), -case::naive(NaiveWrapper {}), -case::decoder_reader(DecoderReaderEngineWrapper {}), -)] -fn all_engines<E: EngineWrapper>(engine_wrapper: E) {} - -/// Some decode tests don't make sense for use with `DecoderReader` as they are difficult to -/// reason about or otherwise inapplicable given how DecoderReader slice up its input along -/// chunk boundaries. -#[template] -#[rstest(engine_wrapper, -case::general_purpose(GeneralPurposeWrapper {}), -case::naive(NaiveWrapper {}), -)] -fn all_engines_except_decoder_reader<E: EngineWrapper>(engine_wrapper: E) {} - -#[apply(all_engines)] -fn rfc_test_vectors_std_alphabet<E: EngineWrapper>(engine_wrapper: E) { - let data = vec![ - ("", ""), - ("f", "Zg=="), - ("fo", "Zm8="), - ("foo", "Zm9v"), - ("foob", "Zm9vYg=="), - ("fooba", "Zm9vYmE="), - ("foobar", "Zm9vYmFy"), - ]; - - let engine = E::standard(); - let engine_no_padding = E::standard_unpadded(); - - for (orig, encoded) in &data { - let encoded_without_padding = encoded.trim_end_matches('='); - - // unpadded - { - let mut encode_buf = [0_u8; 8]; - let mut decode_buf = [0_u8; 6]; - - let encode_len = - engine_no_padding.internal_encode(orig.as_bytes(), &mut encode_buf[..]); - assert_eq!( - &encoded_without_padding, - &std::str::from_utf8(&encode_buf[0..encode_len]).unwrap() - ); - let decode_len = engine_no_padding - .decode_slice_unchecked(encoded_without_padding.as_bytes(), &mut decode_buf[..]) - .unwrap(); - assert_eq!(orig.len(), decode_len); - - assert_eq!( - orig, - &std::str::from_utf8(&decode_buf[0..decode_len]).unwrap() - ); - - // if there was any padding originally, the no padding engine won't decode it - if encoded.as_bytes().contains(&PAD_BYTE) { - assert_eq!( - Err(DecodeError::InvalidPadding), - engine_no_padding.decode(encoded) - ) - } - } - - // padded - { - let mut encode_buf = [0_u8; 8]; - let mut decode_buf = [0_u8; 6]; - - let encode_len = engine.internal_encode(orig.as_bytes(), &mut encode_buf[..]); - assert_eq!( - // doesn't have padding added yet - &encoded_without_padding, - &std::str::from_utf8(&encode_buf[0..encode_len]).unwrap() - ); - let pad_len = add_padding(encode_len, &mut encode_buf[encode_len..]); - assert_eq!(encoded.as_bytes(), &encode_buf[..encode_len + pad_len]); - - let decode_len = engine - .decode_slice_unchecked(encoded.as_bytes(), &mut decode_buf[..]) - .unwrap(); - assert_eq!(orig.len(), decode_len); - - assert_eq!( - orig, - &std::str::from_utf8(&decode_buf[0..decode_len]).unwrap() - ); - - // if there was (canonical) padding, and we remove it, the standard engine won't decode - if encoded.as_bytes().contains(&PAD_BYTE) { - assert_eq!( - Err(DecodeError::InvalidPadding), - engine.decode(encoded_without_padding) - ) - } - } - } -} - -#[apply(all_engines)] -fn roundtrip_random<E: EngineWrapper>(engine_wrapper: E) { - let mut rng = seeded_rng(); - - let mut orig_data = Vec::<u8>::new(); - let mut encode_buf = Vec::<u8>::new(); - let mut decode_buf = Vec::<u8>::new(); - - let len_range = distributions::Uniform::new(1, 1_000); - - for _ in 0..10_000 { - let engine = E::random(&mut rng); - - orig_data.clear(); - encode_buf.clear(); - decode_buf.clear(); - - let (orig_len, _, encoded_len) = generate_random_encoded_data( - &engine, - &mut orig_data, - &mut encode_buf, - &mut rng, - &len_range, - ); - - // exactly the right size - decode_buf.resize(orig_len, 0); - - let dec_len = engine - .decode_slice_unchecked(&encode_buf[0..encoded_len], &mut decode_buf[..]) - .unwrap(); - - assert_eq!(orig_len, dec_len); - assert_eq!(&orig_data[..], &decode_buf[..dec_len]); - } -} - -#[apply(all_engines)] -fn encode_doesnt_write_extra_bytes<E: EngineWrapper>(engine_wrapper: E) { - let mut rng = seeded_rng(); - - let mut orig_data = Vec::<u8>::new(); - let mut encode_buf = Vec::<u8>::new(); - let mut encode_buf_backup = Vec::<u8>::new(); - - let input_len_range = distributions::Uniform::new(0, 1000); - - for _ in 0..10_000 { - let engine = E::random(&mut rng); - let padded = engine.config().encode_padding(); - - orig_data.clear(); - encode_buf.clear(); - encode_buf_backup.clear(); - - let orig_len = fill_rand(&mut orig_data, &mut rng, &input_len_range); - - let prefix_len = 1024; - // plenty of prefix and suffix - fill_rand_len(&mut encode_buf, &mut rng, prefix_len * 2 + orig_len * 2); - encode_buf_backup.extend_from_slice(&encode_buf[..]); - - let expected_encode_len_no_pad = encoded_len(orig_len, false).unwrap(); - - let encoded_len_no_pad = - engine.internal_encode(&orig_data[..], &mut encode_buf[prefix_len..]); - assert_eq!(expected_encode_len_no_pad, encoded_len_no_pad); - - // no writes past what it claimed to write - assert_eq!(&encode_buf_backup[..prefix_len], &encode_buf[..prefix_len]); - assert_eq!( - &encode_buf_backup[(prefix_len + encoded_len_no_pad)..], - &encode_buf[(prefix_len + encoded_len_no_pad)..] - ); - - let encoded_data = &encode_buf[prefix_len..(prefix_len + encoded_len_no_pad)]; - assert_encode_sanity( - std::str::from_utf8(encoded_data).unwrap(), - // engines don't pad - false, - orig_len, - ); - - // pad so we can decode it in case our random engine requires padding - let pad_len = if padded { - add_padding( - encoded_len_no_pad, - &mut encode_buf[prefix_len + encoded_len_no_pad..], - ) - } else { - 0 - }; - - assert_eq!( - orig_data, - engine - .decode(&encode_buf[prefix_len..(prefix_len + encoded_len_no_pad + pad_len)],) - .unwrap() - ); - } -} - -#[apply(all_engines)] -fn encode_engine_slice_fits_into_precisely_sized_slice<E: EngineWrapper>(engine_wrapper: E) { - let mut orig_data = Vec::new(); - let mut encoded_data = Vec::new(); - let mut decoded = Vec::new(); - - let input_len_range = distributions::Uniform::new(0, 1000); - - let mut rng = rngs::SmallRng::from_entropy(); - - for _ in 0..10_000 { - orig_data.clear(); - encoded_data.clear(); - decoded.clear(); - - let input_len = input_len_range.sample(&mut rng); - - for _ in 0..input_len { - orig_data.push(rng.gen()); - } - - let engine = E::random(&mut rng); - - let encoded_size = encoded_len(input_len, engine.config().encode_padding()).unwrap(); - - encoded_data.resize(encoded_size, 0); - - assert_eq!( - encoded_size, - engine.encode_slice(&orig_data, &mut encoded_data).unwrap() - ); - - assert_encode_sanity( - std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(), - engine.config().encode_padding(), - input_len, - ); - - engine - .decode_vec(&encoded_data[0..encoded_size], &mut decoded) - .unwrap(); - assert_eq!(orig_data, decoded); - } -} - -#[apply(all_engines)] -fn decode_doesnt_write_extra_bytes<E>(engine_wrapper: E) -where - E: EngineWrapper, - <<E as EngineWrapper>::Engine as Engine>::Config: fmt::Debug, -{ - let mut rng = seeded_rng(); - - let mut orig_data = Vec::<u8>::new(); - let mut encode_buf = Vec::<u8>::new(); - let mut decode_buf = Vec::<u8>::new(); - let mut decode_buf_backup = Vec::<u8>::new(); - - let len_range = distributions::Uniform::new(1, 1_000); - - for _ in 0..10_000 { - let engine = E::random(&mut rng); - - orig_data.clear(); - encode_buf.clear(); - decode_buf.clear(); - decode_buf_backup.clear(); - - let orig_len = fill_rand(&mut orig_data, &mut rng, &len_range); - encode_buf.resize(orig_len * 2 + 100, 0); - - let encoded_len = engine - .encode_slice(&orig_data[..], &mut encode_buf[..]) - .unwrap(); - encode_buf.truncate(encoded_len); - - // oversize decode buffer so we can easily tell if it writes anything more than - // just the decoded data - let prefix_len = 1024; - // plenty of prefix and suffix - fill_rand_len(&mut decode_buf, &mut rng, prefix_len * 2 + orig_len * 2); - decode_buf_backup.extend_from_slice(&decode_buf[..]); - - let dec_len = engine - .decode_slice_unchecked(&encode_buf, &mut decode_buf[prefix_len..]) - .unwrap(); - - assert_eq!(orig_len, dec_len); - assert_eq!( - &orig_data[..], - &decode_buf[prefix_len..prefix_len + dec_len] - ); - assert_eq!(&decode_buf_backup[..prefix_len], &decode_buf[..prefix_len]); - assert_eq!( - &decode_buf_backup[prefix_len + dec_len..], - &decode_buf[prefix_len + dec_len..] - ); - } -} - -#[apply(all_engines)] -fn decode_detect_invalid_last_symbol<E: EngineWrapper>(engine_wrapper: E) { - // 0xFF -> "/w==", so all letters > w, 0-9, and '+', '/' should get InvalidLastSymbol - let engine = E::standard(); - - assert_eq!(Ok(vec![0x89, 0x85]), engine.decode("iYU=")); - assert_eq!(Ok(vec![0xFF]), engine.decode("/w==")); - - for (suffix, offset) in vec![ - // suffix, offset of bad byte from start of suffix - ("/x==", 1_usize), - ("/z==", 1_usize), - ("/0==", 1_usize), - ("/9==", 1_usize), - ("/+==", 1_usize), - ("//==", 1_usize), - // trailing 01 - ("iYV=", 2_usize), - // trailing 10 - ("iYW=", 2_usize), - // trailing 11 - ("iYX=", 2_usize), - ] { - for prefix_quads in 0..256 { - let mut encoded = "AAAA".repeat(prefix_quads); - encoded.push_str(suffix); - - assert_eq!( - Err(DecodeError::InvalidLastSymbol( - encoded.len() - 4 + offset, - suffix.as_bytes()[offset], - )), - engine.decode(encoded.as_str()) - ); - } - } -} - -#[apply(all_engines)] -fn decode_detect_1_valid_symbol_in_last_quad_invalid_length<E: EngineWrapper>(engine_wrapper: E) { - for len in (0_usize..256).map(|len| len * 4 + 1) { - for mode in all_pad_modes() { - let mut input = vec![b'A'; len]; - - let engine = E::standard_with_pad_mode(true, mode); - - assert_eq!(Err(DecodeError::InvalidLength(len)), engine.decode(&input)); - // if we add padding, then the first pad byte in the quad is invalid because it should - // be the second symbol - for _ in 0..3 { - input.push(PAD_BYTE); - assert_eq!( - Err(DecodeError::InvalidByte(len, PAD_BYTE)), - engine.decode(&input) - ); - } - } - } -} - -#[apply(all_engines)] -fn decode_detect_1_invalid_byte_in_last_quad_invalid_byte<E: EngineWrapper>(engine_wrapper: E) { - for prefix_len in (0_usize..256).map(|len| len * 4) { - for mode in all_pad_modes() { - let mut input = vec![b'A'; prefix_len]; - input.push(b'*'); - - let engine = E::standard_with_pad_mode(true, mode); - - assert_eq!( - Err(DecodeError::InvalidByte(prefix_len, b'*')), - engine.decode(&input) - ); - // adding padding doesn't matter - for _ in 0..3 { - input.push(PAD_BYTE); - assert_eq!( - Err(DecodeError::InvalidByte(prefix_len, b'*')), - engine.decode(&input) - ); - } - } - } -} - -#[apply(all_engines)] -fn decode_detect_invalid_last_symbol_every_possible_two_symbols<E: EngineWrapper>( - engine_wrapper: E, -) { - let engine = E::standard(); - - let mut base64_to_bytes = collections::HashMap::new(); - - for b in 0_u8..=255 { - let mut b64 = vec![0_u8; 4]; - assert_eq!(2, engine.internal_encode(&[b], &mut b64[..])); - let _ = add_padding(2, &mut b64[2..]); - - assert!(base64_to_bytes.insert(b64, vec![b]).is_none()); - } - - // every possible combination of trailing symbols must either decode to 1 byte or get InvalidLastSymbol, with or without any leading chunks - - let mut prefix = Vec::new(); - for _ in 0..256 { - let mut clone = prefix.clone(); - - let mut symbols = [0_u8; 4]; - for &s1 in STANDARD.symbols.iter() { - symbols[0] = s1; - for &s2 in STANDARD.symbols.iter() { - symbols[1] = s2; - symbols[2] = PAD_BYTE; - symbols[3] = PAD_BYTE; - - // chop off previous symbols - clone.truncate(prefix.len()); - clone.extend_from_slice(&symbols[..]); - let decoded_prefix_len = prefix.len() / 4 * 3; - - match base64_to_bytes.get(&symbols[..]) { - Some(bytes) => { - let res = engine - .decode(&clone) - // remove prefix - .map(|decoded| decoded[decoded_prefix_len..].to_vec()); - - assert_eq!(Ok(bytes.clone()), res); - } - None => assert_eq!( - Err(DecodeError::InvalidLastSymbol(1, s2)), - engine.decode(&symbols[..]) - ), - } - } - } - - prefix.extend_from_slice(b"AAAA"); - } -} - -#[apply(all_engines)] -fn decode_detect_invalid_last_symbol_every_possible_three_symbols<E: EngineWrapper>( - engine_wrapper: E, -) { - let engine = E::standard(); - - let mut base64_to_bytes = collections::HashMap::new(); - - let mut bytes = [0_u8; 2]; - for b1 in 0_u8..=255 { - bytes[0] = b1; - for b2 in 0_u8..=255 { - bytes[1] = b2; - let mut b64 = vec![0_u8; 4]; - assert_eq!(3, engine.internal_encode(&bytes, &mut b64[..])); - let _ = add_padding(3, &mut b64[3..]); - - let mut v = Vec::with_capacity(2); - v.extend_from_slice(&bytes[..]); - - assert!(base64_to_bytes.insert(b64, v).is_none()); - } - } - - // every possible combination of symbols must either decode to 2 bytes or get InvalidLastSymbol, with or without any leading chunks - - let mut prefix = Vec::new(); - let mut input = Vec::new(); - for _ in 0..256 { - input.clear(); - input.extend_from_slice(&prefix); - - let mut symbols = [0_u8; 4]; - for &s1 in STANDARD.symbols.iter() { - symbols[0] = s1; - for &s2 in STANDARD.symbols.iter() { - symbols[1] = s2; - for &s3 in STANDARD.symbols.iter() { - symbols[2] = s3; - symbols[3] = PAD_BYTE; - - // chop off previous symbols - input.truncate(prefix.len()); - input.extend_from_slice(&symbols[..]); - let decoded_prefix_len = prefix.len() / 4 * 3; - - match base64_to_bytes.get(&symbols[..]) { - Some(bytes) => { - let res = engine - .decode(&input) - // remove prefix - .map(|decoded| decoded[decoded_prefix_len..].to_vec()); - - assert_eq!(Ok(bytes.clone()), res); - } - None => assert_eq!( - Err(DecodeError::InvalidLastSymbol(2, s3)), - engine.decode(&symbols[..]) - ), - } - } - } - } - prefix.extend_from_slice(b"AAAA"); - } -} - -#[apply(all_engines)] -fn decode_invalid_trailing_bits_ignored_when_configured<E: EngineWrapper>(engine_wrapper: E) { - let strict = E::standard(); - let forgiving = E::standard_allow_trailing_bits(); - - fn assert_tolerant_decode<E: Engine>( - engine: &E, - input: &mut String, - b64_prefix_len: usize, - expected_decode_bytes: Vec<u8>, - data: &str, - ) { - let prefixed = prefixed_data(input, b64_prefix_len, data); - let decoded = engine.decode(prefixed); - // prefix is always complete chunks - let decoded_prefix_len = b64_prefix_len / 4 * 3; - assert_eq!( - Ok(expected_decode_bytes), - decoded.map(|v| v[decoded_prefix_len..].to_vec()) - ); - } - - let mut prefix = String::new(); - for _ in 0..256 { - let mut input = prefix.clone(); - - // example from https://github.com/marshallpierce/rust-base64/issues/75 - assert!(strict - .decode(prefixed_data(&mut input, prefix.len(), "/w==")) - .is_ok()); - assert!(strict - .decode(prefixed_data(&mut input, prefix.len(), "iYU=")) - .is_ok()); - // trailing 01 - assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![255], "/x=="); - assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![137, 133], "iYV="); - // trailing 10 - assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![255], "/y=="); - assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![137, 133], "iYW="); - // trailing 11 - assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![255], "/z=="); - assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![137, 133], "iYX="); - - prefix.push_str("AAAA"); - } -} - -#[apply(all_engines)] -fn decode_invalid_byte_error<E: EngineWrapper>(engine_wrapper: E) { - let mut rng = seeded_rng(); - - let mut orig_data = Vec::<u8>::new(); - let mut encode_buf = Vec::<u8>::new(); - let mut decode_buf = Vec::<u8>::new(); - - let len_range = distributions::Uniform::new(1, 1_000); - - for _ in 0..100_000 { - let alphabet = random_alphabet(&mut rng); - let engine = E::random_alphabet(&mut rng, alphabet); - - orig_data.clear(); - encode_buf.clear(); - decode_buf.clear(); - - let (orig_len, encoded_len_just_data, encoded_len_with_padding) = - generate_random_encoded_data( - &engine, - &mut orig_data, - &mut encode_buf, - &mut rng, - &len_range, - ); - - // exactly the right size - decode_buf.resize(orig_len, 0); - - // replace one encoded byte with an invalid byte - let invalid_byte: u8 = loop { - let byte: u8 = rng.gen(); - - if alphabet.symbols.contains(&byte) || byte == PAD_BYTE { - continue; - } else { - break byte; - } - }; - - let invalid_range = distributions::Uniform::new(0, orig_len); - let invalid_index = invalid_range.sample(&mut rng); - encode_buf[invalid_index] = invalid_byte; - - assert_eq!( - Err(DecodeError::InvalidByte(invalid_index, invalid_byte)), - engine.decode_slice_unchecked( - &encode_buf[0..encoded_len_with_padding], - &mut decode_buf[..], - ) - ); - } -} - -/// Any amount of padding anywhere before the final non padding character = invalid byte at first -/// pad byte. -/// From this and [decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_non_canonical_padding_suffix_all_modes], -/// we know padding must extend contiguously to the end of the input. -#[apply(all_engines)] -fn decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_all_modes< - E: EngineWrapper, ->( - engine_wrapper: E, -) { - // Different amounts of padding, w/ offset from end for the last non-padding char. - // Only canonical padding, so Canonical mode will work. - let suffixes = &[("AA==", 2), ("AAA=", 1), ("AAAA", 0)]; - - for mode in pad_modes_allowing_padding() { - // We don't encode, so we don't care about encode padding. - let engine = E::standard_with_pad_mode(true, mode); - - decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad( - engine, - suffixes.as_slice(), - ); - } -} - -/// See [decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_all_modes] -#[apply(all_engines)] -fn decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_non_canonical_padding_suffix< - E: EngineWrapper, ->( - engine_wrapper: E, -) { - // Different amounts of padding, w/ offset from end for the last non-padding char, and - // non-canonical padding. - let suffixes = [ - ("AA==", 2), - ("AA=", 1), - ("AA", 0), - ("AAA=", 1), - ("AAA", 0), - ("AAAA", 0), - ]; - - // We don't encode, so we don't care about encode padding. - // Decoding is indifferent so that we don't get caught by missing padding on the last quad - let engine = E::standard_with_pad_mode(true, DecodePaddingMode::Indifferent); - - decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad( - engine, - suffixes.as_slice(), - ) -} - -fn decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad( - engine: impl Engine, - suffixes: &[(&str, usize)], -) { - let mut rng = seeded_rng(); - - let prefix_quads_range = distributions::Uniform::from(0..=256); - - for _ in 0..100_000 { - for (suffix, suffix_offset) in suffixes.iter() { - let mut s = "AAAA".repeat(prefix_quads_range.sample(&mut rng)); - s.push_str(suffix); - let mut encoded = s.into_bytes(); - - // calculate a range to write padding into that leaves at least one non padding char - let last_non_padding_offset = encoded.len() - 1 - suffix_offset; - - // don't include last non padding char as it must stay not padding - let padding_end = rng.gen_range(0..last_non_padding_offset); - - // don't use more than 100 bytes of padding, but also use shorter lengths when - // padding_end is near the start of the encoded data to avoid biasing to padding - // the entire prefix on short lengths - let padding_len = rng.gen_range(1..=usize::min(100, padding_end + 1)); - let padding_start = padding_end.saturating_sub(padding_len); - - encoded[padding_start..=padding_end].fill(PAD_BYTE); - - // should still have non-padding before any final padding - assert_ne!(PAD_BYTE, encoded[last_non_padding_offset]); - assert_eq!( - Err(DecodeError::InvalidByte(padding_start, PAD_BYTE)), - engine.decode(&encoded), - "len: {}, input: {}", - encoded.len(), - String::from_utf8(encoded).unwrap() - ); - } - } -} - -/// Any amount of padding before final chunk that crosses over into final chunk with 1-4 bytes = -/// invalid byte at first pad byte. -/// From this we know the padding must start in the final chunk. -#[apply(all_engines)] -fn decode_padding_starts_before_final_chunk_error_invalid_byte_at_first_pad<E: EngineWrapper>( - engine_wrapper: E, -) { - let mut rng = seeded_rng(); - - // must have at least one prefix quad - let prefix_quads_range = distributions::Uniform::from(1..256); - let suffix_pad_len_range = distributions::Uniform::from(1..=4); - // don't use no-padding mode, as the reader decode might decode a block that ends with - // valid padding, which should then be referenced when encountering the later invalid byte - for mode in pad_modes_allowing_padding() { - // we don't encode so we don't care about encode padding - let engine = E::standard_with_pad_mode(true, mode); - for _ in 0..100_000 { - let suffix_len = suffix_pad_len_range.sample(&mut rng); - // all 0 bits so we don't hit InvalidLastSymbol with the reader decoder - let mut encoded = "AAAA" - .repeat(prefix_quads_range.sample(&mut rng)) - .into_bytes(); - encoded.resize(encoded.len() + suffix_len, PAD_BYTE); - - // amount of padding must be long enough to extend back from suffix into previous - // quads - let padding_len = rng.gen_range(suffix_len + 1..encoded.len()); - // no non-padding after padding in this test, so padding goes to the end - let padding_start = encoded.len() - padding_len; - encoded[padding_start..].fill(PAD_BYTE); - - assert_eq!( - Err(DecodeError::InvalidByte(padding_start, PAD_BYTE)), - engine.decode(&encoded), - "suffix_len: {}, padding_len: {}, b64: {}", - suffix_len, - padding_len, - std::str::from_utf8(&encoded).unwrap() - ); - } - } -} - -/// 0-1 bytes of data before any amount of padding in final chunk = invalid byte, since padding -/// is not valid data (consistent with error for pad bytes in earlier chunks). -/// From this we know there must be 2-3 bytes of data before padding -#[apply(all_engines)] -fn decode_too_little_data_before_padding_error_invalid_byte<E: EngineWrapper>(engine_wrapper: E) { - let mut rng = seeded_rng(); - - // want to test no prefix quad case, so start at 0 - let prefix_quads_range = distributions::Uniform::from(0_usize..256); - let suffix_data_len_range = distributions::Uniform::from(0_usize..=1); - for mode in all_pad_modes() { - // we don't encode so we don't care about encode padding - let engine = E::standard_with_pad_mode(true, mode); - for _ in 0..100_000 { - let suffix_data_len = suffix_data_len_range.sample(&mut rng); - let prefix_quad_len = prefix_quads_range.sample(&mut rng); - - // for all possible padding lengths - for padding_len in 1..=(4 - suffix_data_len) { - let mut encoded = "ABCD".repeat(prefix_quad_len).into_bytes(); - encoded.resize(encoded.len() + suffix_data_len, b'A'); - encoded.resize(encoded.len() + padding_len, PAD_BYTE); - - assert_eq!( - Err(DecodeError::InvalidByte( - prefix_quad_len * 4 + suffix_data_len, - PAD_BYTE, - )), - engine.decode(&encoded), - "input {} suffix data len {} pad len {}", - String::from_utf8(encoded).unwrap(), - suffix_data_len, - padding_len - ); - } - } - } -} - -// https://eprint.iacr.org/2022/361.pdf table 2, test 1 -#[apply(all_engines)] -fn decode_malleability_test_case_3_byte_suffix_valid<E: EngineWrapper>(engine_wrapper: E) { - assert_eq!( - b"Hello".as_slice(), - &E::standard().decode("SGVsbG8=").unwrap() - ); -} - -// https://eprint.iacr.org/2022/361.pdf table 2, test 2 -#[apply(all_engines)] -fn decode_malleability_test_case_3_byte_suffix_invalid_trailing_symbol<E: EngineWrapper>( - engine_wrapper: E, -) { - assert_eq!( - DecodeError::InvalidLastSymbol(6, 0x39), - E::standard().decode("SGVsbG9=").unwrap_err() - ); -} - -// https://eprint.iacr.org/2022/361.pdf table 2, test 3 -#[apply(all_engines)] -fn decode_malleability_test_case_3_byte_suffix_no_padding<E: EngineWrapper>(engine_wrapper: E) { - assert_eq!( - DecodeError::InvalidPadding, - E::standard().decode("SGVsbG9").unwrap_err() - ); -} - -// https://eprint.iacr.org/2022/361.pdf table 2, test 4 -#[apply(all_engines)] -fn decode_malleability_test_case_2_byte_suffix_valid_two_padding_symbols<E: EngineWrapper>( - engine_wrapper: E, -) { - assert_eq!( - b"Hell".as_slice(), - &E::standard().decode("SGVsbA==").unwrap() - ); -} - -// https://eprint.iacr.org/2022/361.pdf table 2, test 5 -#[apply(all_engines)] -fn decode_malleability_test_case_2_byte_suffix_short_padding<E: EngineWrapper>(engine_wrapper: E) { - assert_eq!( - DecodeError::InvalidPadding, - E::standard().decode("SGVsbA=").unwrap_err() - ); -} - -// https://eprint.iacr.org/2022/361.pdf table 2, test 6 -#[apply(all_engines)] -fn decode_malleability_test_case_2_byte_suffix_no_padding<E: EngineWrapper>(engine_wrapper: E) { - assert_eq!( - DecodeError::InvalidPadding, - E::standard().decode("SGVsbA").unwrap_err() - ); -} - -// https://eprint.iacr.org/2022/361.pdf table 2, test 7 -// DecoderReader pseudo-engine gets InvalidByte at 8 (extra padding) since it decodes the first -// two complete quads correctly. -#[apply(all_engines_except_decoder_reader)] -fn decode_malleability_test_case_2_byte_suffix_too_much_padding<E: EngineWrapper>( - engine_wrapper: E, -) { - assert_eq!( - DecodeError::InvalidByte(6, PAD_BYTE), - E::standard().decode("SGVsbA====").unwrap_err() - ); -} - -/// Requires canonical padding -> accepts 2 + 2, 3 + 1, 4 + 0 final quad configurations -#[apply(all_engines)] -fn decode_pad_mode_requires_canonical_accepts_canonical<E: EngineWrapper>(engine_wrapper: E) { - assert_all_suffixes_ok( - E::standard_with_pad_mode(true, DecodePaddingMode::RequireCanonical), - vec!["/w==", "iYU=", "AAAA"], - ); -} - -/// Requires canonical padding -> rejects 2 + 0-1, 3 + 0 final chunk configurations -#[apply(all_engines)] -fn decode_pad_mode_requires_canonical_rejects_non_canonical<E: EngineWrapper>(engine_wrapper: E) { - let engine = E::standard_with_pad_mode(true, DecodePaddingMode::RequireCanonical); - - let suffixes = ["/w", "/w=", "iYU"]; - for num_prefix_quads in 0..256 { - for &suffix in suffixes.iter() { - let mut encoded = "AAAA".repeat(num_prefix_quads); - encoded.push_str(suffix); - - let res = engine.decode(&encoded); - - assert_eq!(Err(DecodeError::InvalidPadding), res); - } - } -} - -/// Requires no padding -> accepts 2 + 0, 3 + 0, 4 + 0 final chunk configuration -#[apply(all_engines)] -fn decode_pad_mode_requires_no_padding_accepts_no_padding<E: EngineWrapper>(engine_wrapper: E) { - assert_all_suffixes_ok( - E::standard_with_pad_mode(true, DecodePaddingMode::RequireNone), - vec!["/w", "iYU", "AAAA"], - ); -} - -/// Requires no padding -> rejects 2 + 1-2, 3 + 1 final chunk configuration -#[apply(all_engines)] -fn decode_pad_mode_requires_no_padding_rejects_any_padding<E: EngineWrapper>(engine_wrapper: E) { - let engine = E::standard_with_pad_mode(true, DecodePaddingMode::RequireNone); - - let suffixes = ["/w=", "/w==", "iYU="]; - for num_prefix_quads in 0..256 { - for &suffix in suffixes.iter() { - let mut encoded = "AAAA".repeat(num_prefix_quads); - encoded.push_str(suffix); - - let res = engine.decode(&encoded); - - assert_eq!(Err(DecodeError::InvalidPadding), res); - } - } -} - -/// Indifferent padding accepts 2 + 0-2, 3 + 0-1, 4 + 0 final chunk configuration -#[apply(all_engines)] -fn decode_pad_mode_indifferent_padding_accepts_anything<E: EngineWrapper>(engine_wrapper: E) { - assert_all_suffixes_ok( - E::standard_with_pad_mode(true, DecodePaddingMode::Indifferent), - vec!["/w", "/w=", "/w==", "iYU", "iYU=", "AAAA"], - ); -} - -/// 1 trailing byte that's not padding is detected as invalid byte even though there's padding -/// in the middle of the input. This is essentially mandating the eager check for 1 trailing byte -/// to catch the \n suffix case. -// DecoderReader pseudo-engine can't handle DecodePaddingMode::RequireNone since it will decode -// a complete quad with padding in it before encountering the stray byte that makes it an invalid -// length -#[apply(all_engines_except_decoder_reader)] -fn decode_invalid_trailing_bytes_all_pad_modes_invalid_byte<E: EngineWrapper>(engine_wrapper: E) { - for mode in all_pad_modes() { - do_invalid_trailing_byte(E::standard_with_pad_mode(true, mode), mode); - } -} - -#[apply(all_engines)] -fn decode_invalid_trailing_bytes_invalid_byte<E: EngineWrapper>(engine_wrapper: E) { - // excluding no padding mode because the DecoderWrapper pseudo-engine will fail with - // InvalidPadding because it will decode the last complete quad with padding first - for mode in pad_modes_allowing_padding() { - do_invalid_trailing_byte(E::standard_with_pad_mode(true, mode), mode); - } -} -fn do_invalid_trailing_byte(engine: impl Engine, mode: DecodePaddingMode) { - for last_byte in [b'*', b'\n'] { - for num_prefix_quads in 0..256 { - let mut s: String = "ABCD".repeat(num_prefix_quads); - s.push_str("Cg=="); - let mut input = s.into_bytes(); - input.push(last_byte); - - // The case of trailing newlines is common enough to warrant a test for a good error - // message. - assert_eq!( - Err(DecodeError::InvalidByte( - num_prefix_quads * 4 + 4, - last_byte - )), - engine.decode(&input), - "mode: {:?}, input: {}", - mode, - String::from_utf8(input).unwrap() - ); - } - } -} - -/// When there's 1 trailing byte, but it's padding, it's only InvalidByte if there isn't padding -/// earlier. -#[apply(all_engines)] -fn decode_invalid_trailing_padding_as_invalid_byte_at_first_pad_byte<E: EngineWrapper>( - engine_wrapper: E, -) { - // excluding no padding mode because the DecoderWrapper pseudo-engine will fail with - // InvalidPadding because it will decode the last complete quad with padding first - for mode in pad_modes_allowing_padding() { - do_invalid_trailing_padding_as_invalid_byte_at_first_padding( - E::standard_with_pad_mode(true, mode), - mode, - ); - } -} - -// DecoderReader pseudo-engine can't handle DecodePaddingMode::RequireNone since it will decode -// a complete quad with padding in it before encountering the stray byte that makes it an invalid -// length -#[apply(all_engines_except_decoder_reader)] -fn decode_invalid_trailing_padding_as_invalid_byte_at_first_byte_all_modes<E: EngineWrapper>( - engine_wrapper: E, -) { - for mode in all_pad_modes() { - do_invalid_trailing_padding_as_invalid_byte_at_first_padding( - E::standard_with_pad_mode(true, mode), - mode, - ); - } -} -fn do_invalid_trailing_padding_as_invalid_byte_at_first_padding( - engine: impl Engine, - mode: DecodePaddingMode, -) { - for num_prefix_quads in 0..256 { - for (suffix, pad_offset) in [("AA===", 2), ("AAA==", 3), ("AAAA=", 4)] { - let mut s: String = "ABCD".repeat(num_prefix_quads); - s.push_str(suffix); - - assert_eq!( - // pad after `g`, not the last one - Err(DecodeError::InvalidByte( - num_prefix_quads * 4 + pad_offset, - PAD_BYTE - )), - engine.decode(&s), - "mode: {:?}, input: {}", - mode, - s - ); - } - } -} - -#[apply(all_engines)] -fn decode_into_slice_fits_in_precisely_sized_slice<E: EngineWrapper>(engine_wrapper: E) { - let mut orig_data = Vec::new(); - let mut encoded_data = String::new(); - let mut decode_buf = Vec::new(); - - let input_len_range = distributions::Uniform::new(0, 1000); - let mut rng = rngs::SmallRng::from_entropy(); - - for _ in 0..10_000 { - orig_data.clear(); - encoded_data.clear(); - decode_buf.clear(); - - let input_len = input_len_range.sample(&mut rng); - - for _ in 0..input_len { - orig_data.push(rng.gen()); - } - - let engine = E::random(&mut rng); - engine.encode_string(&orig_data, &mut encoded_data); - assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len); - - decode_buf.resize(input_len, 0); - // decode into the non-empty buf - let decode_bytes_written = engine - .decode_slice_unchecked(encoded_data.as_bytes(), &mut decode_buf[..]) - .unwrap(); - assert_eq!(orig_data.len(), decode_bytes_written); - assert_eq!(orig_data, decode_buf); - - // same for checked variant - decode_buf.clear(); - decode_buf.resize(input_len, 0); - // decode into the non-empty buf - let decode_bytes_written = engine - .decode_slice(encoded_data.as_bytes(), &mut decode_buf[..]) - .unwrap(); - assert_eq!(orig_data.len(), decode_bytes_written); - assert_eq!(orig_data, decode_buf); - } -} - -#[apply(all_engines)] -fn inner_decode_reports_padding_position<E: EngineWrapper>(engine_wrapper: E) { - let mut b64 = String::new(); - let mut decoded = Vec::new(); - let engine = E::standard(); - - for pad_position in 1..10_000 { - b64.clear(); - decoded.clear(); - // plenty of room for original data - decoded.resize(pad_position, 0); - - for _ in 0..pad_position { - b64.push('A'); - } - // finish the quad with padding - for _ in 0..(4 - (pad_position % 4)) { - b64.push('='); - } - - let decode_res = engine.internal_decode( - b64.as_bytes(), - &mut decoded[..], - engine.internal_decoded_len_estimate(b64.len()), - ); - if pad_position % 4 < 2 { - // impossible padding - assert_eq!( - Err(DecodeSliceError::DecodeError(DecodeError::InvalidByte( - pad_position, - PAD_BYTE - ))), - decode_res - ); - } else { - let decoded_bytes = pad_position / 4 * 3 - + match pad_position % 4 { - 0 => 0, - 2 => 1, - 3 => 2, - _ => unreachable!(), - }; - assert_eq!( - Ok(DecodeMetadata::new(decoded_bytes, Some(pad_position))), - decode_res - ); - } - } -} - -#[apply(all_engines)] -fn decode_length_estimate_delta<E: EngineWrapper>(engine_wrapper: E) { - for engine in [E::standard(), E::standard_unpadded()] { - for &padding in &[true, false] { - for orig_len in 0..1000 { - let encoded_len = encoded_len(orig_len, padding).unwrap(); - - let decoded_estimate = engine - .internal_decoded_len_estimate(encoded_len) - .decoded_len_estimate(); - assert!(decoded_estimate >= orig_len); - assert!( - decoded_estimate - orig_len < 3, - "estimate: {}, encoded: {}, orig: {}", - decoded_estimate, - encoded_len, - orig_len - ); - } - } - } -} - -#[apply(all_engines)] -fn estimate_via_u128_inflation<E: EngineWrapper>(engine_wrapper: E) { - // cover both ends of usize - (0..1000) - .chain(usize::MAX - 1000..=usize::MAX) - .for_each(|encoded_len| { - // inflate to 128 bit type to be able to safely use the easy formulas - let len_128 = encoded_len as u128; - - let estimate = E::standard() - .internal_decoded_len_estimate(encoded_len) - .decoded_len_estimate(); - - // This check is a little too strict: it requires using the (len + 3) / 4 * 3 formula - // or equivalent, but until other engines come along that use a different formula - // requiring that we think more carefully about what the allowable criteria are, this - // will do. - assert_eq!( - ((len_128 + 3) / 4 * 3) as usize, - estimate, - "enc len {}", - encoded_len - ); - }) -} - -#[apply(all_engines)] -fn decode_slice_checked_fails_gracefully_at_all_output_lengths<E: EngineWrapper>( - engine_wrapper: E, -) { - let mut rng = seeded_rng(); - for original_len in 0..1000 { - let mut original = vec![0; original_len]; - rng.fill(&mut original[..]); - - for mode in all_pad_modes() { - let engine = E::standard_with_pad_mode( - match mode { - DecodePaddingMode::Indifferent | DecodePaddingMode::RequireCanonical => true, - DecodePaddingMode::RequireNone => false, - }, - mode, - ); - - let encoded = engine.encode(&original); - let mut decode_buf = Vec::with_capacity(original_len); - for decode_buf_len in 0..original_len { - decode_buf.resize(decode_buf_len, 0); - assert_eq!( - DecodeSliceError::OutputSliceTooSmall, - engine - .decode_slice(&encoded, &mut decode_buf[..]) - .unwrap_err(), - "original len: {}, encoded len: {}, buf len: {}, mode: {:?}", - original_len, - encoded.len(), - decode_buf_len, - mode - ); - // internal method works the same - assert_eq!( - DecodeSliceError::OutputSliceTooSmall, - engine - .internal_decode( - encoded.as_bytes(), - &mut decode_buf[..], - engine.internal_decoded_len_estimate(encoded.len()) - ) - .unwrap_err() - ); - } - - decode_buf.resize(original_len, 0); - rng.fill(&mut decode_buf[..]); - assert_eq!( - original_len, - engine.decode_slice(&encoded, &mut decode_buf[..]).unwrap() - ); - assert_eq!(original, decode_buf); - } - } -} - -/// Returns a tuple of the original data length, the encoded data length (just data), and the length including padding. -/// -/// Vecs provided should be empty. -fn generate_random_encoded_data<E: Engine, R: rand::Rng, D: distributions::Distribution<usize>>( - engine: &E, - orig_data: &mut Vec<u8>, - encode_buf: &mut Vec<u8>, - rng: &mut R, - length_distribution: &D, -) -> (usize, usize, usize) { - let padding: bool = engine.config().encode_padding(); - - let orig_len = fill_rand(orig_data, rng, length_distribution); - let expected_encoded_len = encoded_len(orig_len, padding).unwrap(); - encode_buf.resize(expected_encoded_len, 0); - - let base_encoded_len = engine.internal_encode(&orig_data[..], &mut encode_buf[..]); - - let enc_len_with_padding = if padding { - base_encoded_len + add_padding(base_encoded_len, &mut encode_buf[base_encoded_len..]) - } else { - base_encoded_len - }; - - assert_eq!(expected_encoded_len, enc_len_with_padding); - - (orig_len, base_encoded_len, enc_len_with_padding) -} - -// fill to a random length -fn fill_rand<R: rand::Rng, D: distributions::Distribution<usize>>( - vec: &mut Vec<u8>, - rng: &mut R, - length_distribution: &D, -) -> usize { - let len = length_distribution.sample(rng); - for _ in 0..len { - vec.push(rng.gen()); - } - - len -} - -fn fill_rand_len<R: rand::Rng>(vec: &mut Vec<u8>, rng: &mut R, len: usize) { - for _ in 0..len { - vec.push(rng.gen()); - } -} - -fn prefixed_data<'i>(input_with_prefix: &'i mut String, prefix_len: usize, data: &str) -> &'i str { - input_with_prefix.truncate(prefix_len); - input_with_prefix.push_str(data); - input_with_prefix.as_str() -} - -/// A wrapper to make using engines in rstest fixtures easier. -/// The functions don't need to be instance methods, but rstest does seem -/// to want an instance, so instances are passed to test functions and then ignored. -trait EngineWrapper { - type Engine: Engine; - - /// Return an engine configured for RFC standard base64 - fn standard() -> Self::Engine; - - /// Return an engine configured for RFC standard base64, except with no padding appended on - /// encode, and required no padding on decode. - fn standard_unpadded() -> Self::Engine; - - /// Return an engine configured for RFC standard alphabet with the provided encode and decode - /// pad settings - fn standard_with_pad_mode(encode_pad: bool, decode_pad_mode: DecodePaddingMode) - -> Self::Engine; - - /// Return an engine configured for RFC standard base64 that allows invalid trailing bits - fn standard_allow_trailing_bits() -> Self::Engine; - - /// Return an engine configured with a randomized alphabet and config - fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine; - - /// Return an engine configured with the specified alphabet and randomized config - fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine; -} - -struct GeneralPurposeWrapper {} - -impl EngineWrapper for GeneralPurposeWrapper { - type Engine = general_purpose::GeneralPurpose; - - fn standard() -> Self::Engine { - general_purpose::GeneralPurpose::new(&STANDARD, general_purpose::PAD) - } - - fn standard_unpadded() -> Self::Engine { - general_purpose::GeneralPurpose::new(&STANDARD, general_purpose::NO_PAD) - } - - fn standard_with_pad_mode( - encode_pad: bool, - decode_pad_mode: DecodePaddingMode, - ) -> Self::Engine { - general_purpose::GeneralPurpose::new( - &STANDARD, - general_purpose::GeneralPurposeConfig::new() - .with_encode_padding(encode_pad) - .with_decode_padding_mode(decode_pad_mode), - ) - } - - fn standard_allow_trailing_bits() -> Self::Engine { - general_purpose::GeneralPurpose::new( - &STANDARD, - general_purpose::GeneralPurposeConfig::new().with_decode_allow_trailing_bits(true), - ) - } - - fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine { - let alphabet = random_alphabet(rng); - - Self::random_alphabet(rng, alphabet) - } - - fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine { - general_purpose::GeneralPurpose::new(alphabet, random_config(rng)) - } -} - -struct NaiveWrapper {} - -impl EngineWrapper for NaiveWrapper { - type Engine = naive::Naive; - - fn standard() -> Self::Engine { - naive::Naive::new( - &STANDARD, - naive::NaiveConfig { - encode_padding: true, - decode_allow_trailing_bits: false, - decode_padding_mode: DecodePaddingMode::RequireCanonical, - }, - ) - } - - fn standard_unpadded() -> Self::Engine { - naive::Naive::new( - &STANDARD, - naive::NaiveConfig { - encode_padding: false, - decode_allow_trailing_bits: false, - decode_padding_mode: DecodePaddingMode::RequireNone, - }, - ) - } - - fn standard_with_pad_mode( - encode_pad: bool, - decode_pad_mode: DecodePaddingMode, - ) -> Self::Engine { - naive::Naive::new( - &STANDARD, - naive::NaiveConfig { - encode_padding: encode_pad, - decode_allow_trailing_bits: false, - decode_padding_mode: decode_pad_mode, - }, - ) - } - - fn standard_allow_trailing_bits() -> Self::Engine { - naive::Naive::new( - &STANDARD, - naive::NaiveConfig { - encode_padding: true, - decode_allow_trailing_bits: true, - decode_padding_mode: DecodePaddingMode::RequireCanonical, - }, - ) - } - - fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine { - let alphabet = random_alphabet(rng); - - Self::random_alphabet(rng, alphabet) - } - - fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine { - let mode = rng.gen(); - - let config = naive::NaiveConfig { - encode_padding: match mode { - DecodePaddingMode::Indifferent => rng.gen(), - DecodePaddingMode::RequireCanonical => true, - DecodePaddingMode::RequireNone => false, - }, - decode_allow_trailing_bits: rng.gen(), - decode_padding_mode: mode, - }; - - naive::Naive::new(alphabet, config) - } -} - -/// A pseudo-Engine that routes all decoding through [DecoderReader] -struct DecoderReaderEngine<E: Engine> { - engine: E, -} - -impl<E: Engine> From<E> for DecoderReaderEngine<E> { - fn from(value: E) -> Self { - Self { engine: value } - } -} - -impl<E: Engine> Engine for DecoderReaderEngine<E> { - type Config = E::Config; - type DecodeEstimate = E::DecodeEstimate; - - fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize { - self.engine.internal_encode(input, output) - } - - fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate { - self.engine.internal_decoded_len_estimate(input_len) - } - - fn internal_decode( - &self, - input: &[u8], - output: &mut [u8], - decode_estimate: Self::DecodeEstimate, - ) -> Result<DecodeMetadata, DecodeSliceError> { - let mut reader = DecoderReader::new(input, &self.engine); - let mut buf = vec![0; input.len()]; - // to avoid effects like not detecting invalid length due to progressively growing - // the output buffer in read_to_end etc, read into a big enough buffer in one go - // to make behavior more consistent with normal engines - let _ = reader - .read(&mut buf) - .and_then(|len| { - buf.truncate(len); - // make sure we got everything - reader.read_to_end(&mut buf) - }) - .map_err(|io_error| { - *io_error - .into_inner() - .and_then(|inner| inner.downcast::<DecodeError>().ok()) - .unwrap() - })?; - if output.len() < buf.len() { - return Err(DecodeSliceError::OutputSliceTooSmall); - } - output[..buf.len()].copy_from_slice(&buf); - Ok(DecodeMetadata::new( - buf.len(), - input - .iter() - .enumerate() - .filter(|(_offset, byte)| **byte == PAD_BYTE) - .map(|(offset, _byte)| offset) - .next(), - )) - } - - fn config(&self) -> &Self::Config { - self.engine.config() - } -} - -struct DecoderReaderEngineWrapper {} - -impl EngineWrapper for DecoderReaderEngineWrapper { - type Engine = DecoderReaderEngine<general_purpose::GeneralPurpose>; - - fn standard() -> Self::Engine { - GeneralPurposeWrapper::standard().into() - } - - fn standard_unpadded() -> Self::Engine { - GeneralPurposeWrapper::standard_unpadded().into() - } - - fn standard_with_pad_mode( - encode_pad: bool, - decode_pad_mode: DecodePaddingMode, - ) -> Self::Engine { - GeneralPurposeWrapper::standard_with_pad_mode(encode_pad, decode_pad_mode).into() - } - - fn standard_allow_trailing_bits() -> Self::Engine { - GeneralPurposeWrapper::standard_allow_trailing_bits().into() - } - - fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine { - GeneralPurposeWrapper::random(rng).into() - } - - fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine { - GeneralPurposeWrapper::random_alphabet(rng, alphabet).into() - } -} - -fn seeded_rng() -> impl rand::Rng { - rngs::SmallRng::from_entropy() -} - -fn all_pad_modes() -> Vec<DecodePaddingMode> { - vec![ - DecodePaddingMode::Indifferent, - DecodePaddingMode::RequireCanonical, - DecodePaddingMode::RequireNone, - ] -} - -fn pad_modes_allowing_padding() -> Vec<DecodePaddingMode> { - vec![ - DecodePaddingMode::Indifferent, - DecodePaddingMode::RequireCanonical, - ] -} - -fn assert_all_suffixes_ok<E: Engine>(engine: E, suffixes: Vec<&str>) { - for num_prefix_quads in 0..256 { - for &suffix in suffixes.iter() { - let mut encoded = "AAAA".repeat(num_prefix_quads); - encoded.push_str(suffix); - - let res = &engine.decode(&encoded); - assert!(res.is_ok()); - } - } -} |
