rustls_pemfile/
lib.rs

1//! # rustls-pemfile
2//! A basic parser for .pem files containing cryptographic keys and certificates.
3//!
4//! The input to this crate is a .pem file containing potentially many sections,
5//! and the output is those sections as alleged DER-encodings.  This crate does
6//! not decode the actual DER-encoded keys/certificates.
7//!
8//! ## Quick start
9//! Starting with an `io::BufRead` containing the file to be read:
10//! - Use `read_all()` to ingest the whole file, then work through the contents in-memory, or,
11//! - Use `read_one()` to stream through the file, processing the items as found, or,
12//! - Use `certs()` to extract just the certificates (silently discarding other sections), and
13//!   similarly for `rsa_private_keys()` and `pkcs8_private_keys()`.
14//!
15//! # no-std support
16//!
17//! The opt-out "std" Cargo feature can be disabled to put this crate in no-std mode.
18//!
19//! In no-std mode, the `read_one_from_slice` API can be used to parse a .pem file that has already
20//! been loaded into memory.
21//!
22//! ## Example code
23#![cfg_attr(feature = "std", doc = "```")]
24#![cfg_attr(not(feature = "std"), doc = "```ignore")]
25//! use std::iter;
26//! use rustls_pemfile::{Item, read_one};
27//! # let mut reader = std::io::BufReader::new(&b"junk\n-----BEGIN RSA PRIVATE KEY-----\nqw\n-----END RSA PRIVATE KEY-----\n"[..]);
28//! // Assume `reader` is any std::io::BufRead implementor
29//! for item in iter::from_fn(|| read_one(&mut reader).transpose()) {
30//!     match item.unwrap() {
31//!         Item::X509Certificate(cert) => println!("certificate {:?}", cert),
32//!         Item::Crl(crl) => println!("certificate revocation list: {:?}", crl),
33//!         Item::Csr(csr) => println!("certificate signing request: {:?}", csr),
34//!         Item::Pkcs1Key(key) => println!("rsa pkcs1 key {:?}", key),
35//!         Item::Pkcs8Key(key) => println!("pkcs8 key {:?}", key),
36//!         Item::Sec1Key(key) => println!("sec1 ec key {:?}", key),
37//!         _ => println!("unhandled item"),
38//!     }
39//! }
40//! ```
41
42// Require docs for public APIs, deny unsafe code, etc.
43#![forbid(unsafe_code, unused_must_use, unstable_features)]
44#![deny(
45    trivial_casts,
46    trivial_numeric_casts,
47    missing_docs,
48    unused_import_braces,
49    unused_extern_crates,
50    unused_qualifications
51)]
52#![no_std]
53
54extern crate alloc;
55#[cfg(any(feature = "std", test))]
56extern crate std;
57
58#[cfg(test)]
59#[cfg(feature = "std")]
60mod tests;
61
62mod pemfile;
63#[cfg(feature = "std")]
64use core::iter;
65/// --- Legacy APIs:
66#[cfg(feature = "std")]
67use std::io;
68
69#[cfg(feature = "std")]
70pub use pemfile::{read_all, read_one};
71pub use pemfile::{read_one_from_slice, Error, Item};
72#[cfg(feature = "std")]
73use pki_types::PrivateKeyDer;
74#[cfg(feature = "std")]
75use pki_types::{
76    CertificateDer, CertificateRevocationListDer, CertificateSigningRequestDer, PrivatePkcs1KeyDer,
77    PrivatePkcs8KeyDer, PrivateSec1KeyDer, SubjectPublicKeyInfoDer,
78};
79
80/// Return an iterator over certificates from `rd`.
81///
82/// Filters out any PEM sections that are not certificates and yields errors if a problem
83/// occurs while trying to extract a certificate.
84#[cfg(feature = "std")]
85pub fn certs(
86    rd: &mut dyn io::BufRead,
87) -> impl Iterator<Item = Result<CertificateDer<'static>, io::Error>> + '_ {
88    iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
89        Ok(Item::X509Certificate(cert)) => Some(Ok(cert)),
90        Err(err) => Some(Err(err)),
91        _ => None,
92    })
93}
94
95/// Return the first private key found in `rd`.
96///
97/// Yields the first PEM section describing a private key (of any type), or an error if a
98/// problem occurs while trying to read PEM sections.
99#[cfg(feature = "std")]
100pub fn private_key(rd: &mut dyn io::BufRead) -> Result<Option<PrivateKeyDer<'static>>, io::Error> {
101    for result in iter::from_fn(move || read_one(rd).transpose()) {
102        match result? {
103            Item::Pkcs1Key(key) => return Ok(Some(key.into())),
104            Item::Pkcs8Key(key) => return Ok(Some(key.into())),
105            Item::Sec1Key(key) => return Ok(Some(key.into())),
106            Item::X509Certificate(_)
107            | Item::SubjectPublicKeyInfo(_)
108            | Item::Crl(_)
109            | Item::Csr(_) => continue,
110        }
111    }
112
113    Ok(None)
114}
115
116/// Return the first certificate signing request (CSR) found in `rd`.
117///
118/// Yields the first PEM section describing a certificate signing request, or an error if a
119/// problem occurs while trying to read PEM sections.
120#[cfg(feature = "std")]
121pub fn csr(
122    rd: &mut dyn io::BufRead,
123) -> Result<Option<CertificateSigningRequestDer<'static>>, io::Error> {
124    for result in iter::from_fn(move || read_one(rd).transpose()) {
125        match result? {
126            Item::Csr(csr) => return Ok(Some(csr)),
127            Item::Pkcs1Key(_)
128            | Item::Pkcs8Key(_)
129            | Item::Sec1Key(_)
130            | Item::X509Certificate(_)
131            | Item::SubjectPublicKeyInfo(_)
132            | Item::Crl(_) => continue,
133        }
134    }
135
136    Ok(None)
137}
138
139/// Return an iterator certificate revocation lists (CRLs) from `rd`.
140///
141/// Filters out any PEM sections that are not CRLs and yields errors if a problem occurs
142/// while trying to extract a CRL.
143#[cfg(feature = "std")]
144pub fn crls(
145    rd: &mut dyn io::BufRead,
146) -> impl Iterator<Item = Result<CertificateRevocationListDer<'static>, io::Error>> + '_ {
147    iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
148        Ok(Item::Crl(crl)) => Some(Ok(crl)),
149        Err(err) => Some(Err(err)),
150        _ => None,
151    })
152}
153
154/// Return an iterator over RSA private keys from `rd`.
155///
156/// Filters out any PEM sections that are not RSA private keys and yields errors if a problem
157/// occurs while trying to extract an RSA private key.
158#[cfg(feature = "std")]
159pub fn rsa_private_keys(
160    rd: &mut dyn io::BufRead,
161) -> impl Iterator<Item = Result<PrivatePkcs1KeyDer<'static>, io::Error>> + '_ {
162    iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
163        Ok(Item::Pkcs1Key(key)) => Some(Ok(key)),
164        Err(err) => Some(Err(err)),
165        _ => None,
166    })
167}
168
169/// Return an iterator over PKCS8-encoded private keys from `rd`.
170///
171/// Filters out any PEM sections that are not PKCS8-encoded private keys and yields errors if a
172/// problem occurs while trying to extract an RSA private key.
173#[cfg(feature = "std")]
174pub fn pkcs8_private_keys(
175    rd: &mut dyn io::BufRead,
176) -> impl Iterator<Item = Result<PrivatePkcs8KeyDer<'static>, io::Error>> + '_ {
177    iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
178        Ok(Item::Pkcs8Key(key)) => Some(Ok(key)),
179        Err(err) => Some(Err(err)),
180        _ => None,
181    })
182}
183
184/// Return an iterator over SEC1-encoded EC private keys from `rd`.
185///
186/// Filters out any PEM sections that are not SEC1-encoded EC private keys and yields errors if a
187/// problem occurs while trying to extract a SEC1-encoded EC private key.
188#[cfg(feature = "std")]
189pub fn ec_private_keys(
190    rd: &mut dyn io::BufRead,
191) -> impl Iterator<Item = Result<PrivateSec1KeyDer<'static>, io::Error>> + '_ {
192    iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
193        Ok(Item::Sec1Key(key)) => Some(Ok(key)),
194        Err(err) => Some(Err(err)),
195        _ => None,
196    })
197}
198
199/// Return an iterator over SPKI-encoded keys from `rd`.
200///
201/// Filters out any PEM sections that are not SPKI-encoded public keys and yields errors if a
202/// problem occurs while trying to extract a SPKI-encoded public key.
203#[cfg(feature = "std")]
204pub fn public_keys(
205    rd: &mut dyn io::BufRead,
206) -> impl Iterator<Item = Result<SubjectPublicKeyInfoDer<'static>, io::Error>> + '_ {
207    iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
208        Ok(Item::SubjectPublicKeyInfo(key)) => Some(Ok(key)),
209        Err(err) => Some(Err(err)),
210        _ => None,
211    })
212}