use crate::Result;
const PAD: u8 = 64; const INV: u8 = 99;
static BASE64_INDICES: &'static [u8] = &[
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, 62, INV, INV, INV, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, INV, INV, INV, PAD, INV, INV,
INV, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, INV, INV, INV, INV, INV,
INV, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
];
enum Base64Value {
Some(u8),
Pad,
None,
}
fn next_valid_base64_value(iter: &mut dyn Iterator<Item=&u8>) -> Base64Value {
while let Some(c) = iter.next() {
let b = BASE64_INDICES[*c as usize];
if b < PAD {
return Base64Value::Some(b);
}
if b == PAD {
return Base64Value::Pad;
}
}
return Base64Value::None;
}
pub fn base64_decode_into_buf(input: &[u8], output: &mut Vec<u8>) -> Result<()> {
let mut iter = input.iter();
let expected_paddings =
loop {
let c0 = match next_valid_base64_value(&mut iter) {
Base64Value::Some(c) => c,
Base64Value::Pad => return Err("Invalid base64 padding".into()),
Base64Value::None => return Ok(()),
};
let c1 = match next_valid_base64_value(&mut iter) {
Base64Value::Some(c) => { output.push((c0 << 2) | ((c & 0x3f) >> 4)); c }
Base64Value::Pad => return Err("Invalid base64 padding".into()),
Base64Value::None => return Err("Invalid base64 encoding".into()),
};
let c2 = match next_valid_base64_value(&mut iter) {
Base64Value::Some(c) => { output.push((c1 << 4) | ((c & 0x3f) >> 2)); c }
Base64Value::Pad => break 1,
Base64Value::None => return Err("Invalid base64 padding".into()),
};
match next_valid_base64_value(&mut iter) {
Base64Value::Some(c) => { output.push((c2 << 6) | ((c & 0x3f))); }
Base64Value::Pad => break 0,
Base64Value::None => return Err("Invalid base64 padding".into()),
};
};
let mut found_paddings = 0;
while let Some(c) = iter.next() {
if *c == b'=' {
found_paddings += 1;
continue;
}
let b = BASE64_INDICES[*c as usize];
if b < PAD {
return Err("Unexpected characters after base64 padding".into());
}
}
if found_paddings != expected_paddings {
return Err("Invalid base64 padding".into());
}
Ok(())
}
fn hexdigit_to_num(mut a: u8) -> Option<u8> {
if a.is_ascii_digit() {
return Some(a - b'0');
}
a.make_ascii_lowercase();
if a >= b'a' && a <= b'f' {
return Some(a - b'a' + 10);
}
None
}
pub fn qp_decode_into_buf(input: &[u8], output: &mut Vec<u8>) -> Result<()> {
let mut iter = input.iter().peekable();
'outer: loop {
loop {
match iter.next() {
Some(b'=') => break,
Some(c) => output.push(*c),
None => break 'outer,
}
}
if let Some(&first) = iter.next() {
if first == b'\r' {
if iter.peek() == Some(&&b'\n') {
iter.next();
continue;
}
} else if first == b'\n' {
continue;
} else if let Some(first_num) = hexdigit_to_num(first) {
if let Some(&&second) = iter.peek() {
if let Some(second_num) = hexdigit_to_num(second) {
output.push(first_num * 16 + second_num);
iter.next();
continue;
}
}
}
output.extend(&[b'=', first]);
} else {
output.push(b'=');
}
}
Ok(())
}
#[cfg(test)]
mod test_base64 {
use crate::decode::base64_decode_into_buf;
#[test]
fn decodes_full_length() {
let mut decoded = Vec::new();
assert!(base64_decode_into_buf("YWJj".as_bytes(), &mut decoded).is_ok());
assert_eq!(decoded, &[b'a', b'b', b'c']);
}
#[test]
fn decodes_with_two_padding() {
let mut decoded = Vec::new();
assert!(base64_decode_into_buf("YWJjZA==".as_bytes(), &mut decoded).is_ok());
assert_eq!(decoded, &[b'a', b'b', b'c', b'd']);
}
#[test]
fn decodes_with_one_padding() {
let mut decoded = Vec::new();
assert!(base64_decode_into_buf("YWJjZGU=".as_bytes(), &mut decoded).is_ok());
assert_eq!(decoded, &[b'a', b'b', b'c', b'd', b'e']);
}
#[test]
fn decodes_with_ignored_characters() {
let mut decoded = Vec::new();
assert!(base64_decode_into_buf(" Y\t WJ\njZA=\r\n = ".as_bytes(), &mut decoded).is_ok());
assert_eq!(decoded, &[b'a', b'b', b'c', b'd']);
}
#[test]
fn error_with_invalid_paddings() {
let mut decoded = Vec::new();
assert!(base64_decode_into_buf("YWJj====".as_bytes(), &mut decoded).is_err());
assert!(base64_decode_into_buf("YWJjZ===".as_bytes(), &mut decoded).is_err());
assert!(base64_decode_into_buf("====".as_bytes(), &mut decoded).is_err());
}
#[test]
fn error_with_unpadded_input() {
let mut decoded = Vec::new();
assert!(base64_decode_into_buf("YWJjZA=".as_bytes(), &mut decoded).is_err());
}
#[test]
fn error_with_characters_after_padding() {
let mut decoded = Vec::new();
assert!(base64_decode_into_buf("YWJjZA=a".as_bytes(), &mut decoded).is_err());
assert!(base64_decode_into_buf("YWJjZA==b=".as_bytes(), &mut decoded).is_err());
}
}
#[cfg(test)]
mod test_qp {
use crate::decode::qp_decode_into_buf;
#[test]
fn decodes_byte() {
let mut decoded = Vec::new();
assert!(qp_decode_into_buf("a=62c=64".as_bytes(), &mut decoded).is_ok());
assert_eq!(decoded, &[b'a', b'b', b'c', b'd']);
}
#[test]
fn decodes_soft_break() {
let mut decoded = Vec::new();
assert!(qp_decode_into_buf("a=\r\nb=\nc".as_bytes(), &mut decoded).is_ok());
assert_eq!(decoded, &[b'a', b'b', b'c']);
}
#[test]
fn invalid_sequences_are_untouched() {
let mut decoded = Vec::new();
let invalid_sequence = "a=6t= c=".as_bytes();
assert!(qp_decode_into_buf(invalid_sequence, &mut decoded).is_ok());
assert_eq!(decoded, invalid_sequence);
}
}