diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24c3c06..f83db61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 5a5d0bb..4b11c06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/src/lib.rs b/src/lib.rs index 667edb2..9c5f993 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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"[..]); @@ -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, io::Error>> + '_ { @@ -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>, io::Error> { for result in iter::from_fn(move || read_one(rd).transpose()) { match result? { @@ -97,6 +114,7 @@ pub fn private_key(rd: &mut dyn io::BufRead) -> Result impl Iterator, io::Error>> + '_ { @@ -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, io::Error>> + '_ { @@ -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, io::Error>> + '_ { @@ -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, io::Error>> + '_ { diff --git a/src/pemfile.rs b/src/pemfile.rs index c09bc8c..d60da0a 100644 --- a/src/pemfile.rs +++ b/src/pemfile.rs @@ -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::{ @@ -94,6 +96,7 @@ pub fn read_one_from_slice(mut input: &[u8]) -> Result, 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, io::Error> { let mut b64buf = Vec::with_capacity(1024); let mut section = None::<(Vec<_>, Vec<_>)>; @@ -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: &mut R, buf: &mut Vec, @@ -257,6 +261,7 @@ fn read_until_newline( } /// Extract and return all PEM sections by reading `rd`. +#[cfg(feature = "std")] pub fn read_all(rd: &mut dyn io::BufRead) -> impl Iterator> + '_ { iter::from_fn(move || read_one(rd).transpose()) }