1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::io::{self, ErrorKind};
use base64;
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum Item {
X509Certificate(Vec<u8>),
RSAKey(Vec<u8>),
PKCS8Key(Vec<u8>),
ECKey(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)),
_ => None,
}
}
}
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<_>)>;
let mut line = Vec::with_capacity(80);
loop {
line.clear();
let len = rd.read_until(b'\n', &mut line)?;
if len == 0 {
return match section {
Some((_, end_marker)) => Err(io::Error::new(
ErrorKind::InvalidData,
format!(
"section end {:?} missing",
String::from_utf8_lossy(&end_marker)
),
)),
None => Ok(None),
};
}
if line.starts_with(b"-----BEGIN ") {
let (mut trailer, mut pos) = (0, line.len());
for (i, &b) in line.iter().enumerate().rev() {
match b {
b'-' => {
trailer += 1;
pos = i;
}
b'\n' | b'\r' | b' ' => continue,
_ => break,
}
}
if trailer != 5 {
return Err(io::Error::new(
ErrorKind::InvalidData,
format!(
"illegal section start: {:?}",
String::from_utf8_lossy(&line)
),
));
}
let ty = &line[11..pos];
let mut end = Vec::with_capacity(10 + 4 + ty.len());
end.extend_from_slice(b"-----END ");
end.extend_from_slice(ty);
end.extend_from_slice(b"-----");
section = Some((ty.to_owned(), end));
continue;
}
if let Some((section_type, end_marker)) = section.as_ref() {
if line.starts_with(end_marker) {
let der = base64::decode(&b64buf)
.map_err(|err| io::Error::new(ErrorKind::InvalidData, err))?;
if let Some(item) = Item::from_start_line(§ion_type, der) {
return Ok(Some(item));
} else {
section = None;
b64buf.clear();
}
}
}
if section.is_some() {
let mut trim = 0;
for &b in line.iter().rev() {
match b {
b'\n' | b'\r' | b' ' => trim += 1,
_ => break,
}
}
b64buf.extend(&line[..line.len() - trim]);
}
}
}
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),
}
}
}