Skip to content

Commit

Permalink
Add keychain synchronisation and authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
crashdump authored and kornelski committed Jan 6, 2025
1 parent 362832f commit ac1909a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 3 deletions.
4 changes: 4 additions & 0 deletions security-framework-sys/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ extern "C" {
pub static kSecAttrTokenID: CFStringRef;
#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub static kSecAttrTokenIDSecureEnclave: CFStringRef;
#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub static kSecUseAuthenticationContext: CFStringRef;
#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub static kSecAttrSynchronizable: CFStringRef;

pub static kSecAttrKeySizeInBits: CFStringRef;

Expand Down
16 changes: 16 additions & 0 deletions security-framework/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ pub struct ItemSearchOptions {
pub_key_hash: Option<CFData>,
serial_number: Option<CFData>,
app_label: Option<CFData>,
#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
authentication_context: Option<CFType>,
}

#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -298,6 +300,15 @@ impl ItemSearchOptions {
self
}

// The corresponding value is of type LAContext, and represents a reusable
// local authentication context that should be used for keychain item authentication.
#[inline(always)]
#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub fn authentication_context(&mut self, authentication_context: *mut std::os::raw::c_void) -> &mut Self {
self.authentication_context = unsafe { Some(CFType::wrap_under_create_rule(authentication_context)) };
self
}

/// Populates a `CFDictionary` to be passed to `update_item` or `delete_item`.
// CFDictionary should not be exposed in public Rust APIs.
#[inline]
Expand Down Expand Up @@ -396,6 +407,11 @@ impl ItemSearchOptions {
params.add(&kSecAttrApplicationLabel.to_void(), &app_label.to_void());
}

#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
if let Some(ref authentication_context) = self.authentication_context {
params.add(&kSecUseAuthenticationContext.to_void(), &authentication_context.to_void());
}

params.to_immutable()
}
}
Expand Down
36 changes: 33 additions & 3 deletions security-framework/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ use security_framework_sys::{item::{
kSecAttrIsPermanent, kSecAttrLabel, kSecAttrKeyType,
kSecAttrKeySizeInBits, kSecPrivateKeyAttrs, kSecAttrAccessControl
}};
#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
use security_framework_sys::item::kSecAttrSynchronizable;
#[cfg(target_os = "macos")]
use security_framework_sys::item::{
kSecAttrKeyType3DES, kSecAttrKeyTypeDSA, kSecAttrKeyTypeAES,
kSecAttrKeyTypeDES, kSecAttrKeyTypeRC4, kSecAttrKeyTypeCAST,
};

use security_framework_sys::base::SecKeyRef;
use security_framework_sys::key::SecKeyGetTypeID;
use security_framework_sys::key::{SecKeyGetTypeID, kSecKeyOperationTypeSign};

#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub use security_framework_sys::key::Algorithm;
Expand Down Expand Up @@ -381,6 +383,8 @@ pub struct GenerateKeyOptions {
/// Access control
#[deprecated(note = "use set_access_control()")]
pub access_control: Option<SecAccessControl>,
/// kSecAttrSynchronizable
pub synchronizable: Option<bool>,
}

#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
Expand Down Expand Up @@ -422,6 +426,13 @@ impl GenerateKeyOptions {
self
}

/// Set `synchronizable`
#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub fn set_synchronizable(&mut self, synchronizable: bool) -> &mut Self {
self.synchronizable = Some(synchronizable);
self
}

/// Collect options into a `CFDictioanry`
// CFDictionary should not be exposed in public Rust APIs.
#[deprecated(note = "Pass the options to SecKey::new")]
Expand Down Expand Up @@ -449,18 +460,24 @@ impl GenerateKeyOptions {
let key_type = self.key_type.unwrap_or_else(KeyType::rsa).to_str();

let size_in_bits = self.size_in_bits.unwrap_or(match () {
#[cfg(target_os = "macos")]
_ if key_type == KeyType::aes().to_str() => 256,
_ if key_type == KeyType::rsa().to_str() => 2048,
_ if key_type == KeyType::ec().to_str() => 256,
_ if key_type == KeyType::ec_sec_prime_random().to_str() => 256,
_ => 256,
});
let size_in_bits = CFNumber::from(size_in_bits as i32);

let mut attribute_key_values = vec![
(unsafe { kSecAttrKeyType }.to_void(), key_type.to_void()),
(unsafe { kSecAttrKeySizeInBits }.to_void(), size_in_bits.to_void()),
(unsafe { kSecPrivateKeyAttrs }.to_void(), private_attributes.to_void()),
(unsafe { kSecPublicKeyAttrs }.to_void(), public_attributes.to_void()),
];
if key_type != KeyType::aes().to_str() {
attribute_key_values.push((unsafe { kSecPublicKeyAttrs }.to_void(), public_attributes.to_void()));
attribute_key_values.push((unsafe { kSecPrivateKeyAttrs }.to_void(), private_attributes.to_void()));
}

let label = self.label.as_deref().map(CFString::new);
if let Some(label) = &label {
attribute_key_values.push((unsafe { kSecAttrLabel }.to_void(), label.to_void()));
Expand Down Expand Up @@ -495,6 +512,19 @@ impl GenerateKeyOptions {
}
}

#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
if let Some(ref synchronizable) = self.synchronizable {
attribute_key_values.push((
unsafe { kSecAttrSynchronizable }.to_void(),
(if *synchronizable {
CFBoolean::true_value()
} else {
CFBoolean::false_value()
})
.to_void(),
));
}

CFMutableDictionary::from_CFType_pairs(&attribute_key_values).to_immutable()
}
}
Expand Down

0 comments on commit ac1909a

Please sign in to comment.