Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to using the pki-types crate #24

Merged
merged 3 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ categories = ["network-programming", "cryptography"]

[dependencies]
base64 = "0.21"
pki-types = { package = "rustls-pki-types", version = "0.1" }

[dev-dependencies]
bencher = "0.1.5"
Expand Down
8 changes: 7 additions & 1 deletion benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ fn criterion_benchmark(c: &mut Bencher) {
c.iter(|| {
let data = include_bytes!("../tests/data/certificate.chain.pem");
let mut reader = BufReader::new(&data[..]);
assert_eq!(rustls_pemfile::certs(&mut reader).unwrap().len(), 3);
assert_eq!(
rustls_pemfile::certs(&mut reader)
.collect::<Result<Vec<_>, _>>()
.unwrap()
.len(),
3
);
});
}

Expand Down
101 changes: 48 additions & 53 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
//! match item.unwrap() {
//! Item::X509Certificate(cert) => println!("certificate {:?}", cert),
//! Item::Crl(crl) => println!("certificate revocation list: {:?}", crl),
//! Item::RSAKey(key) => println!("rsa pkcs1 key {:?}", key),
//! Item::PKCS8Key(key) => println!("pkcs8 key {:?}", key),
//! Item::ECKey(key) => println!("sec1 ec key {:?}", key),
//! Item::Pkcs1Key(key) => println!("rsa pkcs1 key {:?}", key),
//! Item::Pkcs8Key(key) => println!("pkcs8 key {:?}", key),
//! Item::Sec1Key(key) => println!("sec1 ec key {:?}", key),
//! _ => println!("unhandled item"),
//! }
//! }
Expand All @@ -47,91 +47,86 @@ mod tests;
/// --- Main crate APIs:
mod pemfile;
pub use pemfile::{read_all, read_one, Item};
use pki_types::{
CertificateDer, CertificateRevocationListDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer,
PrivateSec1KeyDer,
};

/// --- Legacy APIs:
use std::io;
use std::iter;

/// Extract all the certificates from `rd`, and return a vec of byte vecs
/// containing the der-format contents.
///
/// This function does not fail if there are no certificates in the file --
/// it returns an empty vector.
pub fn certs(rd: &mut dyn io::BufRead) -> Result<Vec<Vec<u8>>, io::Error> {
let mut certs = Vec::new();

loop {
match read_one(rd)? {
None => return Ok(certs),
Some(Item::X509Certificate(cert)) => certs.push(cert),
_ => {}
};
}
pub fn certs(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, these changes worked out well in the rustls tests.

rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<CertificateDer<'static>, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
Ok(Item::X509Certificate(cert)) => Some(Ok(cert)),
Err(err) => Some(Err(err)),
_ => None,
})
}

/// Extract all the certificate revocation lists (CRLs) from `rd`, and return a vec of byte vecs
/// containing the der-format contents.
///
/// This function does not fail if there are no CRLs in the file --
/// it returns an empty vector.
pub fn crls(rd: &mut dyn io::BufRead) -> Result<Vec<Vec<u8>>, io::Error> {
let mut crls = Vec::new();

loop {
match read_one(rd)? {
None => return Ok(crls),
Some(Item::Crl(crl)) => crls.push(crl),
_ => {}
};
}
pub fn crls(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<CertificateRevocationListDer<'static>, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
Ok(Item::Crl(crl)) => Some(Ok(crl)),
Err(err) => Some(Err(err)),
_ => None,
})
}

/// Extract all RSA private keys from `rd`, and return a vec of byte vecs
/// containing the der-format contents.
///
/// This function does not fail if there are no keys in the file -- it returns an
/// empty vector.
pub fn rsa_private_keys(rd: &mut dyn io::BufRead) -> Result<Vec<Vec<u8>>, io::Error> {
let mut keys = Vec::new();

loop {
match read_one(rd)? {
None => return Ok(keys),
Some(Item::RSAKey(key)) => keys.push(key),
_ => {}
};
}
pub fn rsa_private_keys(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<PrivatePkcs1KeyDer<'static>, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
Ok(Item::Pkcs1Key(key)) => Some(Ok(key)),
Err(err) => Some(Err(err)),
_ => None,
})
}

/// Extract all PKCS8-encoded private keys from `rd`, and return a vec of
/// byte vecs containing the der-format contents.
///
/// This function does not fail if there are no keys in the file -- it returns an
/// empty vector.
pub fn pkcs8_private_keys(rd: &mut dyn io::BufRead) -> Result<Vec<Vec<u8>>, io::Error> {
let mut keys = Vec::new();

loop {
match read_one(rd)? {
None => return Ok(keys),
Some(Item::PKCS8Key(key)) => keys.push(key),
_ => {}
};
}
pub fn pkcs8_private_keys(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<PrivatePkcs8KeyDer<'static>, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
Ok(Item::Pkcs8Key(key)) => Some(Ok(key)),
Err(err) => Some(Err(err)),
_ => None,
})
}

/// Extract all SEC1-encoded EC private keys from `rd`, and return a vec of
/// byte vecs containing the der-format contents.
///
/// This function does not fail if there are no keys in the file -- it returns an
/// empty vector.
pub fn ec_private_keys(rd: &mut dyn io::BufRead) -> Result<Vec<Vec<u8>>, io::Error> {
let mut keys = Vec::new();

loop {
match read_one(rd)? {
None => return Ok(keys),
Some(Item::ECKey(key)) => keys.push(key),
_ => {}
};
}
pub fn ec_private_keys(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<PrivateSec1KeyDer<'static>, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
Ok(Item::Sec1Key(key)) => Some(Ok(key)),
Err(err) => Some(Err(err)),
_ => None,
})
}
81 changes: 41 additions & 40 deletions src/pemfile.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
use std::io::{self, ErrorKind};
use std::iter;

use pki_types::{
CertificateDer, CertificateRevocationListDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer,
PrivateSec1KeyDer,
};

/// The contents of a single recognised block in a PEM file.
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum Item {
/// A DER-encoded x509 certificate.
X509Certificate(Vec<u8>),

/// A DER-encoded plaintext RSA private key; as specified in PKCS#1/RFC3447
RSAKey(Vec<u8>),

/// A DER-encoded plaintext private key; as specified in PKCS#8/RFC5958
PKCS8Key(Vec<u8>),

/// A Sec1-encoded plaintext private key; as specified in RFC5915
ECKey(Vec<u8>),

/// A Certificate Revocation List; as specified in RFC5280
Crl(Vec<u8>),
}

impl Item {
fn from_start_line(start_line: &[u8], der: Vec<u8>) -> Option<Item> {
match start_line {
b"CERTIFICATE" => Some(Item::X509Certificate(der)),
b"RSA PRIVATE KEY" => Some(Item::RSAKey(der)),
b"PRIVATE KEY" => Some(Item::PKCS8Key(der)),
b"EC PRIVATE KEY" => Some(Item::ECKey(der)),
b"X509 CRL" => Some(Item::Crl(der)),
_ => None,
}
}
///
/// Appears as "CERTIFICATE" in PEM files.
X509Certificate(CertificateDer<'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
djc marked this conversation as resolved.
Show resolved Hide resolved
///
/// Appears as "X509 CRL" in PEM files.
Crl(CertificateRevocationListDer<'static>),
}

/// Extract and decode the next PEM section from `rd`.
Expand Down Expand Up @@ -102,11 +105,16 @@ pub fn read_one(rd: &mut dyn io::BufRead) -> Result<Option<Item>, io::Error> {
.decode(&b64buf)
.map_err(|err| io::Error::new(ErrorKind::InvalidData, err))?;

if let Some(item) = Item::from_start_line(section_type, der) {
return Ok(Some(item));
} else {
section = None;
b64buf.clear();
match section_type.as_slice() {
b"CERTIFICATE" => return Ok(Some(Item::X509Certificate(der.into()))),
b"RSA PRIVATE KEY" => return Ok(Some(Item::Pkcs1Key(der.into()))),
b"PRIVATE KEY" => return Ok(Some(Item::Pkcs8Key(der.into()))),
b"EC PRIVATE KEY" => return Ok(Some(Item::Sec1Key(der.into()))),
b"X509 CRL" => return Ok(Some(Item::Crl(der.into()))),
_ => {
section = None;
b64buf.clear();
}
}
}
}
Expand All @@ -125,15 +133,8 @@ pub fn read_one(rd: &mut dyn io::BufRead) -> Result<Option<Item>, io::Error> {
}

/// Extract and return all PEM sections by reading `rd`.
pub fn read_all(rd: &mut dyn io::BufRead) -> Result<Vec<Item>, io::Error> {
let mut v = Vec::<Item>::new();

loop {
match read_one(rd)? {
None => return Ok(v),
Some(item) => v.push(item),
}
}
pub fn read_all(rd: &mut dyn io::BufRead) -> impl Iterator<Item = Result<Item, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose())
}

mod base64 {
Expand Down
8 changes: 4 additions & 4 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
mod unit {
fn check(data: &[u8]) -> Result<Vec<crate::Item>, std::io::Error> {
let mut reader = std::io::BufReader::new(data);
crate::read_all(&mut reader)
crate::read_all(&mut reader).collect()
}

#[test]
Expand All @@ -15,7 +15,7 @@ mod unit {
-----END RSA PRIVATE KEY-----\n"
)
.unwrap(),
vec![crate::Item::RSAKey(vec![0xab])]
vec![crate::Item::Pkcs1Key(vec![0xab].into())]
);
}

Expand All @@ -29,7 +29,7 @@ mod unit {
junk"
)
.unwrap(),
vec![crate::Item::RSAKey(vec![0xab])]
vec![crate::Item::Pkcs1Key(vec![0xab].into())]
);
}

Expand All @@ -44,7 +44,7 @@ mod unit {
\x00\x00"
)
.unwrap(),
vec![crate::Item::RSAKey(vec![0xab])]
vec![crate::Item::Pkcs1Key(vec![0xab].into())]
);
}

Expand Down
Loading