Skip to main content

security/certificate/
mod.rs

1//! `SecCertificate` and `SecKey` wrappers.
2
3use crate::error::{Result, SecurityError};
4use crate::ffi;
5use crate::private::{cf_data, cf_data_to_vec, cf_string_to_string, checked_cf, OwnedCf};
6
7/// Owned `SecKeyRef` extracted from a certificate.
8pub struct PublicKey {
9    raw: ffi::SecKeyRef,
10}
11
12impl PublicKey {
13    /// Borrow the raw `SecKeyRef`.
14    #[must_use]
15    pub const fn as_raw(&self) -> ffi::SecKeyRef {
16        self.raw
17    }
18}
19
20impl Drop for PublicKey {
21    fn drop(&mut self) {
22        if !self.raw.is_null() {
23            unsafe { ffi::CFRelease(self.raw.cast()) };
24        }
25    }
26}
27
28impl core::fmt::Debug for PublicKey {
29    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
30        f.debug_struct("PublicKey").field("raw", &self.raw).finish()
31    }
32}
33
34/// Owned DER-backed `SecCertificateRef`.
35pub struct Certificate {
36    raw: ffi::SecCertificateRef,
37}
38
39impl Certificate {
40    /// Create a certificate from DER-encoded bytes.
41    ///
42    /// # Errors
43    ///
44    /// Returns an error if the bytes are not a valid DER-encoded X.509 certificate.
45    pub fn from_der(der: &[u8]) -> Result<Self> {
46        let der = cf_data(der)?;
47        let raw =
48            unsafe { ffi::SecCertificateCreateWithData(ffi::kCFAllocatorDefault, der.as_data()) };
49        if raw.is_null() {
50            return Err(SecurityError::InvalidArgument(
51                "invalid DER-encoded X.509 certificate".to_owned(),
52            ));
53        }
54        Ok(Self { raw })
55    }
56
57    /// Borrow the raw `SecCertificateRef`.
58    #[must_use]
59    pub const fn as_raw(&self) -> ffi::SecCertificateRef {
60        self.raw
61    }
62
63    /// Return the subject-summary string, when Security.framework can derive one.
64    #[must_use]
65    pub fn subject_summary(&self) -> Option<String> {
66        let raw = unsafe { ffi::SecCertificateCopySubjectSummary(self.raw) };
67        if raw.is_null() {
68            return None;
69        }
70        let owned = OwnedCf::new(raw.cast());
71        cf_string_to_string(owned.as_string())
72    }
73
74    /// Export the certificate back to DER.
75    ///
76    /// # Errors
77    ///
78    /// Returns an error if Security.framework cannot serialize the certificate.
79    pub fn der_data(&self) -> Result<Vec<u8>> {
80        let raw = unsafe { ffi::SecCertificateCopyData(self.raw) };
81        let data = checked_cf(raw.cast(), "SecCertificateCopyData")?;
82        Ok(cf_data_to_vec(data.as_data()))
83    }
84
85    /// Copy the certificate's public key.
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if the certificate's public key cannot be extracted.
90    pub fn public_key(&self) -> Result<PublicKey> {
91        let raw = unsafe { ffi::SecCertificateCopyKey(self.raw) };
92        if raw.is_null() {
93            return Err(SecurityError::CoreFoundation(apple_cf::CFError::new(
94                "SecCertificateCopyKey",
95            )));
96        }
97        Ok(PublicKey { raw })
98    }
99}
100
101impl Drop for Certificate {
102    fn drop(&mut self) {
103        if !self.raw.is_null() {
104            unsafe { ffi::CFRelease(self.raw.cast()) };
105        }
106    }
107}
108
109impl core::fmt::Debug for Certificate {
110    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111        f.debug_struct("Certificate")
112            .field("subject_summary", &self.subject_summary())
113            .finish()
114    }
115}