summaryrefslogtreecommitdiffstats
path: root/vendor/security-framework/src/key.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/security-framework/src/key.rs')
-rw-r--r--vendor/security-framework/src/key.rs387
1 files changed, 387 insertions, 0 deletions
diff --git a/vendor/security-framework/src/key.rs b/vendor/security-framework/src/key.rs
new file mode 100644
index 000000000..6609ffded
--- /dev/null
+++ b/vendor/security-framework/src/key.rs
@@ -0,0 +1,387 @@
+//! Encryption key support
+
+use crate::cvt;
+use core_foundation::{
+ base::TCFType, string::{CFStringRef, CFString},
+ dictionary::CFMutableDictionary,
+};
+use core_foundation::base::ToVoid;
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use core_foundation::boolean::CFBoolean;
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use core_foundation::data::CFData;
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use core_foundation::dictionary::CFDictionary;
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use core_foundation::number::CFNumber;
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use core_foundation::error::{CFError, CFErrorRef};
+
+use security_framework_sys::{
+ item::{kSecAttrKeyTypeRSA, kSecValueRef},
+ keychain_item::SecItemDelete
+};
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use security_framework_sys::{item::{
+ kSecAttrIsPermanent, kSecAttrLabel, kSecAttrKeyType,
+ kSecAttrKeySizeInBits, kSecPrivateKeyAttrs
+}};
+#[cfg(target_os="macos")]
+use security_framework_sys::item::{
+ kSecAttrKeyType3DES, kSecAttrKeyTypeDSA, kSecAttrKeyTypeAES,
+ kSecAttrKeyTypeDES, kSecAttrKeyTypeRC4, kSecAttrKeyTypeCAST,
+};
+
+use security_framework_sys::key::SecKeyGetTypeID;
+use security_framework_sys::base::SecKeyRef;
+
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+pub use security_framework_sys::key::Algorithm;
+
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use security_framework_sys::key::{
+ SecKeyCopyAttributes, SecKeyCopyExternalRepresentation,
+ SecKeyCreateSignature, SecKeyCreateRandomKey,
+ SecKeyCopyPublicKey,
+};
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use security_framework_sys::item::kSecAttrApplicationLabel;
+use std::fmt;
+
+use crate::base::Error;
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+use crate::item::Location;
+
+/// Types of `SecKey`s.
+#[derive(Debug, Copy, Clone)]
+pub struct KeyType(CFStringRef);
+
+#[allow(missing_docs)]
+impl KeyType {
+ #[inline(always)]
+ #[must_use]
+ pub fn rsa() -> Self {
+ unsafe { Self(kSecAttrKeyTypeRSA) }
+ }
+
+ #[cfg(target_os = "macos")]
+ #[inline(always)]
+ #[must_use]
+ pub fn dsa() -> Self {
+ unsafe { Self(kSecAttrKeyTypeDSA) }
+ }
+
+ #[cfg(target_os = "macos")]
+ #[inline(always)]
+ #[must_use]
+ pub fn aes() -> Self {
+ unsafe { Self(kSecAttrKeyTypeAES) }
+ }
+
+ #[cfg(target_os = "macos")]
+ #[inline(always)]
+ #[must_use]
+ pub fn des() -> Self {
+ unsafe { Self(kSecAttrKeyTypeDES) }
+ }
+
+ #[cfg(target_os = "macos")]
+ #[inline(always)]
+ #[must_use]
+ pub fn triple_des() -> Self {
+ unsafe { Self(kSecAttrKeyType3DES) }
+ }
+
+ #[cfg(target_os = "macos")]
+ #[inline(always)]
+ #[must_use]
+ pub fn rc4() -> Self {
+ unsafe { Self(kSecAttrKeyTypeRC4) }
+ }
+
+ #[cfg(target_os = "macos")]
+ #[inline(always)]
+ #[must_use]
+ pub fn cast() -> Self {
+ unsafe { Self(kSecAttrKeyTypeCAST) }
+ }
+
+ #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
+ #[inline(always)]
+ #[must_use]
+ pub fn ec() -> Self {
+ use security_framework_sys::item::kSecAttrKeyTypeEC;
+
+ unsafe { Self(kSecAttrKeyTypeEC) }
+ }
+
+ pub(crate) fn to_str(self) -> CFString {
+ unsafe { CFString::wrap_under_get_rule(self.0) }
+ }
+}
+
+declare_TCFType! {
+ /// A type representing an encryption key.
+ SecKey, SecKeyRef
+}
+impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
+
+unsafe impl Sync for SecKey {}
+unsafe impl Send for SecKey {}
+
+impl SecKey {
+ #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+ /// Translates to `SecKeyCreateRandomKey`
+ /// `GenerateKeyOptions` provides a helper to create an attribute
+ /// `CFDictionary`.
+ pub fn generate(attributes: CFDictionary) -> Result<Self, CFError> {
+ let mut error: CFErrorRef = ::std::ptr::null_mut();
+ let sec_key = unsafe { SecKeyCreateRandomKey(attributes.as_concrete_TypeRef(), &mut error)};
+ if !error.is_null() {
+ Err(unsafe { CFError::wrap_under_create_rule(error) })
+ } else {
+ Ok(unsafe { SecKey::wrap_under_create_rule(sec_key) })
+ }
+ }
+
+ /// Returns the programmatic identifier for the key. For keys of class
+ /// kSecAttrKeyClassPublic and kSecAttrKeyClassPrivate, the value is the
+ /// hash of the public key.
+ #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+ pub fn application_label(&self) -> Option<Vec<u8>> {
+ self.attributes()
+ .find(unsafe { kSecAttrApplicationLabel.to_void() })
+ .map(|v| unsafe { CFData::wrap_under_get_rule(v.cast()) }.to_vec())
+ }
+
+ #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+ /// Translates to `SecKeyCopyAttributes`
+ #[must_use]
+ pub fn attributes(&self) -> CFDictionary {
+ let pka = unsafe { SecKeyCopyAttributes(self.to_void() as _) };
+ unsafe { CFDictionary::wrap_under_create_rule(pka) }
+ }
+
+ #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+ /// Translates to `SecKeyCopyExternalRepresentation`
+ #[must_use]
+ pub fn external_representation(&self) -> Option<CFData> {
+ let mut error: CFErrorRef = ::std::ptr::null_mut();
+ let data = unsafe { SecKeyCopyExternalRepresentation(self.to_void() as _, &mut error) };
+ if data.is_null() {
+ return None;
+ }
+ Some(unsafe { CFData::wrap_under_create_rule(data) })
+ }
+
+ #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+ /// Translates to `SecKeyCopyPublicKey`
+ #[must_use]
+ pub fn public_key(&self) -> Option<Self> {
+ let pub_seckey = unsafe { SecKeyCopyPublicKey(self.0.cast()) };
+ if pub_seckey.is_null() {
+ return None;
+ }
+
+ Some(unsafe { SecKey::wrap_under_create_rule(pub_seckey) })
+ }
+
+ #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+ /// Creates the cryptographic signature for a block of data using a private
+ /// key and specified algorithm.
+ pub fn create_signature(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> {
+ let mut error: CFErrorRef = std::ptr::null_mut();
+
+ let output = unsafe {
+ SecKeyCreateSignature(
+ self.as_concrete_TypeRef(),
+ algorithm.into(),
+ CFData::from_buffer(input).as_concrete_TypeRef(),
+ &mut error,
+ )
+ };
+
+ if !error.is_null() {
+ Err(unsafe { CFError::wrap_under_create_rule(error) })
+ } else {
+ let output = unsafe { CFData::wrap_under_create_rule(output) };
+ Ok(output.to_vec())
+ }
+ }
+
+ /// Verifies the cryptographic signature for a block of data using a public
+ /// key and specified algorithm.
+ #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+ pub fn verify_signature(&self, algorithm: Algorithm, signed_data: &[u8], signature: &[u8]) -> Result<bool, CFError> {
+ use security_framework_sys::key::SecKeyVerifySignature;
+ let mut error: CFErrorRef = std::ptr::null_mut();
+
+ let valid = unsafe {
+ SecKeyVerifySignature(
+ self.as_concrete_TypeRef(),
+ algorithm.into(),
+ CFData::from_buffer(signed_data).as_concrete_TypeRef(),
+ CFData::from_buffer(signature).as_concrete_TypeRef(),
+ &mut error,
+ )
+ };
+
+ if !error.is_null() {
+ return Err(unsafe { CFError::wrap_under_create_rule(error) })?;
+ }
+ Ok(valid != 0)
+ }
+
+ /// Translates to `SecItemDelete`, passing in the `SecKeyRef`
+ pub fn delete(&self) -> Result<(), Error> {
+ let query = CFMutableDictionary::from_CFType_pairs(&[(
+ unsafe { kSecValueRef }.to_void(),
+ self.to_void(),
+ )]);
+
+ cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) })
+ }
+}
+
+/// Where to generate the key.
+pub enum Token {
+ /// Generate the key in software, compatible with all `KeyType`s.
+ Software,
+ /// Generate the key in the Secure Enclave such that the private key is not
+ /// extractable. Only compatible with `KeyType::ec()`.
+ SecureEnclave,
+}
+
+/// Helper for creating `CFDictionary` attributes for `SecKey::generate`
+/// Recommended reading:
+/// <https://developer.apple.com/documentation/technotes/tn3137-on-mac-keychains>
+#[derive(Default)]
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+pub struct GenerateKeyOptions {
+ /// kSecAttrKeyType
+ pub key_type: Option<KeyType>,
+ /// kSecAttrKeySizeInBits
+ pub size_in_bits: Option<u32>,
+ /// kSecAttrLabel
+ pub label: Option<String>,
+ /// kSecAttrTokenID
+ pub token: Option<Token>,
+ /// Which keychain to store the key in, if any.
+ pub location: Option<Location>,
+}
+
+#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
+impl GenerateKeyOptions {
+ /// Set `key_type`
+ pub fn set_key_type(&mut self, key_type: KeyType) -> &mut Self {
+ self.key_type = Some(key_type);
+ self
+ }
+ /// Set `size_in_bits`
+ pub fn set_size_in_bits(&mut self, size_in_bits: u32) -> &mut Self {
+ self.size_in_bits = Some(size_in_bits);
+ self
+ }
+ /// Set `label`
+ pub fn set_label(&mut self, label: impl Into<String>) -> &mut Self {
+ self.label = Some(label.into());
+ self
+ }
+ /// Set `token`
+ pub fn set_token(&mut self, token: Token) -> &mut Self {
+ self.token = Some(token);
+ self
+ }
+ /// Set `location`
+ pub fn set_location(&mut self, location: Location) -> &mut Self {
+ self.location = Some(location);
+ self
+ }
+
+ /// Collect options into a `CFDictioanry`
+ pub fn to_dictionary(&self) -> CFDictionary {
+ #[cfg(target_os = "macos")]
+ use security_framework_sys::item::kSecUseKeychain;
+ use security_framework_sys::item::{
+ kSecAttrTokenID, kSecAttrTokenIDSecureEnclave, kSecPublicKeyAttrs,
+ };
+
+ let is_permanent = CFBoolean::from(self.location.is_some());
+ let private_attributes = CFMutableDictionary::from_CFType_pairs(&[(
+ unsafe { kSecAttrIsPermanent }.to_void(),
+ is_permanent.to_void(),
+ )]);
+
+ let public_attributes = CFMutableDictionary::from_CFType_pairs(&[(
+ unsafe { kSecAttrIsPermanent }.to_void(),
+ is_permanent.to_void(),
+ )]);
+
+ let key_type = self.key_type.unwrap_or_else(KeyType::rsa).to_str();
+
+ let size_in_bits = self.size_in_bits.unwrap_or(match () {
+ _ if key_type == KeyType::rsa().to_str() => 2048,
+ _ if key_type == KeyType::ec().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(),
+ ),
+ ];
+ 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()));
+ }
+
+ #[cfg(target_os = "macos")]
+ match &self.location {
+ #[cfg(feature = "OSX_10_15")]
+ Some(Location::DataProtectionKeychain) => {
+ use security_framework_sys::item::kSecUseDataProtectionKeychain;
+ attribute_key_values.push((
+ unsafe { kSecUseDataProtectionKeychain }.to_void(),
+ CFBoolean::true_value().to_void(),
+ ));
+ }
+ Some(Location::FileKeychain(keychain)) => {
+ attribute_key_values.push((
+ unsafe { kSecUseKeychain }.to_void(),
+ keychain.as_concrete_TypeRef().to_void(),
+ ));
+ }
+ _ => {}
+ }
+
+ match self.token.as_ref().unwrap_or(&Token::Software) {
+ Token::Software => {},
+ Token::SecureEnclave => {
+ attribute_key_values.push((
+ unsafe { kSecAttrTokenID }.to_void(),
+ unsafe { kSecAttrTokenIDSecureEnclave }.to_void(),
+ ));
+ }
+ }
+
+ CFMutableDictionary::from_CFType_pairs(&attribute_key_values).to_immutable()
+ }
+}
+
+impl fmt::Debug for SecKey {
+ #[cold]
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt.debug_struct("SecKey").finish_non_exhaustive()
+ }
+}