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}