Skip to content

Commit

Permalink
add an opt-out std feature
Browse files Browse the repository at this point in the history
  • Loading branch information
japaric committed Nov 9, 2023
1 parent a66deb3 commit 089237b
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 4 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: x86_64-unknown-none

- name: cargo test (debug)
run: cargo test
Expand All @@ -60,3 +61,10 @@ jobs:
run: cargo test --release
env:
RUSTFLAGS: "-D warnings"

# this target does _not_ include the libstd crate in its sysroot
# it will catch unwanted usage of libstd in _dependencies_
- name: cargo build no-std mode
run: cargo build --no-default-features --target x86_64-unknown-none
env:
RUSTFLAGS: "-D warnings"
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@ repository = "https://github.com/rustls/pemfile"
categories = ["network-programming", "cryptography"]

[dependencies]
base64 = "0.21"
base64 = { version = "0.21", default-features = false, features = ["alloc"] }
pki-types = { package = "rustls-pki-types", version = "0.2" }

[dev-dependencies]
bencher = "0.1.5"

[features]
default = ["std"]
std = ["base64/std"]

[[bench]]
name = "benchmark"
harness = false

[[test]]
name = "integration"
required-features = ["std"]
27 changes: 24 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@
//! - Use `certs()` to extract just the certificates (silently discarding other sections), and
//! similarly for `rsa_private_keys()` and `pkcs8_private_keys()`.
//!
//! # no-std support
//!
//! The opt-out "std" Cargo feature can be disabled to put this crate in no-std mode.
//!
//! In no-std mode, the `read_one_from_slice` API can be used to parse a .pem file that has already
//! been loaded into memory.
//!
//! ## Example code
//! ```
#![cfg_attr(feature = "std", doc = "```")]
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
//! use std::iter;
//! use rustls_pemfile::{Item, read_one};
//! # let mut reader = std::io::BufReader::new(&b"junk\n-----BEGIN RSA PRIVATE KEY-----\nqw\n-----END RSA PRIVATE KEY-----\n"[..]);
Expand Down Expand Up @@ -43,29 +51,37 @@
#![cfg_attr(not(test), no_std)]

extern crate alloc;
#[cfg(not(test))]
#[cfg(all(feature = "std", not(test)))]
extern crate std;

#[cfg(test)]
#[cfg(feature = "std")]
mod tests;

/// --- Main crate APIs:
mod pemfile;
pub use pemfile::{read_all, read_one, read_one_from_slice, Error, Item};
#[cfg(feature = "std")]
pub use pemfile::{read_all, read_one};
pub use pemfile::{read_one_from_slice, Error, Item};
#[cfg(feature = "std")]
use pki_types::PrivateKeyDer;
#[cfg(feature = "std")]
use pki_types::{
CertificateDer, CertificateRevocationListDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer,
PrivateSec1KeyDer,
};

#[cfg(feature = "std")]
use core::iter;
/// --- Legacy APIs:
#[cfg(feature = "std")]
use std::io;

/// Return an iterator over certificates from `rd`.
///
/// Filters out any PEM sections that are not certificates and yields errors if a problem
/// occurs while trying to extract a certificate.
#[cfg(feature = "std")]
pub fn certs(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<CertificateDer<'static>, io::Error>> + '_ {
Expand All @@ -80,6 +96,7 @@ pub fn certs(
///
/// Yields the first PEM section describing a private key (of any type), or an error if a
/// problem occurs while trying to read PEM sections.
#[cfg(feature = "std")]
pub fn private_key(rd: &mut dyn io::BufRead) -> Result<Option<PrivateKeyDer<'static>>, io::Error> {
for result in iter::from_fn(move || read_one(rd).transpose()) {
match result? {
Expand All @@ -97,6 +114,7 @@ pub fn private_key(rd: &mut dyn io::BufRead) -> Result<Option<PrivateKeyDer<'sta
///
/// Filters out any PEM sections that are not CRLs and yields errors if a problem occurs
/// while trying to extract a CRL.
#[cfg(feature = "std")]
pub fn crls(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<CertificateRevocationListDer<'static>, io::Error>> + '_ {
Expand All @@ -111,6 +129,7 @@ pub fn crls(
///
/// Filters out any PEM sections that are not RSA private keys and yields errors if a problem
/// occurs while trying to extract an RSA private key.
#[cfg(feature = "std")]
pub fn rsa_private_keys(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<PrivatePkcs1KeyDer<'static>, io::Error>> + '_ {
Expand All @@ -125,6 +144,7 @@ pub fn rsa_private_keys(
///
/// Filters out any PEM sections that are not PKCS8-encoded private keys and yields errors if a
/// problem occurs while trying to extract an RSA private key.
#[cfg(feature = "std")]
pub fn pkcs8_private_keys(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<PrivatePkcs8KeyDer<'static>, io::Error>> + '_ {
Expand All @@ -139,6 +159,7 @@ pub fn pkcs8_private_keys(
///
/// Filters out any PEM sections that are not SEC1-encoded EC private keys and yields errors if a
/// problem occurs while trying to extract a SEC1-encoded EC private key.
#[cfg(feature = "std")]
pub fn ec_private_keys(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<PrivateSec1KeyDer<'static>, io::Error>> + '_ {
Expand Down
5 changes: 5 additions & 0 deletions src/pemfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use alloc::borrow::ToOwned;
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
#[cfg(feature = "std")]
use core::iter;
use core::ops::ControlFlow;
#[cfg(feature = "std")]
use std::io::{self, ErrorKind};

use pki_types::{
Expand Down Expand Up @@ -94,6 +96,7 @@ pub fn read_one_from_slice(mut input: &[u8]) -> Result<Option<(Item, &[u8])>, Er
///
/// 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> {
let mut b64buf = Vec::with_capacity(1024);
let mut section = None::<(Vec<_>, Vec<_>)>;
Expand Down Expand Up @@ -221,6 +224,7 @@ fn read_one_impl(

// Ported from https://github.com/rust-lang/rust/blob/91cfcb021935853caa06698b759c293c09d1e96a/library/std/src/io/mod.rs#L1990 and
// modified to look for our accepted newlines.
#[cfg(feature = "std")]
fn read_until_newline<R: io::BufRead + ?Sized>(
r: &mut R,
buf: &mut Vec<u8>,
Expand Down Expand Up @@ -257,6 +261,7 @@ fn read_until_newline<R: io::BufRead + ?Sized>(
}

/// 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())
}
Expand Down

0 comments on commit 089237b

Please sign in to comment.