pretty-good 0.2.2

RFC4880 (OpenPGP Message Format) library
Documentation
use failure::Error;
use nom::be_u8;
use nom::{ErrorKind, IResult};
use nom::Err as NomErr;

use types::*;

pub(crate) fn s2k_iv(inp: &[u8]) -> IResult<&[u8], [u8; 8]> {
    let mut out = [0u8; 8];

    let (remaining, slice) = match take!(inp, 8) {
        IResult::Done(remaining, slice) => (remaining, slice),
        IResult::Error(e) => return IResult::Error(e),
        IResult::Incomplete(i) => return IResult::Incomplete(i),
    };

    out.copy_from_slice(&slice[..8]);

    IResult::Done(remaining, out)
}

fn s2k_decode_count(c: u8) -> u32 {
    let c = c as u32;
    (16 + (c & 15)) << ((c >> 4) + 6)
}

named!(simple_s2k<StringToKey>,
    do_parse!(
        tag!(&[0u8]) >>
        hash_algo: map!(be_u8, HashAlgorithm::from) >>
        (StringToKey::Simple(hash_algo))
    )
);

named!(salted_s2k<StringToKey>,
    do_parse!(
        tag!(&[1u8]) >>
        hash_algo: map!(be_u8, HashAlgorithm::from) >>
        iv: s2k_iv >>
        (StringToKey::Salted(hash_algo, iv))
    )
);

named!(iterated_salted_s2k<StringToKey>,
    do_parse!(
        tag!(&[3u8]) >>
        hash_algo: map!(be_u8, HashAlgorithm::from) >>
        iv: s2k_iv >>
        count: map!(be_u8, s2k_decode_count) >>
        (StringToKey::IteratedSalted(hash_algo, iv, count))
    )
);

named!(pub s2k<StringToKey>, alt!(simple_s2k | salted_s2k | iterated_salted_s2k));

#[derive(Clone, Copy, Debug)]
pub enum StringToKey {
    Simple(HashAlgorithm),
    Salted(HashAlgorithm, [u8; 8]),
    IteratedSalted(HashAlgorithm, [u8; 8], u32),
}

impl StringToKey {
    pub fn from_bytes(bytes: &[u8]) -> Result<StringToKey, Error> {
        let (_, string_to_key) = match s2k(bytes) {
            IResult::Done(remaining, string_to_key) => (remaining, string_to_key),
            IResult::Error(NomErr::Code(ErrorKind::Custom(e))) => {
                let e = NomError::from(e);

                bail!(S2kError::InvalidFormat {
                    reason: format!("{:?}", e),
                })
            }
            IResult::Error(e) => bail!(S2kError::InvalidFormat {
                reason: format!("{}", e),
            }),
            IResult::Incomplete(i) => bail!(S2kError::InvalidFormat {
                reason: format!("{:?}", i),
            }),
        };

        Ok(string_to_key)
    }
}

#[derive(Debug, Fail)]
pub enum S2kError {
    #[fail(display = "Invalid string to key specifier: {}", reason)]
    InvalidFormat { reason: String },
}