rustls_pemfile/pemfile.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
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,
};
/// The contents of a single recognised block in a PEM file.
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum Item {
/// A DER-encoded x509 certificate.
///
/// Appears as "CERTIFICATE" in PEM files.
X509Certificate(CertificateDer<'static>),
/// A DER-encoded Subject Public Key Info; as specified in RFC 7468.
///
/// Appears as "PUBLIC KEY" in PEM files.
SubjectPublicKeyInfo(SubjectPublicKeyInfoDer<'static>),
/// A DER-encoded plaintext RSA private key; as specified in PKCS #1/RFC 3447
///
/// Appears as "RSA PRIVATE KEY" in PEM files.
Pkcs1Key(PrivatePkcs1KeyDer<'static>),
/// A DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958
///
/// Appears as "PRIVATE KEY" in PEM files.
Pkcs8Key(PrivatePkcs8KeyDer<'static>),
/// A Sec1-encoded plaintext private key; as specified in RFC 5915
///
/// Appears as "EC PRIVATE KEY" in PEM files.
Sec1Key(PrivateSec1KeyDer<'static>),
/// A Certificate Revocation List; as specified in RFC 5280
///
/// Appears as "X509 CRL" in PEM files.
Crl(CertificateRevocationListDer<'static>),
/// A Certificate Signing Request; as specified in RFC 2986
///
/// Appears as "CERTIFICATE REQUEST" in PEM files.
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,
}
}
}
/// Errors that may arise when parsing the contents of a PEM file
///
/// This differs from [`rustls_pki_types::pem::Error`] because it is `PartialEq`;
/// it is retained for compatibility.
#[derive(Debug, PartialEq)]
pub enum Error {
/// a section is missing its "END marker" line
MissingSectionEnd {
/// the expected "END marker" line that was not found
end_marker: Vec<u8>,
},
/// syntax error found in the line that starts a new section
IllegalSectionStart {
/// line that contains the syntax error
line: Vec<u8>,
},
/// base64 decode error
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),
// this is a necessary bodge to funnel any new errors into our existing type
// (to which we can add no new variants)
other => Error::Base64Decode(format!("{other:?}")),
}
}
}
/// Extract and decode the next PEM section from `input`
///
/// - `Ok(None)` is returned if there is no PEM section to read from `input`
/// - Syntax errors and decoding errors produce a `Err(...)`
/// - Otherwise each decoded section is returned with a `Ok(Some((Item::..., remainder)))` where
/// `remainder` is the part of the `input` that follows the returned section
pub fn read_one_from_slice(input: &[u8]) -> Result<Option<(Item, &[u8])>, Error> {
Item::from_slice(input).map_err(Into::into)
}
/// Extract and decode the next PEM section from `rd`.
///
/// - Ok(None) is returned if there is no PEM section read from `rd`.
/// - Underlying IO errors produce a `Err(...)`
/// - Otherwise each decoded section is returned with a `Ok(Some(Item::...))`
///
/// You can use this function to build an iterator, for example:
/// `for item in iter::from_fn(|| read_one(rd).transpose()) { ... }`
#[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(),
})
}
/// Extract and return all PEM sections by reading `rd`.
#[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())
}