use crate::error::{PEMError, X509Error};
use crate::x509::X509Certificate;
use crate::x509_parser::parse_x509_der;
use base64;
use nom::{Err, IResult};
use std::io::{BufRead, Cursor, Seek};
#[derive(PartialEq, Debug)]
pub struct Pem {
pub label: String,
pub contents: Vec<u8>,
}
pub fn pem_to_der<'a>(i: &'a [u8]) -> IResult<&'a [u8], Pem, PEMError> {
let reader = Cursor::new(i);
let res = Pem::read(reader);
match res {
Ok((pem, bytes_read)) => Ok((&i[bytes_read..], pem)),
Err(e) => Err(Err::Error(e)),
}
}
impl Pem {
pub fn read(mut r: impl BufRead + Seek) -> Result<(Pem, usize), PEMError>
{
let mut line = String::new();
let label = loop {
let num_bytes = r.read_line(&mut line).or(Err(PEMError::MissingHeader))?;
if num_bytes == 0 { return Err(PEMError::MissingHeader);
}
if !line.starts_with("-----BEGIN ") {
line.clear();
continue;
}
let mut iter = line.split_whitespace();
let label = iter.nth(1).ok_or(PEMError::InvalidHeader)?;
break label;
};
let label = label.split('-').next().ok_or(PEMError::InvalidHeader)?;
let mut s = String::new();
loop {
let mut l = String::new();
let num_bytes = r.read_line(&mut l)?;
if num_bytes == 0 {
return Err(PEMError::IncompletePEM);
}
if l.starts_with("-----END ") {
break;
}
s.push_str(l.trim_end());
}
let contents = base64::decode(&s).or(Err(PEMError::Base64DecodeError))?;
let pem = Pem {
label: label.to_string(),
contents,
};
Ok((pem, r.seek(std::io::SeekFrom::Current(0))? as usize))
}
pub fn parse_x509(&self) -> Result<X509Certificate, ::nom::Err<X509Error>> {
parse_x509_der(&self.contents)
.map(|(_,x509)| x509)
}
}
#[test]
fn read_pem_from_file() {
let file = std::io::BufReader::new(std::fs::File::open("assets/certificate.pem").unwrap());
let subject = Pem::read(file).unwrap().0
.parse_x509().unwrap().tbs_certificate.subject.to_string();
assert_eq!(subject, "CN=lists.for-our.info");
}