Helper to convert X509 to dict (for getpeercert with binary=False)
(vm: &VirtualMachine, cert: &X509Ref)
| 186 | |
| 187 | // Helper to convert X509 to dict (for getpeercert with binary=False) |
| 188 | fn cert_to_dict(vm: &VirtualMachine, cert: &X509Ref) -> PyResult { |
| 189 | let dict = vm.ctx.new_dict(); |
| 190 | |
| 191 | dict.set_item("subject", name_to_py(vm, cert.subject_name())?, vm)?; |
| 192 | dict.set_item("issuer", name_to_py(vm, cert.issuer_name())?, vm)?; |
| 193 | // X.509 version: OpenSSL uses 0-based (0=v1, 1=v2, 2=v3) but Python uses 1-based (1=v1, 2=v2, 3=v3) |
| 194 | dict.set_item("version", vm.new_pyobj(cert.version() + 1), vm)?; |
| 195 | |
| 196 | let serial_num = cert |
| 197 | .serial_number() |
| 198 | .to_bn() |
| 199 | .and_then(|bn| bn.to_hex_str()) |
| 200 | .map_err(|e| convert_openssl_error(vm, e))?; |
| 201 | // Serial number must have even length (each byte = 2 hex chars) |
| 202 | // BigNum::to_hex_str() strips leading zeros, so we need to pad |
| 203 | let serial_str = serial_num.to_string(); |
| 204 | let serial_str = if serial_str.len() % 2 == 1 { |
| 205 | format!("0{}", serial_str) |
| 206 | } else { |
| 207 | serial_str |
| 208 | }; |
| 209 | dict.set_item("serialNumber", vm.ctx.new_str(serial_str).into(), vm)?; |
| 210 | |
| 211 | dict.set_item( |
| 212 | "notBefore", |
| 213 | vm.ctx.new_str(cert.not_before().to_string()).into(), |
| 214 | vm, |
| 215 | )?; |
| 216 | dict.set_item( |
| 217 | "notAfter", |
| 218 | vm.ctx.new_str(cert.not_after().to_string()).into(), |
| 219 | vm, |
| 220 | )?; |
| 221 | |
| 222 | if let Some(names) = cert.subject_alt_names() { |
| 223 | let san: Vec<PyObjectRef> = names |
| 224 | .iter() |
| 225 | .map(|gen_name| { |
| 226 | if let Some(email) = gen_name.email() { |
| 227 | vm.new_tuple((ascii!("email"), email)).into() |
| 228 | } else if let Some(dnsname) = gen_name.dnsname() { |
| 229 | vm.new_tuple((ascii!("DNS"), dnsname)).into() |
| 230 | } else if let Some(ip) = gen_name.ipaddress() { |
| 231 | // Parse IP address properly (IPv4 or IPv6) |
| 232 | let ip_str = if ip.len() == 4 { |
| 233 | // IPv4 |
| 234 | format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]) |
| 235 | } else if ip.len() == 16 { |
| 236 | // IPv6 - format with all zeros visible (not compressed) |
| 237 | let ip_addr = |
| 238 | std::net::Ipv6Addr::from(<[u8; 16]>::try_from(&ip[0..16]).unwrap()); |
| 239 | let s = ip_addr.segments(); |
| 240 | format!( |
| 241 | "{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}", |
| 242 | s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7] |
| 243 | ) |
| 244 | } else { |
| 245 | // Fallback for unexpected length |
no test coverage detected