use crate::{
error::{SecureEnvError, SecureEnvResult},
KeyOps, SecureEnvironmentOps,
};
use p256::{ecdsa::Signature, elliptic_curve::group::GroupEncoding};
use security_framework::{
access_control::{ProtectionMode, SecAccessControl},
item::{ItemClass, ItemSearchOptions, KeyClass, Location, SearchResult},
key::{Algorithm, GenerateKeyOptions, KeyType, SecKey, Token},
passwords_options::AccessControlOptions,
};
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
pub struct SecureEnvironment;
impl SecureEnvironmentOps<Key> for SecureEnvironment {
fn generate_keypair(id: impl Into<String>, backed_by_biometrics: bool) -> SecureEnvResult<Key> {
let mut opts = GenerateKeyOptions::default();
let opts = opts.set_key_type(KeyType::ec());
let opts = opts.set_token(Token::SecureEnclave);
let opts = if backed_by_biometrics {
let access_control = SecAccessControl::create_with_protection(
Some(ProtectionMode::AccessibleWhenUnlockedThisDeviceOnly),
AccessControlOptions::BIOMETRY_CURRENT_SET.bits(),
)
.map_err(|_| {
SecureEnvError::UnableToGenerateKey(
"Unable to create access control flags".to_owned(),
)
})?;
opts.set_access_control(access_control)
} else {
opts
};
let opts = opts.set_location(Location::DataProtectionKeychain);
let opts = opts.set_label(id);
let dict = opts.to_dictionary();
let key = SecKey::generate(dict)
.map_err(|e| SecureEnvError::UnableToGenerateKey(e.to_string()))?;
Ok(Key(key))
}
fn get_keypair_by_id(id: impl Into<String>) -> SecureEnvResult<Key> {
let id = id.into();
let search_result = ItemSearchOptions::new()
.label(&id)
.load_refs(true)
.class(ItemClass::key())
.key_class(KeyClass::private())
.limit(1)
.search()
.map_err(|_| {
SecureEnvError::UnableToGetKeyPairById(format!(
"Key reference with id: '{id}' not found."
))
})?;
let result = search_result
.first()
.ok_or(SecureEnvError::UnableToGetKeyPairById(format!(
"Key reference with id: '{id}' not found."
)))?;
match result {
SearchResult::Ref(r) => match r {
security_framework::item::Reference::Key(k) => Ok(Key(k.to_owned())),
_ => Err(SecureEnvError::UnableToGetKeyPairById(
"Found Reference, but not of key instance".to_owned(),
)),
},
_ => Err(SecureEnvError::UnableToGetKeyPairById(
"Did not find search reference".to_owned(),
)),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Key(SecKey);
impl KeyOps for Key {
fn get_public_key(&self) -> SecureEnvResult<Vec<u8>> {
let public_key = self
.0
.public_key()
.ok_or(SecureEnvError::UnableToGetPublicKey(
"No public key reference found on the internal `SecKey`".to_owned(),
))?;
let sec1_bytes = public_key
.external_representation()
.ok_or(SecureEnvError::UnableToGetPublicKey(
"Could not create an external representation for the public key on the `SecKey`"
.to_owned(),
))?
.to_vec();
let public_key = p256::PublicKey::from_sec1_bytes(&sec1_bytes)
.map_err(|e| SecureEnvError::UnableToGetPublicKey(e.to_string()))?;
let public_key = public_key.as_affine().to_bytes().to_vec();
Ok(public_key)
}
fn sign(&self, msg: &[u8]) -> SecureEnvResult<Vec<u8>> {
let der_sig = self
.0
.create_signature(Algorithm::ECDSASignatureMessageX962SHA256, msg)
.map_err(|e| SecureEnvError::UnableToCreateSignature(e.to_string()))?;
let signature = Signature::from_der(&der_sig)
.map_err(|e| SecureEnvError::UnableToCreateSignature(e.to_string()))?;
let signature = signature.to_vec();
Ok(signature)
}
}