apple_security_framework/os/macos/
identity.rs

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