secure_env/
ios.rs

1use crate::{
2    error::{SecureEnvError, SecureEnvResult},
3    KeyOps, SecureEnvironmentOps,
4};
5use p256::{ecdsa::Signature, elliptic_curve::group::GroupEncoding};
6use security_framework::{
7    access_control::{ProtectionMode, SecAccessControl},
8    item::{ItemClass, ItemSearchOptions, KeyClass, Location, SearchResult},
9    key::{Algorithm, GenerateKeyOptions, KeyType, SecKey, Token},
10    passwords_options::AccessControlOptions,
11};
12
13/// Unit struct that can be used to create and get keypairs by id
14///
15/// # Examples
16///
17/// ## Generate a keypair
18///
19/// ```
20/// use secure_env::{SecureEnvironment, SecureEnvironmentOps};
21///
22/// let key = SecureEnvironment::generate_keypair("my-unique-id").unwrap();
23/// ```
24///
25/// ## Get a keypair from the keychain
26///
27/// ```
28/// use secure_env::{SecureEnvironment, SecureEnvironmentOps};
29///
30/// {
31///     SecureEnvironment::generate_keypair("my-unique-id").unwrap();
32/// }
33///
34/// let key = SecureEnvironment::get_keypair_by_id("my-unique-id").unwrap();
35/// ```
36#[derive(Debug, Clone, Eq, PartialEq, Copy)]
37pub struct SecureEnvironment;
38
39impl SecureEnvironmentOps<Key> for SecureEnvironment {
40    fn generate_keypair(id: impl Into<String>, backed_by_biometrics: bool) -> SecureEnvResult<Key> {
41        // Create a dictionary with the following options:
42        let mut opts = GenerateKeyOptions::default();
43
44        // Set the key type to `ec` (Elliptic Curve)
45        let opts = opts.set_key_type(KeyType::ec());
46
47        // Set the a token of `SecureEnclave`.
48        // Meaning Apple will store the key in a secure element
49        let opts = opts.set_token(Token::SecureEnclave);
50
51        let opts = if backed_by_biometrics {
52            // Set the access control so that biometrics via LocalAuthentication.framework is required
53            let access_control = SecAccessControl::create_with_protection(
54                Some(ProtectionMode::AccessibleWhenUnlockedThisDeviceOnly),
55                AccessControlOptions::BIOMETRY_CURRENT_SET.bits(),
56            )
57            .map_err(|_| {
58                SecureEnvError::UnableToGenerateKey(
59                    "Unable to create access control flags".to_owned(),
60                )
61            })?;
62
63            opts.set_access_control(access_control)
64        } else {
65            opts
66        };
67
68        // Store the key in the keychain
69        let opts = opts.set_location(Location::DataProtectionKeychain);
70
71        // Give the key a label so we can retrieve it later
72        // with the `SecureEnvironment::get_keypair_by_id` method
73        let opts = opts.set_label(id);
74
75        let dict = opts.to_dictionary();
76
77        // Generate a key using the dictionary
78        // This also passes along any information the OS provides when an error occurs
79        let key = SecKey::generate(dict)
80            .map_err(|e| SecureEnvError::UnableToGenerateKey(e.to_string()))?;
81
82        Ok(Key(key))
83    }
84
85    fn get_keypair_by_id(id: impl Into<String>) -> SecureEnvResult<Key> {
86        let id = id.into();
87
88        let search_result = ItemSearchOptions::new()
89            // Search by the provided label
90            .label(&id)
91            // Load the reference, not the actual data
92            .load_refs(true)
93            // Looking for a `Key` instance
94            .class(ItemClass::key())
95            // We want access to the private key
96            .key_class(KeyClass::private())
97            // Limit to 1 output key
98            .limit(1)
99            // Search the keychain
100            .search()
101            .map_err(|_| {
102                SecureEnvError::UnableToGetKeyPairById(format!(
103                    "Key reference with id: '{id}' not found."
104                ))
105            })?;
106
107        let result = search_result
108            .first()
109            .ok_or(SecureEnvError::UnableToGetKeyPairById(format!(
110                "Key reference with id: '{id}' not found."
111            )))?;
112
113        match result {
114            SearchResult::Ref(r) => match r {
115                security_framework::item::Reference::Key(k) => Ok(Key(k.to_owned())),
116                _ => Err(SecureEnvError::UnableToGetKeyPairById(
117                    "Found Reference, but not of key instance".to_owned(),
118                )),
119            },
120            _ => Err(SecureEnvError::UnableToGetKeyPairById(
121                "Did not find search reference".to_owned(),
122            )),
123        }
124    }
125}
126
127/// Key structure which allows for signing and retrieval of the public key
128///
129/// # Examples
130///
131/// ## Get the public Key
132///
133/// ```
134/// use secure_env::{SecureEnvironment, SecureEnvironmentOps, Key, KeyOps};
135///
136/// let key = SecureEnvironment::generate_keypair("documentation-public-key-token").unwrap();
137/// let public_key_bytes = key.get_public_key().unwrap();
138///
139/// assert_eq!(public_key_bytes.len(), 33);
140/// ```
141///
142/// ## Sign a message
143///
144/// ```
145/// use secure_env::{SecureEnvironment, SecureEnvironmentOps, Key, KeyOps};
146///
147/// let key = SecureEnvironment::generate_keypair("documentation-sign-key-token").unwrap();
148/// let signature = key.sign(b"Hello World").unwrap();
149///
150/// assert_eq!(signature.len(), 64);
151/// ```
152///
153/// ## Verify the signed message with `askar_crypto`
154///
155/// ```
156/// use secure_env::{SecureEnvironment, SecureEnvironmentOps, Key, KeyOps};
157/// use askar_crypto::{alg::p256::P256KeyPair, repr::KeyPublicBytes};
158///
159/// let msg = b"Hello World!";
160/// let key = SecureEnvironment::generate_keypair("my-test-sign-key").unwrap();
161///
162/// let public_key = key.get_public_key().unwrap();
163/// let signature = key.sign(b"Hello World!").unwrap();
164///
165/// let verify_key = P256KeyPair::from_public_bytes(&public_key).unwrap();
166/// let is_signature_valid = verify_key.verify_signature(msg, &signature);
167///
168/// assert!(is_signature_valid);
169/// ```
170#[derive(Debug, Clone, Eq, PartialEq)]
171pub struct Key(SecKey);
172
173impl KeyOps for Key {
174    fn get_public_key(&self) -> SecureEnvResult<Vec<u8>> {
175        // Retrieve the internal representation of the public key of the `SecKey`
176        let public_key = self
177            .0
178            .public_key()
179            .ok_or(SecureEnvError::UnableToGetPublicKey(
180                "No public key reference found on the internal `SecKey`".to_owned(),
181            ))?;
182
183        // Convert the public key reference to the `sec1` format in bytes
184        let sec1_bytes = public_key
185            .external_representation()
186            .ok_or(SecureEnvError::UnableToGetPublicKey(
187                "Could not create an external representation for the public key on the `SecKey`"
188                    .to_owned(),
189            ))?
190            .to_vec();
191
192        // Instantiate a P256 public key from the `sec1` bytes
193        let public_key = p256::PublicKey::from_sec1_bytes(&sec1_bytes)
194            .map_err(|e| SecureEnvError::UnableToGetPublicKey(e.to_string()))?;
195
196        // Get the affine point of the public key and convert this into a byte representation
197        let public_key = public_key.as_affine().to_bytes().to_vec();
198
199        Ok(public_key)
200    }
201
202    /**
203     *
204     * Signing is an operation that requires authentication. Make sure to manually authenticate
205     * before calling this operation
206     *
207     */
208    fn sign(&self, msg: &[u8]) -> SecureEnvResult<Vec<u8>> {
209        // Sign the message with the `der` format
210        let der_sig = self
211            .0
212            .create_signature(Algorithm::ECDSASignatureMessageX962SHA256, msg)
213            .map_err(|e| SecureEnvError::UnableToCreateSignature(e.to_string()))?;
214
215        // Convert the `ASN.1 der` format signature
216        let signature = Signature::from_der(&der_sig)
217            .map_err(|e| SecureEnvError::UnableToCreateSignature(e.to_string()))?;
218
219        // Convert the signature to a byte representation
220        let signature = signature.to_vec();
221
222        Ok(signature)
223    }
224}