Skip to content

Commit

Permalink
introduce methods to create and access the fields of the Pem
Browse files Browse the repository at this point in the history
  • Loading branch information
jcreekmore committed Feb 26, 2023
1 parent 87782c4 commit a2c12dd
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 92 deletions.
30 changes: 13 additions & 17 deletions benches/pem_benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
#[macro_use]
extern crate criterion;
extern crate pem;

use criterion::Criterion;
use criterion::{criterion_group, criterion_main, Criterion};

const SAMPLE: &str = "-----BEGIN RSA PRIVATE KEY-----\r
MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc\r
Expand Down Expand Up @@ -47,9 +43,9 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("pem::parse_many", |b| b.iter(pem_parse_many));

// Encode
let pem = pem::Pem {
tag: String::from("RSA PRIVATE KEY"),
contents: vec![
let pem = pem::Pem::new(
"RSA PRIVATE KEY",
[
48, 130, 1, 61, 2, 1, 0, 2, 65, 0, 235, 31, 139, 144, 6, 98, 23, 81, 179, 252, 122,
171, 145, 251, 145, 39, 49, 3, 66, 179, 206, 167, 186, 88, 142, 160, 127, 175, 173,
115, 162, 47, 41, 2, 104, 230, 180, 28, 117, 101, 146, 67, 73, 209, 26, 221, 161, 56,
Expand All @@ -68,13 +64,13 @@ fn criterion_benchmark(c: &mut Criterion) {
176, 5, 147, 135, 242, 20, 63, 206, 123, 56, 112, 24, 118, 214, 154, 143, 210, 249,
183, 222, 2, 34, 33, 217, 43, 54, 23, 18, 137,
],
};
);
c.bench_function("pem::encode", move |b| b.iter(|| pem_encode(&pem)));

let pems = vec![
pem::Pem {
tag: String::from("RSA PRIVATE KEY"),
contents: vec![
pem::Pem::new(
"RSA PRIVATE KEY",
vec![
48, 130, 1, 61, 2, 1, 0, 2, 65, 0, 235, 31, 139, 144, 6, 98, 23, 81, 179, 252, 122,
171, 145, 251, 145, 39, 49, 3, 66, 179, 206, 167, 186, 88, 142, 160, 127, 175, 173,
115, 162, 47, 41, 2, 104, 230, 180, 28, 117, 101, 146, 67, 73, 209, 26, 221, 161,
Expand All @@ -94,10 +90,10 @@ fn criterion_benchmark(c: &mut Criterion) {
112, 24, 118, 214, 154, 143, 210, 249, 183, 222, 2, 34, 33, 217, 43, 54, 23, 18,
137,
],
},
pem::Pem {
tag: String::from("RSA PUBLIC KEY"),
contents: vec![
),
pem::Pem::new(
"RSA PUBLIC KEY",
vec![
48, 130, 1, 58, 2, 1, 0, 2, 65, 0, 194, 30, 10, 121, 253, 27, 254, 224, 217, 158,
137, 250, 161, 206, 19, 101, 194, 44, 187, 143, 162, 30, 77, 29, 51, 182, 22, 111,
143, 111, 48, 111, 105, 240, 104, 210, 134, 40, 65, 85, 114, 237, 250, 243, 198,
Expand All @@ -117,7 +113,7 @@ fn criterion_benchmark(c: &mut Criterion) {
151, 212, 173, 178, 23, 127, 236, 102, 237, 235, 159, 208, 98, 175, 175, 251, 27,
192, 121, 61, 2, 163, 34, 32,
],
},
),
];
c.bench_function("pem::encode_many", move |b| {
b.iter(|| pem_encode_many(&pems))
Expand Down
138 changes: 63 additions & 75 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
//! ";
//!
//! let pem = parse(SAMPLE).unwrap();
//! assert_eq!(pem.tag, "RSA PRIVATE KEY");
//! assert_eq!(pem.tag(), "RSA PRIVATE KEY");
//! ```
//!
//! # Example: parse a set of PEM-encoded test
Expand Down Expand Up @@ -89,8 +89,8 @@
//!
//! let pems = parse_many(SAMPLE).unwrap();
//! assert_eq!(pems.len(), 2);
//! assert_eq!(pems[0].tag, "INTERMEDIATE CERT");
//! assert_eq!(pems[1].tag, "CERTIFICATE");
//! assert_eq!(pems[0].tag(), "INTERMEDIATE CERT");
//! assert_eq!(pems[1].tag(), "CERTIFICATE");
//! ```

#![recursion_limit = "1024"]
Expand Down Expand Up @@ -137,10 +137,8 @@ pub struct EncodeConfig {
/// A representation of Pem-encoded data
#[derive(PartialEq, Debug, Clone)]
pub struct Pem {
/// The tag extracted from the Pem-encoded data
pub tag: String,
/// The binary contents of the Pem-encoded data
pub contents: Vec<u8>,
tag: String,
contents: Vec<u8>,
}

fn decode_data(raw_data: &str) -> Result<Vec<u8>> {
Expand All @@ -157,6 +155,29 @@ fn decode_data(raw_data: &str) -> Result<Vec<u8>> {
}

impl Pem {
/// Create a new Pem struct
pub fn new(tag: impl ToString, contents: impl Into<Vec<u8>>) -> Pem {
Pem {
tag: tag.to_string(),
contents: contents.into(),
}
}

/// Get the tag extracted from the Pem-encoded data
pub fn tag(&self) -> &str {
&self.tag
}

/// Get the binary contents extracted from the Pem-encoded data
pub fn contents(&self) -> &[u8] {
&self.contents
}

/// Consume the Pem struct to get an owned copy of the binary contents
pub fn into_contents(self) -> Vec<u8> {
self.contents
}

fn new_from_captures(caps: Captures) -> Result<Pem> {
fn as_utf8(bytes: &[u8]) -> Result<&str> {
str::from_utf8(bytes).map_err(PemError::NotUtf8)
Expand All @@ -183,10 +204,7 @@ impl Pem {
let raw_data = as_utf8(caps.data)?;
let contents = decode_data(raw_data)?;

Ok(Pem {
tag: tag.to_owned(),
contents,
})
Ok(Pem::new(tag, contents))
}
}

Expand Down Expand Up @@ -232,7 +250,7 @@ impl fmt::Display for Pem {
/// let SAMPLE_BYTES: Vec<u8> = SAMPLE.into();
///
/// let pem = parse(SAMPLE_BYTES).unwrap();
/// assert_eq!(pem.tag, "RSA PRIVATE KEY");
/// assert_eq!(pem.tag(), "RSA PRIVATE KEY");
/// ```
///
/// # Example: parse PEM-encoded data from a String
Expand All @@ -253,7 +271,7 @@ impl fmt::Display for Pem {
/// let SAMPLE_STRING: String = SAMPLE.into();
///
/// let pem = parse(SAMPLE_STRING).unwrap();
/// assert_eq!(pem.tag, "RSA PRIVATE KEY");
/// assert_eq!(pem.tag(), "RSA PRIVATE KEY");
/// ```
pub fn parse<B: AsRef<[u8]>>(input: B) -> Result<Pem> {
parse_captures(input.as_ref())
Expand Down Expand Up @@ -293,8 +311,8 @@ pub fn parse<B: AsRef<[u8]>>(input: B) -> Result<Pem> {
///
/// let pems = parse_many(SAMPLE_BYTES).unwrap();
/// assert_eq!(pems.len(), 2);
/// assert_eq!(pems[0].tag, "INTERMEDIATE CERT");
/// assert_eq!(pems[1].tag, "CERTIFICATE");
/// assert_eq!(pems[0].tag(), "INTERMEDIATE CERT");
/// assert_eq!(pems[1].tag(), "CERTIFICATE");
/// ```
///
/// # Example: parse a set of PEM-encoded data from a String
Expand Down Expand Up @@ -327,8 +345,8 @@ pub fn parse<B: AsRef<[u8]>>(input: B) -> Result<Pem> {
///
/// let pems = parse_many(SAMPLE_STRING).unwrap();
/// assert_eq!(pems.len(), 2);
/// assert_eq!(pems[0].tag, "INTERMEDIATE CERT");
/// assert_eq!(pems[1].tag, "CERTIFICATE");
/// assert_eq!(pems[0].tag(), "INTERMEDIATE CERT");
/// assert_eq!(pems[1].tag(), "CERTIFICATE");
/// ```
pub fn parse_many<B: AsRef<[u8]>>(input: B) -> Result<Vec<Pem>> {
// Each time our regex matches a PEM section, we need to decode it.
Expand All @@ -343,11 +361,8 @@ pub fn parse_many<B: AsRef<[u8]>>(input: B) -> Result<Vec<Pem>> {
/// ```rust
/// use pem::{Pem, encode};
///
/// let pem = Pem {
/// tag: String::from("FOO"),
/// contents: vec![1, 2, 3, 4],
/// };
/// encode(&pem);
/// let pem = Pem::new("FOO", [1, 2, 3, 4]);
/// encode(&pem);
/// ```
pub fn encode(pem: &Pem) -> String {
encode_config(
Expand All @@ -365,11 +380,8 @@ pub fn encode(pem: &Pem) -> String {
/// ```rust
/// use pem::{Pem, encode_config, EncodeConfig, LineEnding};
///
/// let pem = Pem {
/// tag: String::from("FOO"),
/// contents: vec![1, 2, 3, 4],
/// };
/// encode_config(&pem, EncodeConfig { line_ending: LineEnding::LF });
/// let pem = Pem::new("FOO", [1, 2, 3, 4]);
/// encode_config(&pem, EncodeConfig { line_ending: LineEnding::LF });
/// ```
pub fn encode_config(pem: &Pem, config: EncodeConfig) -> String {
let line_ending = match config.line_ending {
Expand Down Expand Up @@ -401,16 +413,10 @@ pub fn encode_config(pem: &Pem, config: EncodeConfig) -> String {
/// use pem::{Pem, encode_many};
///
/// let data = vec![
/// Pem {
/// tag: String::from("FOO"),
/// contents: vec![1, 2, 3, 4],
/// },
/// Pem {
/// tag: String::from("BAR"),
/// contents: vec![5, 6, 7, 8],
/// },
/// ];
/// encode_many(&data);
/// Pem::new("FOO", [1, 2, 3, 4]),
/// Pem::new("BAR", [5, 6, 7, 8]),
/// ];
/// encode_many(&data);
/// ```
pub fn encode_many(pems: &[Pem]) -> String {
pems.iter()
Expand All @@ -429,14 +435,8 @@ pub fn encode_many(pems: &[Pem]) -> String {
/// use pem::{Pem, encode_many_config, EncodeConfig, LineEnding};
///
/// let data = vec![
/// Pem {
/// tag: String::from("FOO"),
/// contents: vec![1, 2, 3, 4],
/// },
/// Pem {
/// tag: String::from("BAR"),
/// contents: vec![5, 6, 7, 8],
/// },
/// Pem::new("FOO", [1, 2, 3, 4]),
/// Pem::new("BAR", [5, 6, 7, 8]),
/// ];
/// encode_many_config(&data, EncodeConfig { line_ending: LineEnding::LF });
/// ```
Expand Down Expand Up @@ -547,7 +547,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
#[test]
fn test_parse_works() {
let pem = parse(SAMPLE_CRLF).unwrap();
assert_eq!(pem.tag, "RSA PRIVATE KEY");
assert_eq!(pem.tag(), "RSA PRIVATE KEY");
}

#[test]
Expand Down Expand Up @@ -612,15 +612,15 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
let input = "-----BEGIN DATA-----
-----END DATA-----";
let pem = parse(input).unwrap();
assert_eq!(pem.contents.len(), 0);
assert_eq!(pem.contents().len(), 0);
}

#[test]
fn test_parse_many_works() {
let pems = parse_many(SAMPLE_CRLF).unwrap();
assert_eq!(pems.len(), 2);
assert_eq!(pems[0].tag, "RSA PRIVATE KEY");
assert_eq!(pems[1].tag, "RSA PUBLIC KEY");
assert_eq!(pems[0].tag(), "RSA PRIVATE KEY");
assert_eq!(pems[1].tag(), "RSA PUBLIC KEY");
}

#[test]
Expand All @@ -631,10 +631,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg

#[test]
fn test_encode_empty_contents() {
let pem = Pem {
tag: String::from("FOO"),
contents: vec![],
};
let pem = Pem::new("FOO", vec![]);
let encoded = encode(&pem);
assert!(!encoded.is_empty());

Expand All @@ -644,10 +641,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg

#[test]
fn test_encode_contents() {
let pem = Pem {
tag: String::from("FOO"),
contents: vec![1, 2, 3, 4],
};
let pem = Pem::new("FOO", [1, 2, 3, 4]);
let encoded = encode(&pem);
assert!(!encoded.is_empty());

Expand All @@ -665,10 +659,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg

#[test]
fn test_encode_config_contents() {
let pem = Pem {
tag: String::from("FOO"),
contents: vec![1, 2, 3, 4],
};
let pem = Pem::new("FOO", [1, 2, 3, 4]);
let config = EncodeConfig {
line_ending: LineEnding::LF,
};
Expand All @@ -693,10 +684,7 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
#[cfg(feature = "serde")]
#[test]
fn test_serde() {
let pem = Pem {
tag: String::from("Mock tag"),
contents: "Mock contents".as_bytes().to_vec(),
};
let pem = Pem::new("Mock tag", "Mock contents".as_bytes());
let value = serde_json::to_string_pretty(&pem).unwrap();
let result = serde_json::from_str(&value).unwrap();
assert_eq!(pem, result);
Expand Down Expand Up @@ -794,14 +782,14 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg",
fn test_parse_many_with_headers_crlf() {
let pems = parse_many(HEADER_CRLF).unwrap();
assert_eq!(pems.len(), 2);
assert_eq!(pems[0].tag, "CERTIFICATE");
assert_eq!(pems[0].tag(), "CERTIFICATE");
assert!(cmp_data(
&pems[0].contents,
pems[0].contents(),
&decode_data(HEADER_CRLF_DATA[0]).unwrap()
));
assert_eq!(pems[1].tag, "RSA PRIVATE KEY");
assert_eq!(pems[1].tag(), "RSA PRIVATE KEY");
assert!(cmp_data(
&pems[1].contents,
pems[1].contents(),
&decode_data(HEADER_CRLF_DATA[1]).unwrap()
));
}
Expand All @@ -810,22 +798,22 @@ RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg",
fn test_parse_many_with_headers_lf() {
let pems = parse_many(HEADER_LF).unwrap();
assert_eq!(pems.len(), 2);
assert_eq!(pems[0].tag, "CERTIFICATE");
assert_eq!(pems[0].tag(), "CERTIFICATE");
assert!(cmp_data(
&pems[0].contents,
pems[0].contents(),
&decode_data(HEADER_LF_DATA[0]).unwrap()
));
assert_eq!(pems[1].tag, "RSA PRIVATE KEY");
assert_eq!(pems[1].tag(), "RSA PRIVATE KEY");
assert!(cmp_data(
&pems[1].contents,
pems[1].contents(),
&decode_data(HEADER_LF_DATA[1]).unwrap()
));
}

proptest! {
#[test]
fn test_str_parse_and_display(tag in ".+", contents in prop::collection::vec(0..255u8, 0..200)) {
let pem = Pem { tag, contents };
fn test_str_parse_and_display(tag in "[A-Z ]+", contents in prop::collection::vec(0..255u8, 0..200)) {
let pem = Pem::new(tag, contents);
prop_assert_eq!(&pem, &pem.to_string().parse::<Pem>().unwrap());
}
}
Expand Down

0 comments on commit a2c12dd

Please sign in to comment.