Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 0227a3a

Browse files
japaricdjc
authored andcommitted
add read_one_from_slice
1 parent 6e36748 commit 0227a3a

File tree

3 files changed

+90
-33
lines changed

3 files changed

+90
-33
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ mod tests;
5151

5252
/// --- Main crate APIs:
5353
mod pemfile;
54-
pub use pemfile::{read_all, read_one, Error, Item};
54+
pub use pemfile::{read_all, read_one, read_one_from_slice, Error, Item};
5555
use pki_types::PrivateKeyDer;
5656
use pki_types::{
5757
CertificateDer, CertificateRevocationListDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer,

src/pemfile.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,32 @@ pub enum Error {
6060
Base64Decode(String),
6161
}
6262

63+
/// Extract and decode the next PEM section from `input`
64+
///
65+
/// - `Ok(None)` is returned if there is no PEM section to read from `input`
66+
/// - Syntax errors and decoding errors produce a `Err(...)`
67+
/// - Otherwise each decoded section is returned with a `Ok(Some((Item::..., remainder)))` where
68+
/// `remainder` is the part of the `input` that follows the returned section
69+
pub fn read_one_from_slice(mut input: &[u8]) -> Result<Option<(Item, &[u8])>, Error> {
70+
let mut b64buf = Vec::with_capacity(1024);
71+
let mut section = None::<(Vec<_>, Vec<_>)>;
72+
73+
loop {
74+
let next_line = if let Some(index) = input.iter().position(|byte| *byte == b'\n') {
75+
let (line, newline_plus_remainder) = input.split_at(index);
76+
input = &newline_plus_remainder[1..];
77+
Some(line)
78+
} else {
79+
None
80+
};
81+
82+
match read_one_impl(next_line, &mut section, &mut b64buf)? {
83+
ControlFlow::Continue(()) => continue,
84+
ControlFlow::Break(item) => return Ok(item.map(|item| (item, input))),
85+
}
86+
}
87+
}
88+
6389
/// Extract and decode the next PEM section from `rd`.
6490
///
6591
/// - Ok(None) is returned if there is no PEM section read from `rd`.

src/tests.rs

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,131 @@
11
#[cfg(test)]
22
mod unit {
3-
fn check(data: &[u8]) -> Result<Vec<crate::Item>, std::io::Error> {
4-
let mut reader = std::io::BufReader::new(data);
5-
crate::read_all(&mut reader).collect()
6-
}
3+
use crate::{Error, Item};
74

85
#[test]
96
fn skips_leading_junk() {
107
assert_eq!(
11-
check(
8+
check_both(
129
b"junk\n\
1310
-----BEGIN RSA PRIVATE KEY-----\n\
1411
qw\n\
1512
-----END RSA PRIVATE KEY-----\n"
16-
)
17-
.unwrap(),
18-
vec![crate::Item::Pkcs1Key(vec![0xab].into())]
13+
),
14+
vec![Item::Pkcs1Key(vec![0xab].into())]
1915
);
2016
}
2117

2218
#[test]
2319
fn skips_trailing_junk() {
2420
assert_eq!(
25-
check(
21+
check_both(
2622
b"-----BEGIN RSA PRIVATE KEY-----\n\
2723
qw\n\
2824
-----END RSA PRIVATE KEY-----\n\
2925
junk"
30-
)
31-
.unwrap(),
32-
vec![crate::Item::Pkcs1Key(vec![0xab].into())]
26+
),
27+
vec![Item::Pkcs1Key(vec![0xab].into())]
3328
);
3429
}
3530

3631
#[test]
3732
fn skips_non_utf8_junk() {
3833
assert_eq!(
39-
check(
34+
check_both(
4035
b"\x00\x00\n\
4136
-----BEGIN RSA PRIVATE KEY-----\n\
4237
qw\n\
4338
-----END RSA PRIVATE KEY-----\n
4439
\x00\x00"
45-
)
46-
.unwrap(),
47-
vec![crate::Item::Pkcs1Key(vec![0xab].into())]
40+
),
41+
vec![Item::Pkcs1Key(vec![0xab].into())]
4842
);
4943
}
5044

5145
#[test]
5246
fn rejects_invalid_base64() {
53-
assert_eq!(
54-
format!(
55-
"{:?}",
56-
check(
57-
b"-----BEGIN RSA PRIVATE KEY-----\n\
47+
let input = b"-----BEGIN RSA PRIVATE KEY-----\n\
5848
q=w\n\
59-
-----END RSA PRIVATE KEY-----\n"
60-
)
61-
),
49+
-----END RSA PRIVATE KEY-----\n";
50+
assert_eq!(
51+
format!("{:?}", check_io(input)),
6252
"Err(Custom { kind: InvalidData, error: \"InvalidByte(1, 61)\" })"
6353
);
54+
assert!(matches!(check_slice(input), Err(Error::Base64Decode(_))));
6455
}
6556

6657
#[test]
6758
fn rejects_unclosed_start_section() {
59+
let input = b"-----BEGIN RSA PRIVATE KEY-----\n\
60+
qw\n";
6861
assert_eq!(
6962
format!("{:?}",
70-
check(b"-----BEGIN RSA PRIVATE KEY-----\n\
71-
qw\n")),
63+
check_io(input)),
7264
"Err(Custom { kind: InvalidData, error: \"section end \\\"-----END RSA PRIVATE KEY-----\\\" missing\" })"
7365
);
66+
assert_eq!(
67+
check_slice(input),
68+
Err(Error::MissingSectionEnd {
69+
end_marker: b"-----END RSA PRIVATE KEY-----".to_vec()
70+
})
71+
)
7472
}
7573

7674
#[test]
7775
fn rejects_bad_start() {
76+
let input = b"-----BEGIN RSA PRIVATE KEY----\n\
77+
qw\n\
78+
-----END RSA PRIVATE KEY-----\n";
7879
assert_eq!(
7980
format!("{:?}",
80-
check(b"-----BEGIN RSA PRIVATE KEY----\n\
81-
qw\n\
82-
-----END RSA PRIVATE KEY-----\n")),
81+
check_io(input)),
8382
"Err(Custom { kind: InvalidData, error: \"illegal section start: \\\"-----BEGIN RSA PRIVATE KEY----\\\\n\\\"\" })"
8483
);
84+
assert_eq!(
85+
check_slice(input),
86+
Err(Error::IllegalSectionStart {
87+
line: b"-----BEGIN RSA PRIVATE KEY----".to_vec()
88+
})
89+
)
8590
}
8691

8792
#[test]
8893
fn skips_unrecognised_section() {
8994
assert_eq!(
90-
check(
95+
check_both(
9196
b"junk\n\
9297
-----BEGIN BREAKFAST CLUB-----\n\
9398
qw\n\
9499
-----END BREAKFAST CLUB-----\n"
95-
)
96-
.unwrap(),
100+
),
97101
vec![]
98102
);
99103
}
104+
105+
fn check_both(data: &[u8]) -> Vec<Item> {
106+
let mut reader = std::io::BufReader::new(data);
107+
let io_outcome = crate::read_all(&mut reader)
108+
.collect::<Result<Vec<_>, _>>()
109+
.unwrap();
110+
let slice_outcome = check_slice(data).unwrap();
111+
112+
assert_eq!(io_outcome, slice_outcome);
113+
114+
io_outcome
115+
}
116+
117+
fn check_slice(mut data: &[u8]) -> Result<Vec<Item>, Error> {
118+
let mut items = vec![];
119+
while let Some((item, rest)) = crate::read_one_from_slice(data)? {
120+
items.push(item);
121+
data = rest;
122+
}
123+
124+
Ok(items)
125+
}
126+
127+
fn check_io(data: &[u8]) -> Result<Vec<Item>, std::io::Error> {
128+
let mut reader = std::io::BufReader::new(data);
129+
crate::read_all(&mut reader).collect()
130+
}
100131
}

0 commit comments

Comments
 (0)