|
|
|
@ -242,7 +242,7 @@ pub mod formats
|
|
|
|
|
/// A format spec that must satisfy both these format specs in order
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
|
pub struct BothFormat<T, U = AnyFormat>(PhantomData<(T, U)>)
|
|
|
|
|
where T: FormatSpec, U: FormatSpec;
|
|
|
|
|
where T: FormatSpec, U: FormatSpec;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum MultiFormatError<T,U>
|
|
|
|
@ -379,9 +379,61 @@ pub mod formats
|
|
|
|
|
type Error = PEMFormatError;
|
|
|
|
|
fn validate(_s: &str) -> Result<(), Self::Error> {
|
|
|
|
|
todo!("Learn the PEM ciphertext format");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
|
|
|
|
pub enum Base64Format{}
|
|
|
|
|
|
|
|
|
|
impl Base64Format
|
|
|
|
|
{
|
|
|
|
|
const CHARSET_STR: &'static str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FormatSpec for Base64Format
|
|
|
|
|
{
|
|
|
|
|
type Error = InvalidBase64Error;
|
|
|
|
|
|
|
|
|
|
fn validate(s: &str) -> Result<(), Self::Error> {
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref CHARSET: [bool; 256] = {
|
|
|
|
|
let mut map = [false; 256];
|
|
|
|
|
for byte in Base64Format::CHARSET_STR.bytes()
|
|
|
|
|
{
|
|
|
|
|
map[byte as usize] = true;
|
|
|
|
|
}
|
|
|
|
|
map
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
let mut iter = s.as_bytes().chunks(4).peekable();
|
|
|
|
|
while let Some(window) = iter.next()
|
|
|
|
|
{
|
|
|
|
|
let is_last = iter.peek().is_none();
|
|
|
|
|
// eprintln!("Window: {:?} ({})", std::str::from_utf8(window), is_last);
|
|
|
|
|
for byte in window.iter().copied()
|
|
|
|
|
{
|
|
|
|
|
if !(CHARSET[byte as usize] || (is_last && byte == b'=')) {
|
|
|
|
|
return Err(InvalidBase64Error(byte as char));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// On invalid base64 string
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct InvalidBase64Error(char);
|
|
|
|
|
|
|
|
|
|
impl error::Error for InvalidBase64Error{}
|
|
|
|
|
impl fmt::Display for InvalidBase64Error
|
|
|
|
|
{
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
|
|
|
{
|
|
|
|
|
write!(f, "invalid char {:?} in base64 string", self.0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub type MaxLenStr<const MAX: usize> = FormattedStr<MaxLenFormat<MAX>>;
|
|
|
|
|
pub type MaxLenString<const MAX: usize> = FormattedString<MaxLenFormat<MAX>>;
|
|
|
|
@ -389,9 +441,12 @@ pub mod formats
|
|
|
|
|
pub type HexFormattedStr = FormattedStr<HexFormat>;
|
|
|
|
|
pub type HexFormattedString = FormattedString<HexFormat>;
|
|
|
|
|
|
|
|
|
|
pub type Base64FormattedStr = FormattedStr<Base64Format>;
|
|
|
|
|
pub type Base64FormattedString = FormattedString<Base64Format>;
|
|
|
|
|
|
|
|
|
|
pub type PEMFormattedStr = FormattedStr<PEMFormat>;
|
|
|
|
|
pub type PEMFormattedString = FormattedString<PEMFormat>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests
|
|
|
|
|
{
|
|
|
|
@ -402,6 +457,20 @@ pub mod formats
|
|
|
|
|
let _invalid = HexFormattedStr::new("ab120982039840i ").expect_err("Invalidation");
|
|
|
|
|
let _valid = HexFormattedStr::new("abc123982095830495adcfDD").expect("Validation");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn base64()
|
|
|
|
|
{
|
|
|
|
|
let mut random = [0u8; 256];
|
|
|
|
|
for len in 9..101
|
|
|
|
|
{
|
|
|
|
|
getrandom::getrandom(&mut random[..len]).expect("rng");
|
|
|
|
|
let encoded = base64::encode(&random[..len]);
|
|
|
|
|
println!("String: {}", encoded);
|
|
|
|
|
let _encoded = Base64FormattedString::new(encoded).expect("Encode validate failed");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|