security_framework/os/macos/
identity.rs

1//! OSX specific extensions to identity functionality.
2use core_foundation::array::CFArray;
3use core_foundation::base::TCFType;
4use security_framework_sys::identity::SecIdentityCreateWithCertificate;
5use std::ptr;
6
7use crate::base::Result;
8use crate::certificate::SecCertificate;
9use crate::cvt;
10use crate::identity::SecIdentity;
11use crate::os::macos::keychain::SecKeychain;
12
13/// An extension trait adding OSX specific functionality to `SecIdentity`.
14pub trait SecIdentityExt {
15    /// Creates an identity corresponding to a certificate, looking in the
16    /// provided keychains for the corresponding private key.
17    ///
18    /// To search the default keychains, use an empty slice for `keychains`.
19    ///
20    /// <https://developer.apple.com/documentation/security/1401160-secidentitycreatewithcertificate>
21    fn with_certificate(
22        keychains: &[SecKeychain],
23        certificate: &SecCertificate,
24    ) -> Result<SecIdentity>;
25}
26
27impl SecIdentityExt for SecIdentity {
28    fn with_certificate(keychains: &[SecKeychain], certificate: &SecCertificate) -> Result<Self> {
29        let keychains = CFArray::from_CFTypes(keychains);
30        unsafe {
31            let mut identity = ptr::null_mut();
32            cvt(SecIdentityCreateWithCertificate(
33                if !keychains.is_empty() { keychains.as_CFTypeRef() } else { ptr::null() },
34                certificate.as_concrete_TypeRef(),
35                &mut identity,
36            ))?;
37            Ok(Self::wrap_under_create_rule(identity))
38        }
39    }
40}
41
42#[cfg(test)]
43mod test {
44    use tempfile::tempdir;
45
46    use super::*;
47    use crate::os::macos::certificate::SecCertificateExt;
48    use crate::os::macos::import_export::ImportOptions;
49    use crate::os::macos::keychain::CreateOptions;
50    use crate::os::macos::test::identity;
51    use crate::test;
52
53    #[test]
54    fn certificate() {
55        let dir = p!(tempdir());
56        let identity = identity(dir.path());
57        let certificate = p!(identity.certificate());
58        assert_eq!("foobar.com", p!(certificate.common_name()));
59    }
60
61    #[test]
62    fn private_key() {
63        let dir = p!(tempdir());
64        let identity = identity(dir.path());
65        p!(identity.private_key());
66    }
67
68    #[test]
69    fn with_certificate() {
70        let dir = p!(tempdir());
71
72        let keychain = p!(CreateOptions::new()
73            .password("foobar")
74            .create(dir.path().join("test.keychain")));
75
76        let key = include_bytes!("../../../test/server.key");
77        p!(ImportOptions::new()
78            .filename("server.key")
79            .keychain(&keychain)
80            .import(key));
81
82        let cert = test::certificate();
83        p!(SecIdentity::with_certificate(&[keychain], &cert));
84    }
85}