1pub fn system_roots() -> Vec<Vec<u8>> {
12 imp::roots()
13}
14
15#[cfg(windows)]
16mod imp {
17 use core::ffi::c_void;
18
19 #[repr(C)]
21 struct CertContext {
22 encoding_type: u32,
23 cert_encoded: *const u8,
24 cert_encoded_len: u32,
25 cert_info: *mut c_void,
26 store: *mut c_void,
27 }
28
29 #[link(name = "crypt32")]
30 extern "system" {
31 fn CertOpenSystemStoreW(prov: usize, subsystem: *const u16) -> *mut c_void;
32 fn CertEnumCertificatesInStore(store: *mut c_void, prev: *const CertContext) -> *const CertContext;
33 fn CertCloseStore(store: *mut c_void, flags: u32) -> i32;
34 }
35
36 const X509_ASN_ENCODING: u32 = 1;
37
38 pub(super) fn roots() -> Vec<Vec<u8>> {
39 let name: Vec<u16> = "ROOT".encode_utf16().chain(Some(0)).collect();
40 let store = unsafe { CertOpenSystemStoreW(0, name.as_ptr()) };
43 if store.is_null() {
44 return Vec::new();
45 }
46 let mut out = Vec::new();
47 let mut ctx: *const CertContext = std::ptr::null();
48 loop {
49 ctx = unsafe { CertEnumCertificatesInStore(store, ctx) };
52 if ctx.is_null() {
53 break;
54 }
55 let der = unsafe {
58 let c = &*ctx;
59 if c.encoding_type & X509_ASN_ENCODING == 0 || c.cert_encoded.is_null() {
60 None
61 } else {
62 Some(std::slice::from_raw_parts(c.cert_encoded, c.cert_encoded_len as usize).to_vec())
63 }
64 };
65 if let Some(d) = der {
66 out.push(d);
67 }
68 }
69 unsafe { CertCloseStore(store, 0) };
72 out
73 }
74}
75
76#[cfg(not(windows))]
77mod imp {
78 pub(super) fn roots() -> Vec<Vec<u8>> {
79 Vec::new()
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 #[cfg(windows)]
86 #[test]
87 fn windows_root_store_has_certificates() {
88 let roots = super::system_roots();
89 assert!(roots.len() > 5, "{} roots", roots.len());
90 assert!(roots.iter().all(|d| d.first() == Some(&0x30)), "DER SEQUENCE");
91 }
92
93 #[cfg(not(windows))]
94 #[test]
95 fn other_systems_use_bundles() {
96 assert!(super::system_roots().is_empty());
97 }
98}