nom_pem 4.0.0

PEM parser (rfc1421) based of nom
Documentation
#[cfg(not(std))]
use core::str;
#[cfg(std)]
use std::str;
#[cfg(not(std))]
use core::fmt;
#[cfg(std)]
use std::fmt;


use nom::{IResult, Err, Needed, is_space};

#[derive(Debug, PartialEq)]
pub enum HeaderEntry<'a> {
    ProcType(u8, ProcTypeType),
    DEKInfo(RFC1423Algorithm, Vec<u8>),
    Entry(&'a str, Vec<String>),
}

#[repr(u8)]
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum ProcTypeType {
    ENCRYPTED,
    MIC_ONLY,
    MIC_CLEAR,
    CRL,
}

#[repr(u8)]
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum RFC1423Algorithm {
    DES_CBC,
    DES_EDE3_CBC,
    AES_128_CBC,
    AES_192_CBC,
    AES_256_CBC,
}

named!(pub pem_header<HeaderEntry>,  alt!(
     pem_header_proctype |
     pem_header_dekinfo |
     pem_header_key_value
     ));

use RFC1423Algorithm::*;

impl RFC1423Algorithm {
    pub fn key_size(&self) -> usize {
        match *self {
            DES_CBC => 8,
            DES_EDE3_CBC => 24,
            AES_128_CBC => 16,
            AES_192_CBC => 16,
            AES_256_CBC => 16
        }
    }
    pub fn block_size(&self) -> usize {
        match *self {
            DES_CBC => 8,
            DES_EDE3_CBC => 8,
            AES_128_CBC => 16,
            AES_192_CBC => 16,
            AES_256_CBC => 16
        }
    }
}

pub fn is_pem_header_key_char(c: u8) -> bool { (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A) || c == 45 }
named!(pub pem_header_key<&str>,map_res!(take_while!(is_pem_header_key_char),str::from_utf8));

named!(pub pem_header_value<Vec<String>>,  do_parse!(
    value: alt!(
        multi_line |
        str_end_of_line
    ) >>
    ({
        let str = &value;
        let v1 : Vec<&str> = str.split(",").collect();
        let v2 : Vec<String>= v1.iter().map(|&str| {String::from(str)}).collect();
        v2
    })
    ));



use nom::digit;
named!(u8_digit<u8>,map_res!(map_res!(digit,str::from_utf8),str::FromStr::from_str));
named!(pub pem_header_proctype<HeaderEntry>,  do_parse!(
    tag!("Proc-Type:") >>
    spaces >>
    code: u8_digit >>
    tag!(",") >>
    t: pem_proctype >>
    _value: pem_header_value >>
    (HeaderEntry::ProcType(code, t))
    ));

named!(pub pem_header_dekinfo<HeaderEntry>,  do_parse!(
    tag!("DEK-Info:") >>
    spaces >>
    alg: pem_rfc1423_algorithm >>
    tag!(",") >>
    data: parse_hex >>
    str_end_of_line >>
    (HeaderEntry::DEKInfo(alg, data))
    ));

named!(pub pem_proctype<ProcTypeType>,  alt!(
    do_parse!(tag!("ENCRYPTED") >> (ProcTypeType::ENCRYPTED)) |
    do_parse!(tag!("MIC-ONLY")  >> (ProcTypeType::MIC_ONLY)) |
    do_parse!(tag!("MIC-CLEAR") >> (ProcTypeType::MIC_CLEAR)) |
    do_parse!(tag!("CRL")       >> (ProcTypeType::CRL))
    ));

named!(pem_rfc1423_algorithm<RFC1423Algorithm>, alt!(
     do_parse!(tag!("DES-CBC")      >> (DES_CBC)) |
     do_parse!(tag!("DES-EDE3-CBC") >> (DES_EDE3_CBC)) |
     do_parse!(tag!("AES-128-CBC")  >> (AES_128_CBC)) |
     do_parse!(tag!("AES-192-CBC")  >> (AES_192_CBC)) |
     do_parse!(tag!("AES-256-CBC")  >> (AES_256_CBC))
    ));

named!(pub pem_header_key_value<HeaderEntry>,  do_parse!(
    key: pem_header_key >>
    tag!(":") >>
    spaces >>
    values: pem_header_value >>
    (HeaderEntry::Entry(key, values))
    ));

named!(pub pem_headers<Vec<HeaderEntry>>, do_parse!(
    headers: many1!(pem_header) >>
    tag!("\n") >>
    (headers)
    ));

pub fn parse_hex(i: &[u8]) -> ::nom::IResult<&[u8], Vec<u8>> {
    let mut high = true;
    let mut register = 0u8;
    let mut ret: Vec<u8> = Vec::new();
    let mut pos = 0;
    const T1: u8 = ('1' as u8) - 1;
    const T2: u8 = ('A' as u8) - 1;
    const T3: u8 = ('a' as u8) - 1;
    while pos < i.len() {
        let c = i[pos];
        let b: u8 = if c == ('0' as u8) {
            0
        } else if (c > T1) & (c <= ('9' as u8)) {
            c - T1
        } else if (c > T2) & (c <= ('F' as u8)) {
            c - T2 + 9
        } else if (c > T3) & (c <= ('f' as u8)) {
            c - T3 + 9
        } else {
            break;
        };
        if high {
            register = b << 4;
            high = false;
        } else {
            ret.push(register | b);
            high = true;
        }
        pos += 1;
    }
    if high {
        return Ok((&i[pos..], ret));
    } else {
        return Err(Err::Incomplete(Needed::Size(1)));
    }
}

impl fmt::Display for RFC1423Algorithm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DES_CBC => write!(f, "DES-CBC"),
            DES_EDE3_CBC => write!(f, "DES-EDE3-CBC"),
            AES_128_CBC => write!(f, "AES-128-CBC"),
            AES_192_CBC => write!(f, "AES-192-CBC"),
            AES_256_CBC => write!(f, "AES-256-CBC")
        }
    }
}

impl<'a> fmt::Display for HeaderEntry<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &HeaderEntry::ProcType(ref l, ref t) => { write!(f, "Proc-Type: {},{:?}", l, t) }
            &HeaderEntry::DEKInfo(ref alg, ref v) => {
                write!(f, "DEK-Info: {},", alg)?;
                write_hex(f, &v)
            }
            &HeaderEntry::Entry(ref key, ref values) => {
                write!(f, "{}: ", key)?;
                let mut pos: usize = key.len() + 2;
                for (i, v) in values.iter().enumerate() {
                    if i > 0 {
                        write!(f, ",")?;
                        pos += 1
                    }
                    if (v.len() + pos) <= 65 {
                        write!(f, "{}", v)?;
                    } else {
                        let mut s: &str = v;
                        while s.len() > 64 {
                            let (head, tail) = s.split_at(64);
                            write!(f, "\n {}", head)?;
                            s = tail;
                        }
                        write!(f, "\n {}", s)?;
                        pos = s.len() + 1;
                    }
                }
                write!(f, "")
            }
        }
    }
}

fn write_hex(f: &mut fmt::Formatter, data: &[u8]) -> fmt::Result {
    for i in data.iter() {
        write_hex_char(f, i >> 4)?;
        write_hex_char(f, i & 0b1111)?;
    }
    Ok(())
}

fn write_hex_char(f: &mut fmt::Formatter, b: u8) -> fmt::Result {
    const T2: u8 = ('A' as u8) - 10;
    if b > 9 {
        write!(f, "{}", (b + T2) as char)
    } else {
        write!(f, "{}", b)
    }
}


#[cfg(test)]
#[test]
fn hex() {
    assert_eq!(Ok((&[45][..], vec!(0u8, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f))),
               parse_hex(b"000102030405060708090a0b0c0d0e0f-"));
    assert_eq!(Ok((&[45][..], vec!(0x11u8, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff))),
               parse_hex(b"112233445566778899AABBCCDDEEFF-"));
    assert_eq!(Err(Err::Incomplete(Needed::Size(1))),
               parse_hex(b"112233445566778899AABBCCDDEEFFF-"));
}


named!(pub str_end_of_line<String>, do_parse!(
s: map_res!(map_res!(take_till!(is_nl),str::from_utf8),str::FromStr::from_str) >>
take!(1) >>
(s)
));

named!(pub str_second_line<String>, do_parse!(
tag!(" ") >>  // all following lines of a header value has to start with a space
spaces >>
s: str_end_of_line >>
(s)
));



named!(pub multi_line<String>, do_parse!(
s1: str_end_of_line >>
s: fold_many1!(str_second_line, s1, |a: String,b: String| {
    a+&b
 }) >>
(s)
));


#[inline(always)]
fn spaces(i: &[u8]) -> IResult<&[u8], ()> {
    for pos in 0..i.len() {
        let b = i[pos];
        if is_space(b) {
            continue;
        }
        return Ok((&i[pos..], ()));
    }
    Ok((i, ()))
}

#[inline]
pub fn is_nl(chr: u8) -> bool {
    chr == 10
}