stackforge_core/layer/tls/
cert.rs1#[derive(Debug, Clone)]
8pub struct TlsCertificate {
9 pub der: Vec<u8>,
11}
12
13impl TlsCertificate {
14 pub fn from_der(der: Vec<u8>) -> Self {
16 Self { der }
17 }
18
19 pub fn from_pem(pem: &str) -> Option<Self> {
21 let lines: Vec<&str> = pem
22 .lines()
23 .filter(|line| !line.starts_with("-----"))
24 .collect();
25 let b64 = lines.join("");
26 let der = base64_decode(&b64)?;
27 Some(Self { der })
28 }
29
30 pub fn as_der(&self) -> &[u8] {
32 &self.der
33 }
34
35 pub fn len(&self) -> usize {
37 self.der.len()
38 }
39
40 pub fn is_empty(&self) -> bool {
42 self.der.is_empty()
43 }
44
45 pub fn subject_cn(&self) -> Option<String> {
51 let cn_oid = [0x55, 0x04, 0x03];
52 find_nth_string_after_oid(&self.der, &cn_oid, 1)
53 .or_else(|| find_nth_string_after_oid(&self.der, &cn_oid, 0))
54 }
55
56 pub fn issuer_cn(&self) -> Option<String> {
58 let cn_oid = [0x55, 0x04, 0x03];
59 find_nth_string_after_oid(&self.der, &cn_oid, 0)
60 }
61}
62
63pub fn parse_pem_chain(pem: &str) -> Vec<TlsCertificate> {
65 let mut certs = Vec::new();
66 let mut in_cert = false;
67 let mut current = String::new();
68
69 for line in pem.lines() {
70 if line.contains("BEGIN CERTIFICATE") {
71 in_cert = true;
72 current.clear();
73 current.push_str(line);
74 current.push('\n');
75 } else if line.contains("END CERTIFICATE") {
76 current.push_str(line);
77 current.push('\n');
78 if let Some(cert) = TlsCertificate::from_pem(¤t) {
79 certs.push(cert);
80 }
81 in_cert = false;
82 } else if in_cert {
83 current.push_str(line);
84 current.push('\n');
85 }
86 }
87
88 certs
89}
90
91fn base64_decode(input: &str) -> Option<Vec<u8>> {
93 const TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
94
95 let input: Vec<u8> = input
96 .bytes()
97 .filter(|&b| b != b'\n' && b != b'\r' && b != b' ')
98 .collect();
99 if input.is_empty() {
100 return Some(Vec::new());
101 }
102
103 let mut output = Vec::with_capacity(input.len() * 3 / 4);
104 let mut buf: u32 = 0;
105 let mut bits = 0;
106
107 for &byte in &input {
108 let val = if byte == b'=' {
109 continue;
110 } else if let Some(pos) = TABLE.iter().position(|&b| b == byte) {
111 pos as u32
112 } else {
113 return None;
114 };
115
116 buf = (buf << 6) | val;
117 bits += 6;
118
119 if bits >= 8 {
120 bits -= 8;
121 output.push((buf >> bits) as u8);
122 buf &= (1 << bits) - 1;
123 }
124 }
125
126 Some(output)
127}
128
129fn find_nth_string_after_oid(data: &[u8], oid: &[u8], n: usize) -> Option<String> {
133 let mut count = 0;
134 for i in 0..data.len().saturating_sub(oid.len()) {
135 if data[i..].starts_with(oid) {
136 let pos = i + oid.len();
137 if pos + 2 > data.len() {
138 continue;
139 }
140 let tag = data[pos];
141 if tag == 0x0C || tag == 0x13 || tag == 0x16 {
143 let len = data[pos + 1] as usize;
144 if pos + 2 + len <= data.len() {
145 if count == n {
146 return String::from_utf8(data[pos + 2..pos + 2 + len].to_vec()).ok();
147 }
148 count += 1;
149 }
150 }
151 }
152 }
153 None
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_from_der() {
162 let cert = TlsCertificate::from_der(vec![0x30, 0x82, 0x01, 0x00]);
163 assert_eq!(cert.len(), 4);
164 assert!(!cert.is_empty());
165 }
166
167 #[test]
168 fn test_base64_decode() {
169 assert_eq!(base64_decode("SGVsbG8="), Some(b"Hello".to_vec()));
170 assert_eq!(base64_decode(""), Some(Vec::new()));
171 assert_eq!(base64_decode("YQ=="), Some(b"a".to_vec()));
172 }
173
174 #[test]
175 fn test_from_pem() {
176 let pem = "-----BEGIN CERTIFICATE-----\nMIIB\n-----END CERTIFICATE-----\n";
177 let cert = TlsCertificate::from_pem(pem);
178 assert!(cert.is_some());
179 }
180
181 #[test]
182 fn test_find_cn() {
183 let mut data = Vec::new();
185 data.extend_from_slice(&[0x30, 0x20]); data.extend_from_slice(&[0x06, 0x03, 0x55, 0x04, 0x03]); data.extend_from_slice(&[0x0C, 0x04]); data.extend_from_slice(b"test");
189
190 let result = find_nth_string_after_oid(&data, &[0x55, 0x04, 0x03], 0);
191 assert_eq!(result, Some("test".to_string()));
192 }
193}