use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
#[cfg(feature = "std")]
use core::iter;
#[cfg(feature = "std")]
use std::io::{self, ErrorKind};
use pki_types::{
pem, CertificateDer, CertificateRevocationListDer, CertificateSigningRequestDer,
PrivatePkcs1KeyDer, PrivatePkcs8KeyDer, PrivateSec1KeyDer, SubjectPublicKeyInfoDer,
};
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum Item {
X509Certificate(CertificateDer<'static>),
SubjectPublicKeyInfo(SubjectPublicKeyInfoDer<'static>),
Pkcs1Key(PrivatePkcs1KeyDer<'static>),
Pkcs8Key(PrivatePkcs8KeyDer<'static>),
Sec1Key(PrivateSec1KeyDer<'static>),
Crl(CertificateRevocationListDer<'static>),
Csr(CertificateSigningRequestDer<'static>),
}
impl Item {
#[cfg(feature = "std")]
fn from_buf(rd: &mut dyn io::BufRead) -> Result<Option<Self>, pem::Error> {
loop {
match pem::from_buf(rd)? {
Some((kind, data)) => match Self::from_kind(kind, data) {
Some(item) => return Ok(Some(item)),
None => continue,
},
None => return Ok(None),
}
}
}
fn from_slice(pem: &[u8]) -> Result<Option<(Self, &[u8])>, pem::Error> {
let mut iter = <(pem::SectionKind, Vec<u8>) as pem::PemObject>::pem_slice_iter(pem);
for found in iter.by_ref() {
match found {
Ok((kind, data)) => match Self::from_kind(kind, data) {
Some(item) => return Ok(Some((item, iter.remainder()))),
None => continue,
},
Err(err) => return Err(err.into()),
}
}
Ok(None)
}
fn from_kind(kind: pem::SectionKind, data: Vec<u8>) -> Option<Self> {
use pem::SectionKind::*;
match kind {
Certificate => Some(Self::X509Certificate(data.into())),
PublicKey => Some(Self::SubjectPublicKeyInfo(data.into())),
RsaPrivateKey => Some(Self::Pkcs1Key(data.into())),
PrivateKey => Some(Self::Pkcs8Key(data.into())),
EcPrivateKey => Some(Self::Sec1Key(data.into())),
Crl => Some(Self::Crl(data.into())),
Csr => Some(Self::Csr(data.into())),
_ => None,
}
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
MissingSectionEnd {
end_marker: Vec<u8>,
},
IllegalSectionStart {
line: Vec<u8>,
},
Base64Decode(String),
}
#[cfg(feature = "std")]
impl From<Error> for io::Error {
fn from(error: Error) -> Self {
match error {
Error::MissingSectionEnd { end_marker } => io::Error::new(
ErrorKind::InvalidData,
format!(
"section end {:?} missing",
String::from_utf8_lossy(&end_marker)
),
),
Error::IllegalSectionStart { line } => io::Error::new(
ErrorKind::InvalidData,
format!(
"illegal section start: {:?}",
String::from_utf8_lossy(&line)
),
),
Error::Base64Decode(err) => io::Error::new(ErrorKind::InvalidData, err),
}
}
}
impl From<pem::Error> for Error {
fn from(error: pem::Error) -> Self {
match error {
pem::Error::MissingSectionEnd { end_marker } => Error::MissingSectionEnd { end_marker },
pem::Error::IllegalSectionStart { line } => Error::IllegalSectionStart { line },
pem::Error::Base64Decode(str) => Error::Base64Decode(str),
other => Error::Base64Decode(format!("{other:?}")),
}
}
}
pub fn read_one_from_slice(input: &[u8]) -> Result<Option<(Item, &[u8])>, Error> {
Item::from_slice(input).map_err(Into::into)
}
#[cfg(feature = "std")]
pub fn read_one(rd: &mut dyn io::BufRead) -> Result<Option<Item>, io::Error> {
Item::from_buf(rd).map_err(|err| match err {
pem::Error::Io(io) => io,
other => Error::from(other).into(),
})
}
#[cfg(feature = "std")]
pub fn read_all(rd: &mut dyn io::BufRead) -> impl Iterator<Item = Result<Item, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose())
}