feat: add keychain-services

This commit is contained in:
2022-10-16 21:16:13 +08:00
parent 9ab7685f2d
commit a49d639b18
33 changed files with 5185 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
//! Keychain item access control types: ACLs and policies around usage of
//! private keys stored in the keychain.
use crate::{attr::AttrAccessible, error::Error, ffi::*};
use core_foundation::{
base::{kCFAllocatorDefault, CFOptionFlags, TCFType},
error::CFErrorRef,
};
use std::{
fmt::{self, Debug},
ptr,
};
/// Marker trait for types which can be used as `AccessControlFlags`.
pub trait AccessControlFlag: Copy + Clone + Sized + Into<CFOptionFlags> {}
/// Constraints on keychain item access.
///
/// See "Constraints" topic under the "Topics" section of the
/// `SecAccessControlCreateFlags` documentation at:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum AccessConstraint {
/// Require either passcode or biometric auth (TouchID/FaceID).
///
/// Wrapper for `kSecAccessControlUserPresence`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontroluserpresence>
UserPresence,
/// Require biometric auth (TouchID/FaceID) from any enrolled user for this device.
///
/// Wrapper for `kSecAccessControlBiometryAny`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolbiometryany>
BiometryAny,
/// Require biometric auth (TouchID/FaceID) from the current user.
///
/// Wrapper for `kSecAccessControlBiometryCurrentSet`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolbiometrycurrentset>
BiometryCurrentSet,
/// Require device passcode.
///
/// Wrapper for `kSecAccessControlDevicePasscode`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontroldevicepasscode>
DevicePasscode,
}
impl AccessControlFlag for AccessConstraint {}
impl From<AccessConstraint> for CFOptionFlags {
fn from(constraint: AccessConstraint) -> CFOptionFlags {
match constraint {
AccessConstraint::UserPresence => 1,
AccessConstraint::BiometryAny => 1 << 1,
AccessConstraint::BiometryCurrentSet => 1 << 3,
AccessConstraint::DevicePasscode => 1 << 4,
}
}
}
/// Conjunctions (and/or) on keychain item access.
///
/// See "Conjunctions" topic under the "Topics" section of the
/// `SecAccessControlCreateFlags` documentation at:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum AccessConjunction {
/// Require *all* constraints be satisfied.
///
/// Wrapper for `kSecAccessControlAnd`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontroland>
And,
/// Require *at least one* constraint must be satisfied.
///
/// Wrapper for `kSecAccessControlOr`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolor>
Or,
}
impl AccessControlFlag for AccessConjunction {}
impl From<AccessConjunction> for CFOptionFlags {
fn from(conjunction: AccessConjunction) -> CFOptionFlags {
match conjunction {
AccessConjunction::Or => 1 << 14,
AccessConjunction::And => 1 << 15,
}
}
}
/// Options for keychain item access.
///
/// See "Additional Options" topic under the "Topics" section of the
/// `SecAccessControlCreateFlags` documentation at:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum AccessOption {
/// Require private key be stored in the device's Secure Enclave.
///
/// Wrapper for `kSecAccessControlPrivateKeyUsage`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolprivatekeyusage>
PrivateKeyUsage,
/// Generate encryption-key from an application-provided password.
///
/// Wrapper for `kSecAccessControlApplicationPassword`. See:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolapplicationpassword>
ApplicationPassword,
}
impl AccessControlFlag for AccessOption {}
impl From<AccessOption> for CFOptionFlags {
fn from(option: AccessOption) -> CFOptionFlags {
match option {
AccessOption::PrivateKeyUsage => 1 << 30,
AccessOption::ApplicationPassword => 1 << 31,
}
}
}
/// Access control restrictions for a particular keychain item.
///
/// More information about restricting keychain items can be found at:
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/restricting_keychain_item_accessibility>
///
/// Wrapper for the `SecAccessControlCreateFlags` type:
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct AccessControlFlags(CFOptionFlags);
impl AccessControlFlags {
/// Create `SecAccessControlFlags` with no policy set
pub fn new() -> Self {
Self::default()
}
/// Add an `AccessControlFlag` to this set of flags.
// TODO: handle illegal combinations of flags?
pub fn add<F: AccessControlFlag>(&mut self, flag: F) {
self.0 |= flag.into();
}
}
/// Shorthand syntax for when flags are all of the same type
impl<'a, F> From<&'a [F]> for AccessControlFlags
where
F: AccessControlFlag,
{
fn from(flags: &[F]) -> AccessControlFlags {
let mut result = AccessControlFlags::new();
for flag in flags {
result.add(*flag)
}
result
}
}
declare_TCFType! {
/// Access control policy (a.k.a. ACL) for a keychain item, combining both a
/// set of `AccessControlFlags` and a `AttrAccessible` restriction.
///
/// Wrapper for the `SecAccessControl`/`SecAccessControlRef` types:
/// <https://developer.apple.com/documentation/security/secaccesscontrolref>
AccessControl, AccessControlRef
}
impl_TCFType!(AccessControl, AccessControlRef, SecAccessControlGetTypeID);
impl AccessControl {
/// Create a new `AccessControl` policy/ACL.
///
/// Wrapper for the `SecAccessControlCreateWithFlags()` function:
/// <https://developer.apple.com/documentation/security/1394452-secaccesscontrolcreatewithflags>
pub fn create_with_flags(
protection: AttrAccessible,
flags: AccessControlFlags,
) -> Result<Self, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let result = unsafe {
SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
protection.as_CFString().as_CFTypeRef(),
flags.0,
&mut error,
)
};
if error.is_null() {
Ok(unsafe { Self::wrap_under_create_rule(result) })
} else {
Err(error.into())
}
}
}
impl Debug for AccessControl {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO: display more information about `AccessControl`s
write!(f, "SecAccessControl {{ ... }}")
}
}

View File

@@ -0,0 +1,898 @@
//! Keychain item attributes (i.e. `SecAttr*`)
use crate::ffi::*;
use core_foundation::{
base::{CFType, TCFType, ToVoid},
data::CFData,
string::{CFString, CFStringRef},
};
use std::{
ffi::c_void,
fmt::{self, Debug, Display},
str::{self, Utf8Error},
};
/// Trait implemented by all `Attr*` types to simplify adding them to
/// attribute dictionaries.
pub(crate) trait TAttr {
/// Get the `AttrKind` for this attribute.
fn kind(&self) -> AttrKind;
/// Get a `CFType` object representing this attribute.
fn as_CFType(&self) -> CFType;
}
/// Enum of attribute types passed in parameter dictionaries. This wraps up
/// access to framework constants which would otherwise be unsafe.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum AttrKind {
/// Wrapper for the `kSecAttrAccessControl` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattraccesscontrol>
AccessControl,
/// Wrapper for the `kSecAttrAccessible` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattraccessible>
Accessible,
/// Wrapper for the `kSecAttrAccount` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattraccount>
Account,
/// Wrapper for the `kSecAttrApplicationLabel` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
ApplicationLabel,
/// Wrapper for the `kSecAttrApplicationTag` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
ApplicationTag,
/// Wrapper for the `kSecKeyDerive` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrcanderive>
Derive,
/// Wrapper for the `kSecKeyDecrypt` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrcandecrypt>
Decrypt,
/// Wrapper for the `kSecKeyEncrypt` attribute key. See:
/// https://developer.apple.com/documentation/security/ksecattrcanencrypt>
Encrypt,
/// Wrapper for the `kSecKeyExtractable` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrisextractable>
Extractable,
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
KeyClass,
/// Wrapper for the `kSecAttrKeySizeInBits` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeysizeinbits>
KeySizeInBits,
/// Wrapper for the `kSecAttrKeyType` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeytype>
KeyType,
/// Wrapper for the `kSecAttrLabel` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
Label,
/// Wrapper for the `kSecAttrIsPermanent` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrispermanent>
Permanent,
/// Wrapper for the `kSecAttrProtocol` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrprotocol>
Protocol,
/// Wrapper for `kSecKeySensitive` attribute key. See
/// <https://developer.apple.com/documentation/security/ksecattrissensitive>
Sensitive,
/// Wrapper for the `kSecAttrServer` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrserver>
Server,
/// Wrapper for the `kSecAttrService` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrservice>
Service,
/// Wrapper for the `kSecKeySign` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrcansign>
Sign,
/// Wrapper for the `kSecAttrSynchronizable` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrsynchronizable>
Synchronizable,
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
TokenId,
/// Wrapper for the `kSecKeyUnwrap` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrcanunwrap>
Unwrap,
/// Wrapper for the `kSecKeyVerify` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrcanverify>
Verify,
/// Wrapper for the `kSecKeyWrap` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrcanwrap>
Wrap,
}
impl AttrKind {
/// Attempt to look up an attribute kind by its `SecKeychainAttrType`.
// TODO: cache `SecKeychainAttrTypes`? e.g. as `lazy_static`
pub(crate) fn from_tag(tag: SecKeychainAttrType) -> Option<Self> {
let result = unsafe {
if tag == SecKeychainAttrType::from(kSecAttrAccessControl) {
AttrKind::AccessControl
} else if tag == SecKeychainAttrType::from(kSecAttrAccessible) {
AttrKind::Accessible
} else if tag == SecKeychainAttrType::from(kSecAttrAccount) {
AttrKind::Account
} else if tag == SecKeychainAttrType::from(kSecAttrApplicationLabel) {
AttrKind::ApplicationLabel
} else if tag == SecKeychainAttrType::from(kSecAttrApplicationTag) {
AttrKind::ApplicationTag
} else if tag == SecKeychainAttrType::from(kSecAttrKeyClass) {
AttrKind::KeyClass
} else if tag == SecKeychainAttrType::from(kSecAttrKeySizeInBits) {
AttrKind::KeySizeInBits
} else if tag == SecKeychainAttrType::from(kSecAttrKeyType) {
AttrKind::KeyType
} else if tag == SecKeychainAttrType::from(kSecAttrIsPermanent) {
AttrKind::Permanent
} else if tag == SecKeychainAttrType::from(kSecAttrLabel) {
AttrKind::Label
} else if tag == SecKeychainAttrType::from(kSecAttrProtocol) {
AttrKind::Protocol
} else if tag == SecKeychainAttrType::from(kSecAttrServer) {
AttrKind::Server
} else if tag == SecKeychainAttrType::from(kSecAttrService) {
AttrKind::Service
} else if tag == SecKeychainAttrType::from(kSecAttrSynchronizable) {
AttrKind::Synchronizable
} else if tag == SecKeychainAttrType::from(kSecAttrTokenID) {
AttrKind::TokenId
} else if tag == SecKeychainAttrType::from(kSecAttrCanDerive) {
AttrKind::Derive
} else if tag == SecKeychainAttrType::from(kSecAttrCanDecrypt) {
AttrKind::Decrypt
} else if tag == SecKeychainAttrType::from(kSecAttrCanEncrypt) {
AttrKind::Encrypt
} else if tag == SecKeychainAttrType::from(kSecAttrCanSign) {
AttrKind::Sign
} else if tag == SecKeychainAttrType::from(kSecAttrCanVerify) {
AttrKind::Verify
} else if tag == SecKeychainAttrType::from(kSecAttrCanWrap) {
AttrKind::Wrap
} else if tag == SecKeychainAttrType::from(kSecAttrCanUnwrap) {
AttrKind::Unwrap
} else if tag == SecKeychainAttrType::from(kSecAttrIsExtractable) {
AttrKind::Extractable
} else if tag == SecKeychainAttrType::from(kSecAttrIsSensitive) {
AttrKind::Sensitive
} else {
return None;
}
};
Some(result)
}
}
impl From<SecKeychainAttrType> for AttrKind {
fn from(tag: SecKeychainAttrType) -> Self {
Self::from_tag(tag).unwrap_or_else(|| panic!("invalid SecKeychainAttrType tag: {:?}", tag))
}
}
impl From<AttrKind> for CFStringRef {
fn from(attr: AttrKind) -> CFStringRef {
unsafe {
match attr {
AttrKind::AccessControl => kSecAttrAccessControl,
AttrKind::Accessible => kSecAttrAccessible,
AttrKind::Account => kSecAttrAccount,
AttrKind::ApplicationLabel => kSecAttrApplicationLabel,
AttrKind::ApplicationTag => kSecAttrApplicationTag,
AttrKind::Derive => kSecAttrCanDerive,
AttrKind::Decrypt => kSecAttrCanDecrypt,
AttrKind::Encrypt => kSecAttrCanEncrypt,
AttrKind::Extractable => kSecAttrIsExtractable,
AttrKind::KeyClass => kSecAttrKeyClass,
AttrKind::KeySizeInBits => kSecAttrKeySizeInBits,
AttrKind::KeyType => kSecAttrKeyType,
AttrKind::Permanent => kSecAttrIsPermanent,
AttrKind::Sensitive => kSecAttrIsSensitive,
AttrKind::Sign => kSecAttrCanSign,
AttrKind::Verify => kSecAttrCanVerify,
AttrKind::Wrap => kSecAttrCanWrap,
AttrKind::Unwrap => kSecAttrCanUnwrap,
AttrKind::Label => kSecAttrLabel,
AttrKind::Protocol => kSecAttrProtocol,
AttrKind::Server => kSecAttrServer,
AttrKind::Service => kSecAttrService,
AttrKind::Synchronizable => kSecAttrSynchronizable,
AttrKind::TokenId => kSecAttrTokenID,
}
}
}
}
unsafe impl ToVoid<CFType> for AttrKind {
fn to_void(&self) -> *const c_void {
CFStringRef::from(*self).to_void()
}
}
/// Keychain item accessibility restrictions (from most to least restrictive).
///
/// More information about restricting keychain items can be found at:
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/restricting_keychain_item_accessibility>
///
/// Wrapper for the `kSecAttrAccessible` attribute key. See
/// "Accessibility Values" section of "Item Attribute Keys and Values":
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_attribute_keys_and_values>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AttrAccessible {
/// Device is unlocked and a passcode has been set on the device.
/// <https://developer.apple.com/documentation/security/ksecattraccessiblewhenpasscodesetthisdeviceonly>
WhenPasscodeSetThisDeviceOnly,
/// The device is unlocked (no passcode mandatory). Non-exportable.
/// <https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlockedthisdeviceonly>
WhenUnlockedThisDeviceOnly,
/// The device is unlocked (no passcode mandatory).
/// <https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlocked>
WhenUnlocked,
/// Permanently accessible after the device is first unlocked after boot.
/// Non-exportable.
/// <https://developer.apple.com/documentation/security/ksecattraccessibleafterfirstunlockthisdeviceonly>
AfterFirstUnlockThisDeviceOnly,
/// Permanently accessible after the device is first unlocked after boot.
/// <https://developer.apple.com/documentation/security/ksecattraccessibleafterfirstunlock>
AfterFirstUnlock,
/// Item is always accessible on this device. Non-exportable.
/// <https://developer.apple.com/documentation/security/ksecattraccessiblealwaysthisdeviceonly>
AlwaysThisDeviceOnly,
/// Item is always accessible.
/// <https://developer.apple.com/documentation/security/ksecattraccessiblealways>
Always,
}
impl AttrAccessible {
/// Get pointer to an accessibility value to associate with the
/// `kSecAttrAccessible` key for a keychain item
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
AttrAccessible::WhenPasscodeSetThisDeviceOnly => {
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
}
AttrAccessible::WhenUnlockedThisDeviceOnly => {
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
}
AttrAccessible::WhenUnlocked => kSecAttrAccessibleWhenUnlocked,
AttrAccessible::AfterFirstUnlockThisDeviceOnly => {
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
}
AttrAccessible::AfterFirstUnlock => kSecAttrAccessibleAfterFirstUnlock,
AttrAccessible::AlwaysThisDeviceOnly => kSecAttrAccessibleAlwaysThisDeviceOnly,
AttrAccessible::Always => kSecAttrAccessibleAlways,
})
}
}
}
impl TAttr for AttrAccessible {
fn kind(&self) -> AttrKind {
AttrKind::Accessible
}
fn as_CFType(&self) -> CFType {
self.as_CFString().as_CFType()
}
}
/// Application-specific key labels, i.e. key fingerprints.
///
/// Not to be confused with `SecAttrApplicationTag` or `SecAttrLabel`, the
/// `SecAttrApplicationLabel` value is useful for programatically looking up
/// public/private key pairs, and is set to the hash of the public key, a.k.a.
/// the public key fingerprint.
///
/// Wrapper for the `kSecAttrApplicationLabel` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrapplicationlabel>
#[derive(Clone, Eq, PartialEq)]
pub struct AttrApplicationLabel(pub(crate) CFData);
impl AttrApplicationLabel {
/// Create a new application label from a byte slice
pub fn new(bytes: &[u8]) -> Self {
AttrApplicationLabel(CFData::from_buffer(bytes))
}
/// Borrow this value as a byte slice
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8]> for AttrApplicationLabel {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Debug for AttrApplicationLabel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = Vec::from(self.as_bytes());
write!(f, "SecAttrApplicationLabel({:?})", bytes)
}
}
impl<'a> From<&'a [u8]> for AttrApplicationLabel {
fn from(bytes: &[u8]) -> Self {
Self::new(bytes)
}
}
impl TAttr for AttrApplicationLabel {
fn kind(&self) -> AttrKind {
AttrKind::ApplicationLabel
}
fn as_CFType(&self) -> CFType {
self.0.as_CFType()
}
}
/// Application-specific tags for keychain items.
///
/// These should be unique for a specific item (i.e. named after its purpose
/// and used as the "primary key" for locating a particular keychain item),
/// and often use a reversed domain name-like syntax, e.g.
/// `io.crates.PackageSigning`
///
/// Wrapper for the `kSecAttrApplicationTag` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AttrApplicationTag(pub(crate) CFData);
impl AttrApplicationTag {
/// Create a new application tag from a byte slice
pub fn new(bytes: &[u8]) -> Self {
AttrApplicationTag(CFData::from_buffer(bytes))
}
/// Borrow the tag data as a byte slice
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
/// Borrow the tag data as a `str` (if it is valid UTF-8)
pub fn as_str(&self) -> Result<&str, Utf8Error> {
str::from_utf8(self.as_bytes())
}
}
impl AsRef<[u8]> for AttrApplicationTag {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Display for AttrApplicationTag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.0))
}
}
impl<'a> From<&'a [u8]> for AttrApplicationTag {
fn from(bytes: &[u8]) -> Self {
Self::new(bytes)
}
}
impl<'a> From<&'a str> for AttrApplicationTag {
fn from(string: &str) -> Self {
Self::new(string.as_bytes())
}
}
impl TAttr for AttrApplicationTag {
fn kind(&self) -> AttrKind {
AttrKind::ApplicationTag
}
fn as_CFType(&self) -> CFType {
self.0.as_CFType()
}
}
/// Human readable/meaningful labels for keychain items.
///
/// Wrapper for the `kSecAttrLabel` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AttrLabel(pub(crate) CFString);
impl AttrLabel {
/// Create a new label from a `&str`
pub fn new(label: &str) -> Self {
AttrLabel(CFString::new(label))
}
}
impl Display for AttrLabel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.0)
}
}
impl<'a> From<&'a str> for AttrLabel {
fn from(label: &str) -> Self {
Self::new(label)
}
}
impl TAttr for AttrLabel {
fn kind(&self) -> AttrKind {
AttrKind::Label
}
fn as_CFType(&self) -> CFType {
self.0.as_CFType()
}
}
/// Classes of keys supported by Keychain Services (not to be confused with
/// `SecClass`, `SecAttrClass` or `SecAttrKeyType`)
///
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AttrKeyClass {
/// Public keys.
///
/// Wrapper for the `kSecAttrKeyClassPublic` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeyclasspublic>
Public,
/// Private keys.
///
/// Wrapper for the `kSecAttrKeyClassPrivate` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeyclassprivate>
Private,
/// Symmetric keys
///
/// Wrapper for the `kSecAttrKeyClassSymmetric` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeyclasssymmetric>
// TODO: support for symmetric encryption
Symmetric,
}
impl AttrKeyClass {
/// Get `CFString` containing the `kSecAttrKeyClass` dictionary value for
/// this particular `SecAttrKeyClass`.
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
AttrKeyClass::Public => kSecAttrKeyClassPublic,
AttrKeyClass::Private => kSecAttrKeyClassPrivate,
AttrKeyClass::Symmetric => kSecAttrKeyClassSymmetric,
})
}
}
}
impl TAttr for AttrKeyClass {
fn kind(&self) -> AttrKind {
AttrKind::KeyClass
}
fn as_CFType(&self) -> CFType {
self.as_CFString().as_CFType()
}
}
impl From<CFStringRef> for AttrKeyClass {
fn from(string_ref: CFStringRef) -> AttrKeyClass {
unsafe {
if string_ref == kSecAttrKeyClassPublic {
AttrKeyClass::Public
} else if string_ref == kSecAttrKeyClassPrivate {
AttrKeyClass::Private
} else {
AttrKeyClass::Symmetric
}
}
}
}
impl<'a> From<&'a CFString> for AttrKeyClass {
fn from(string: &'a CFString) -> AttrKeyClass {
unsafe {
if *string == CFString::wrap_under_get_rule(kSecAttrKeyClassPublic) {
AttrKeyClass::Public
} else if *string == CFString::wrap_under_get_rule(kSecAttrKeyClassPrivate) {
AttrKeyClass::Private
} else {
AttrKeyClass::Symmetric
}
}
}
}
/// Types of keys supported by Keychain Services (not to be confused with
/// `AttrKeyClass`)
///
/// Wrapper for the `kSecAttrKeyType` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeytype>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AttrKeyType {
/// AES algorithm.
///
/// Wrapper for the `kSecAttrKeyTypeAES` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeytypeaes>
// TODO: support for AES encryption
Aes,
/// RSA algorithm.
///
/// Wrapper for the `kSecAttrKeyTypeRSA` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeytypersa>
Rsa,
/// Elliptic curve cryptography over the NIST curves (e.g. P-256)
///
/// Wrapper for the `kSecAttrKeyTypeECSECPrimeRandom` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeytypeecsecprimerandom>
EcSecPrimeRandom,
}
impl AttrKeyType {
/// Get `CFString` containing the `kSecAttrKeyType` dictionary value for
/// this particular `SecAttrKeyType`.
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
AttrKeyType::Aes => kSecAttrKeyTypeAES,
AttrKeyType::Rsa => kSecAttrKeyTypeRSA,
AttrKeyType::EcSecPrimeRandom => kSecAttrKeyTypeECSECPrimeRandom,
})
}
}
}
impl TAttr for AttrKeyType {
fn kind(&self) -> AttrKind {
AttrKind::KeyType
}
fn as_CFType(&self) -> CFType {
self.as_CFString().as_CFType()
}
}
impl From<CFStringRef> for AttrKeyType {
fn from(string_ref: CFStringRef) -> AttrKeyType {
unsafe {
if string_ref == kSecAttrKeyTypeECSECPrimeRandom {
AttrKeyType::EcSecPrimeRandom
} else if string_ref == kSecAttrKeyTypeRSA {
AttrKeyType::Rsa
} else {
AttrKeyType::Aes
}
}
}
}
impl<'a> From<&'a CFString> for AttrKeyType {
fn from(string: &'a CFString) -> AttrKeyType {
unsafe {
if *string == CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) {
AttrKeyType::EcSecPrimeRandom
} else if *string == CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) {
AttrKeyType::Rsa
} else {
AttrKeyType::Aes
}
}
}
}
/// Internet protocols optionally associated with `SecClass::InternetPassword`
/// keychain items.
///
/// Wrapper for the `kSecAttrProtocol` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrprotocol>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AttrProtocol {
/// File Transfer Protocol
FTP,
/// Client-side FTP account.
FTPAccount,
/// Hypertext Transfer Protocol.
HTTP,
/// Internet Relay Chat.
IRC,
/// Network News Transfer Protocol.
NNTP,
/// Post Office Protocol v3.
POP3,
/// Simple Mail Transfer Protocol.
SMTP,
/// SOCKS protocol.
SOCKS,
/// Internet Message Access Protocol.
IMAP,
/// Lightweight Directory Access Protocol.
LDAP,
/// AFP over AppleTalk.
AppleTalk,
/// AFP over TCP.
AFP,
/// Telnet protocol.
Telnet,
/// Secure Shell Protocol.
SSH,
/// FTP over TLS/SSL.
FTPS,
/// HTTP over TLS/SSL.
HTTPS,
/// HTTP proxy.
HTTPProxy,
/// HTTPS proxy.
HTTPSProxy,
/// FTP proxy.
FTPProxy,
/// Server Message Block protocol.
SMB,
/// Real Time Streaming Protocol
RTSP,
/// RTSP proxy.
RTSPProxy,
/// DAAP protocol.
DAAP,
/// Remote Apple Events.
EPPC,
/// IPP protocol.
IPP,
/// NNTP over TLS/SSL.
NNTPS,
/// LDAP over TLS/SSL.
LDAPS,
/// Telnet over TLS/SSL.
TelnetS,
/// IMAP over TLS/SSL.
IMAPS,
/// IRC over TLS/SSL.
IRCS,
/// POP3 over TLS/SSL.
POP3S,
}
impl AttrProtocol {
/// Get `CFString` containing the `kSecAttrProtocol` dictionary value for
/// this particular `SecAttrProtocol`.
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
AttrProtocol::FTP => kSecAttrProtocolFTP,
AttrProtocol::FTPAccount => kSecAttrProtocolFTPAccount,
AttrProtocol::HTTP => kSecAttrProtocolHTTP,
AttrProtocol::IRC => kSecAttrProtocolIRC,
AttrProtocol::NNTP => kSecAttrProtocolNNTP,
AttrProtocol::POP3 => kSecAttrProtocolPOP3,
AttrProtocol::SMTP => kSecAttrProtocolSMTP,
AttrProtocol::SOCKS => kSecAttrProtocolSOCKS,
AttrProtocol::IMAP => kSecAttrProtocolIMAP,
AttrProtocol::LDAP => kSecAttrProtocolLDAP,
AttrProtocol::AppleTalk => kSecAttrProtocolAppleTalk,
AttrProtocol::AFP => kSecAttrProtocolAFP,
AttrProtocol::Telnet => kSecAttrProtocolTelnet,
AttrProtocol::SSH => kSecAttrProtocolSSH,
AttrProtocol::FTPS => kSecAttrProtocolFTPS,
AttrProtocol::HTTPS => kSecAttrProtocolHTTPS,
AttrProtocol::HTTPProxy => kSecAttrProtocolHTTPProxy,
AttrProtocol::HTTPSProxy => kSecAttrProtocolHTTPSProxy,
AttrProtocol::FTPProxy => kSecAttrProtocolFTPProxy,
AttrProtocol::SMB => kSecAttrProtocolSMB,
AttrProtocol::RTSP => kSecAttrProtocolRTSP,
AttrProtocol::RTSPProxy => kSecAttrProtocolRTSPProxy,
AttrProtocol::DAAP => kSecAttrProtocolDAAP,
AttrProtocol::EPPC => kSecAttrProtocolEPPC,
AttrProtocol::IPP => kSecAttrProtocolIPP,
AttrProtocol::NNTPS => kSecAttrProtocolNNTPS,
AttrProtocol::LDAPS => kSecAttrProtocolLDAPS,
AttrProtocol::TelnetS => kSecAttrProtocolTelnetS,
AttrProtocol::IMAPS => kSecAttrProtocolIMAPS,
AttrProtocol::IRCS => kSecAttrProtocolIRCS,
AttrProtocol::POP3S => kSecAttrProtocolPOP3S,
})
}
}
}
impl TAttr for AttrProtocol {
fn kind(&self) -> AttrKind {
AttrKind::Protocol
}
fn as_CFType(&self) -> CFType {
self.as_CFString().as_CFType()
}
}
/// Identifiers for external storage tokens for cryptographic keys
/// (i.e. Secure Enclave).
///
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AttrTokenId {
/// Secure Enclave Processor (SEP), e.g. T1/T2 chip.
///
/// Wrapper for the `kSecAttrTokenIDSecureEnclave` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecattrtokenidsecureenclave>
SecureEnclave,
}
impl AttrTokenId {
/// Get `CFString` containing the `kSecAttrTokenID` dictionary value for
/// this particular `SecAttrTokenId`.
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
AttrTokenId::SecureEnclave => kSecAttrTokenIDSecureEnclave,
})
}
}
}
impl TAttr for AttrTokenId {
fn kind(&self) -> AttrKind {
AttrKind::TokenId
}
fn as_CFType(&self) -> CFType {
self.as_CFString().as_CFType()
}
}
/// Keychain Item Attribute Constants For Keys
///
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/1495743-keychain_item_attribute_constant>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum KeyAttr {
/// Wrapper for the `kSecKeyAlwaysSensitive` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyalwayssensitive>
AlwaysSensitive,
/// Wrapper for the `kSecKeyDerive` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyderive>
CanDerive,
/// Wrapper for the `kSecKeyDecrypt` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeydecrypt>
CanDecrypt,
/// Wrapper for the `kSecKeyEncrypt` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyencrypt>
CanEncrypt,
/// Wrapper for the `kSecKeySign` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeysign>
CanSign,
/// Wrapper for the `kSecKeyUnwrap` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyunwrap>
CanUnwrap,
/// Wrapper for the `kSecKeyVerify` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyverify>
CanVerify,
/// Wrapper for the `kSecKeyWrap` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeywrap>
CanWrap,
/// Wrapper for the `kSecKeyEffectiveKeySize` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyeffectivekeysize>
EffectiveKeySize,
/// Wrapper for the `kSecKeyEndDate` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyenddate>
EndDate,
/// Wrapper for the `kSecKeyExtractable` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyextractable>
Extractable,
/// Wrapper for the `kSecKeyModifiable` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeymodifiable>
Modifiable,
/// Wrapper for the `kSecKeyNeverExtractable` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyneverextractable>
NeverExtractable,
/// Wrapper for the `kSecKeyPermanent` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeypermanent>
Permanent,
/// Wrapper for the `kSecKeyPrivate` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeyprivate>
Private,
/// Wrapper for the `kSecKeySensitive` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeysensitive>
Sensitive,
/// Wrapper for the `kSecKeyKeySizeInBits` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeykeysizeinbits>
SizeInBits,
/// Wrapper for the `kSecKeyStartDate` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeystartdate>
StartDate,
/// Wrapper for the `kSecKeyKeyType` attribute value see:
/// <https://developer.apple.com/documentation/security/kseckeykeytype>
Type,
}
impl KeyAttr {
/// Get `CFString` containing the `kSecKeyAttr` dictionary value for
/// this particular `SecKeyAttr`.
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
KeyAttr::AlwaysSensitive => kSecKeyAlwaysSensitive,
KeyAttr::CanDerive => kSecKeyDerive,
KeyAttr::CanDecrypt => kSecKeyDecrypt,
KeyAttr::CanEncrypt => kSecKeyEncrypt,
KeyAttr::CanSign => kSecKeySign,
KeyAttr::CanUnwrap => kSecKeyUnwrap,
KeyAttr::CanVerify => kSecKeyVerify,
KeyAttr::CanWrap => kSecKeyWrap,
KeyAttr::Extractable => kSecKeyExtractable,
KeyAttr::EffectiveKeySize => kSecKeyEffectiveKeySize,
KeyAttr::EndDate => kSecKeyEndDate,
KeyAttr::Modifiable => kSecKeyModifiable,
KeyAttr::NeverExtractable => kSecKeyNeverExtractable,
KeyAttr::Permanent => kSecKeyPermanent,
KeyAttr::Private => kSecKeyPrivate,
KeyAttr::Sensitive => kSecKeySensitive,
KeyAttr::SizeInBits => kSecKeyKeySizeInBits,
KeyAttr::StartDate => kSecKeyStartDate,
KeyAttr::Type => kSecKeyKeyType,
})
}
}
}

View File

@@ -0,0 +1,48 @@
//! Ciphertext produced by this library.
//!
//! This type doesn't map directly to any type in the Keychain Services API,
//! but instead provides a newtype for ciphertexts this binding produces.
use crate::key::KeyAlgorithm;
/// Cryptographic signatures
#[derive(Clone, Debug)]
pub struct Ciphertext {
alg: KeyAlgorithm,
bytes: Vec<u8>,
}
impl Ciphertext {
/// Create a new `Ciphertext`
pub fn new(alg: KeyAlgorithm, bytes: Vec<u8>) -> Self {
// TODO: restrict valid algorithms to encryption algorithms?
Self { alg, bytes }
}
/// Get the algorithm which produced this signature
pub fn algorithm(&self) -> KeyAlgorithm {
self.alg
}
/// Borrow the ciphertext data as bytes
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
/// Convert into a byte vector
pub fn into_vec(self) -> Vec<u8> {
self.bytes
}
}
impl AsRef<[u8]> for Ciphertext {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl From<Ciphertext> for Vec<u8> {
fn from(ciphertext: Ciphertext) -> Vec<u8> {
ciphertext.into_vec()
}
}

View File

@@ -0,0 +1,79 @@
//! Builder for constructing a `CFDictionary` from attribute pairs.
use crate::{attr::TAttr, ffi::kSecClass, keychain::item};
use core_foundation::{
self,
base::{CFType, TCFType},
boolean::CFBoolean,
number::CFNumber,
string::{CFString, CFStringRef},
};
/// All CFDictionary types we use follow this signature
pub(crate) type Dictionary = core_foundation::dictionary::CFDictionary<CFType, CFType>;
/// Builder for attribute/parameter dictionaries we pass as arguments.
// TODO: ensure there are no duplicate items, e.g. with `HashMap`/`BTreeMap`
// storage and checking if the same key is added twice.
#[derive(Clone, Default, Debug)]
pub(crate) struct DictionaryBuilder(Vec<(CFType, CFType)>);
impl DictionaryBuilder {
/// Create a new dictionary builder
pub(crate) fn new() -> DictionaryBuilder {
DictionaryBuilder(vec![])
}
/// Add a key/value pair to the dictionary
pub(crate) fn add<K, V>(&mut self, key: K, value: &V)
where
K: Into<CFStringRef>,
V: TCFType,
{
self.0.push((
unsafe { CFString::wrap_under_get_rule(key.into()) }.as_CFType(),
value.as_CFType(),
))
}
/// Add an attribute (i.e. `TSecAttr`) to the dictionary
pub(crate) fn add_attr(&mut self, attr: &dyn TAttr) {
self.add(attr.kind(), &attr.as_CFType())
}
/// Add a key/value pair with a `bool` value to the dictionary
pub(crate) fn add_boolean<K>(&mut self, key: K, value: bool)
where
K: Into<CFStringRef>,
{
self.add(key, &CFBoolean::from(value))
}
/// Add a `keychain::item::Class` value to the dictionary
pub(crate) fn add_class(&mut self, class: item::Class) {
self.add(unsafe { kSecClass }, &class.as_CFString());
}
/// Add a key/value pair with an `i64` value to the dictionary
pub(crate) fn add_number<K>(&mut self, key: K, value: i64)
where
K: Into<CFStringRef>,
{
self.add(key, &CFNumber::from(value))
}
/// Add a key/value pair with an `AsRef<str>` value to the dictionary
pub(crate) fn add_string<K, V>(&mut self, key: K, value: V)
where
K: Into<CFStringRef>,
V: AsRef<str>,
{
self.add(key, &CFString::from(value.as_ref()))
}
}
impl From<DictionaryBuilder> for Dictionary {
fn from(builder: DictionaryBuilder) -> Dictionary {
Dictionary::from_CFType_pairs(&builder.0)
}
}

View File

@@ -0,0 +1,562 @@
//! Error types
use crate::ffi::*;
use core_foundation::{
base::{CFRelease, CFTypeRef, OSStatus, TCFType},
error::{CFErrorCopyDescription, CFErrorGetCode, CFErrorGetDomain, CFErrorRef},
string::CFString,
};
use failure::{Backtrace, Fail};
use std::{
fmt::{self, Display},
io, ptr,
};
/// No error occurred.
/// <https://developer.apple.com/documentation/security/errsecsuccess>
const errSecSuccess: OSStatus = 0;
/// Authentication and/or authorization failed.
/// <https://developer.apple.com/documentation/security/errsecauthfailed>
const errSecAuthFailed: OSStatus = -25293;
/// Buffer is too small.
/// <https://developer.apple.com/documentation/security/errsecbuffertoosmall>
const errSecBufferTooSmall: OSStatus = -25301;
/// Certificate chain creation attempt failed.
/// <https://developer.apple.com/documentation/security/errseccreatechainfailed>
const errSecCreateChainFailed: OSStatus = -25318;
/// Data too large for the given data type.
/// <https://developer.apple.com/documentation/security/errsecdatatoolarge>
const errSecDataTooLarge: OSStatus = -25302;
/// Data is not available.
/// <https://developer.apple.com/documentation/security/errsecdatanotavailable>
const errSecDataNotAvailable: OSStatus = -25316;
/// Data cannot be modified.
/// <https://developer.apple.com/documentation/security/errsecdatanotmodifiable>
const errSecDataNotModifiable: OSStatus = -25317;
/// Callback with the same name already exists.
/// <https://developer.apple.com/documentation/security/errsecduplicatecallback>
const errSecDuplicateCallback: OSStatus = -25297;
/// Item already exists.
/// <https://developer.apple.com/documentation/security/errsecduplicateitem>
const errSecDuplicateItem: OSStatus = -25299;
/// Keychain with the same name already exists.
/// <https://developer.apple.com/documentation/security/errsecduplicatekeychain>
const errSecDuplicateKeychain: OSStatus = -25296;
/// Base number for `OSStatus` values which map directly to errno values
const errSecErrnoBase: OSStatus = 100_000;
/// Upper limit number for `OSStatus` values which map directly to errno values
const errSecErrnoLimit: OSStatus = 100_255;
/// System is in a dark wake state - user interface cannot be displayed.
/// <https://developer.apple.com/documentation/security/errsecindarkwake>
const errSecInDarkWake: OSStatus = -25320;
/// Security Server interactions not allowed in this context.
/// <https://developer.apple.com/documentation/security/errsecinteractionnotallowed>
const errSecInteractionNotAllowed: OSStatus = -25308;
/// User interaction required.
/// <https://developer.apple.com/documentation/security/errsecinteractionrequired>
const errSecInteractionRequired: OSStatus = -25315;
/// Callback is invalid.
/// <https://developer.apple.com/documentation/security/errsecinvalidcallback>
const errSecInvalidCallback: OSStatus = -25298;
/// Item reference is invalid.
/// <https://developer.apple.com/documentation/security/errsecinvaliditemref>
const errSecInvalidItemRef: OSStatus = -25304;
/// Keychain is invalid.
/// <https://developer.apple.com/documentation/security/errsecinvalidkeychain>
const errSecInvalidKeychain: OSStatus = -25295;
/// Specified preference domain is not valid.
/// <https://developer.apple.com/documentation/security/errsecinvalidprefsdomain>
const errSecInvalidPrefsDomain: OSStatus = -25319;
/// Search reference is invalid.
/// <https://developer.apple.com/documentation/security/errsecinvalidsearchref>
const errSecInvalidSearchRef: OSStatus = -25305;
/// Item could not be found.
/// <https://developer.apple.com/documentation/security/errsecitemnotfound>
const errSecItemNotFound: OSStatus = -25300;
/// Invalid key size.
/// <https://developer.apple.com/documentation/security/errseckeysizenotallowed>
const errSecKeySizeNotAllowed: OSStatus = -25311;
/// Missing entitlement: keychain access disallowed because app is unsigned.
/// <https://developer.apple.com/documentation/security/errsecmissingentitlement>
const errSecMissingEntitlement: OSStatus = -34018;
/// Certificate module unavailable.
/// <https://developer.apple.com/documentation/security/errsecnocertificatemodule>
const errSecNoCertificateModule: OSStatus = -25313;
/// Default keychain does not exist.
/// <https://developer.apple.com/documentation/security/errsecnodefaultkeychain>
const errSecNoDefaultKeychain: OSStatus = -25307;
/// Policy module unavailable.
/// <https://developer.apple.com/documentation/security/errsecnopolicymodule>
const errSecNoPolicyModule: OSStatus = -25314;
/// Storage module unavailable.
/// <https://developer.apple.com/documentation/security/errsecnostoragemodule>
const errSecNoStorageModule: OSStatus = -25312;
/// Specified attribute does not exist.
/// <https://developer.apple.com/documentation/security/errsecnosuchattr>
const errSecNoSuchAttr: OSStatus = -25303;
/// Specified keychain item class does not exist.
/// <https://developer.apple.com/documentation/security/errsecnosuchclass>
const errSecNoSuchClass: OSStatus = -25306;
/// Specified keychain does not exist.
/// <https://developer.apple.com/documentation/security/errsecnosuchkeychain>
const errSecNoSuchKeychain: OSStatus = -25294;
/// Trust results not available.
/// <https://developer.apple.com/documentation/security/errsecnotavailable>
const errSecNotAvailable: OSStatus = -25291;
/// Can't perform given action on read-only item.
/// <https://developer.apple.com/documentation/security/errsecreadonly>
const errSecReadOnly: OSStatus = -25292;
/// Can't perform action on read-only attribute
/// <https://developer.apple.com/documentation/security/errsecreadonlyattr>
const errSecReadOnlyAttr: OSStatus = -25309;
/// Invalid version.
/// <https://developer.apple.com/documentation/security/errsecwrongversion>
const errSecWrongSecVersion: OSStatus = -25310;
/// Error type.
///
/// Wrapper for the `CFError` type:
/// <https://developer.apple.com/documentation/corefoundation/cferror>
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
backtrace: Backtrace,
description: String,
}
impl Error {
/// Create a new error of the given kind with the given description
pub fn new<D>(kind: ErrorKind, description: &D) -> Self
where
D: ToString + ?Sized,
{
Error {
kind,
backtrace: Backtrace::new(),
description: description.to_string(),
}
}
/// Create an error from an `OSStatus` if the status is not success
pub fn maybe_from_OSStatus(status: OSStatus) -> Option<Self> {
if status == errSecSuccess {
None
} else {
let kind = ErrorKind::from(status);
let description = unsafe {
CFString::wrap_under_create_rule(SecCopyErrorMessageString(status, ptr::null()))
};
Some(Error::new(kind, &description))
}
}
/// Get the `ErrorKind` for this error
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ({})", &self.description, &self.kind)
}
}
impl Fail for Error {
fn cause(&self) -> Option<&dyn Fail> {
None
}
fn backtrace(&self) -> Option<&Backtrace> {
Some(&self.backtrace)
}
}
impl From<CFErrorRef> for Error {
/// Creates an `Error` with copies of all error data on the Rust heap.
///
/// Calls `CFRelease` on the provided `CFErrorRef`.
fn from(error_ref: CFErrorRef) -> Error {
let kind = ErrorKind::from(error_ref);
let backtrace = Backtrace::new();
let description =
unsafe { CFString::wrap_under_create_rule(CFErrorCopyDescription(error_ref)) }
.to_string();
// Free the error reference
unsafe {
CFRelease(error_ref as CFTypeRef);
}
Error {
kind,
backtrace,
description,
}
}
}
/// Kinds of errors.
#[derive(Clone, Debug, Fail)]
pub enum ErrorKind {
/// Authentication and/or authorization failed.
///
/// Wrapper for the `errSecAuthFailed` status code. See:
/// <https://developer.apple.com/documentation/security/errsecauthfailed>
#[fail(display = "authentication failed")]
AuthFailed,
/// Buffer is too small.
///
/// Wrapper for the `errSecBufferTooSmall` status code. See:
/// <https://developer.apple.com/documentation/security/errsecbuffertoosmall>
#[fail(display = "buffer too small")]
BufferTooSmall,
/// Certificate chain creation attempt failed.
///
/// Wrapper for the `errSecCreateChainFailed` status code. See:
/// <https://developer.apple.com/documentation/security/errseccreatechainfailed>
#[fail(display = "certificate chain creation attempt failed")]
CreateChainFailed,
/// Data too large for the given data type.
///
/// Wrapper for the `errSecDataTooLarge` status code. See:
/// <https://developer.apple.com/documentation/security/errsecdatatoolarge>
#[fail(display = "data too large")]
DataTooLarge,
/// Data is not available.
///
/// Wrapper for the `errSecDataNotAvailable` status code. See:
/// <https://developer.apple.com/documentation/security/errsecdatanotavailable>
#[fail(display = "data not available")]
DataNotAvailable,
/// Data cannot be modified.
///
/// Wrapper for the `errSecDataNotModifiable` status code. See:
/// <https://developer.apple.com/documentation/security/errsecdatanotmodifiable>
#[fail(display = "data not modifiable")]
DataNotModifiable,
/// Callback with the same name already exists.
///
/// Wrapper for the `errSecDuplicateCallback` status code. See:
/// <https://developer.apple.com/documentation/security/errsecduplicatecallback>
#[fail(display = "duplicate callback")]
DuplicateCallback,
/// Item already exists.
///
/// Wrapper for the `errSecDuplicateItem` status code. See:
/// <https://developer.apple.com/documentation/security/errsecduplicateitem>
#[fail(display = "duplicate item")]
DuplicateItem,
/// Keychain with the same name already exists.
///
/// Wrapper for the `errSecDuplicateKeychain` status code. See:
/// <https://developer.apple.com/documentation/security/errsecduplicatekeychain>
#[fail(display = "duplicate keychain")]
DuplicateKeychain,
/// System is in a dark wake state - user interface cannot be displayed.
///
/// Wrapper for the `errSecInDarkWake` status code. See:
/// <https://developer.apple.com/documentation/security/errsecindarkwake>
#[fail(display = "in dark wake")]
InDarkWake,
/// Security Server interactions not allowed in this context.
///
/// Wrapper for the `errSecInteractionNotAllowed` status code. See:
/// <https://developer.apple.com/documentation/security/errsecinteractionnotallowed>
#[fail(display = "interaction not allowed")]
InteractionNotAllowed,
/// User interaction required.
///
/// Wrapper for the `errSecInteractionRequired` status code. See:
/// <https://developer.apple.com/documentation/security/errsecinteractionrequired>
#[fail(display = "user interaction required")]
InteractionRequired,
/// Callback is invalid.
///
/// Wrapper for the `errSecInvalidCallback` status code. See:
/// <https://developer.apple.com/documentation/security/errsecinvalidcallback>
#[fail(display = "invalid callback")]
InvalidCallback,
/// Item reference is invalid.
///
/// Wrapper for the `errSecInvalidItemRef` status code. See:
/// <https://developer.apple.com/documentation/security/errsecinvaliditemref>
#[fail(display = "invalid item ref")]
InvalidItemRef,
/// Keychain is invalid.
///
/// Wrapper for the `errSecInvalidKeychain` status code. See:
/// <https://developer.apple.com/documentation/security/errsecinvalidkeychain>
#[fail(display = "invalid keychain")]
InvalidKeychain,
/// Specified preference domain is not valid.
///
/// Wrapper for the `errSecInvalidPrefsDomain` status code. See:
/// <https://developer.apple.com/documentation/security/errsecinvalidprefsdomain>
#[fail(display = "invalid preference domain")]
InvalidPrefsDomain,
/// Search reference is invalid.
///
/// Wrapper for the `errSecInvalidSearchRef` status code. See:
/// <https://developer.apple.com/documentation/security/errsecinvalidsearchref>
#[fail(display = "search ref is invalid")]
InvalidSearchRef,
/// Item could not be found.
///
/// Wrapper for the `errSecItemNotFound` status code. See:
/// <https://developer.apple.com/documentation/security/errsecitemnotfound>
#[fail(display = "item not found")]
ItemNotFound,
/// Invalid key size.
///
/// Wrapper for the `errSecKeySizeNotAllowed` status code. See:
/// <https://developer.apple.com/documentation/security/errseckeysizenotallowed>
#[fail(display = "key size not allowed")]
KeySizeNotAllowed,
/// Required entitlement for accessing the keychain is missing. This error
/// occurs when attempting to access certain keychain functionality from an
/// application which is either unsigned or missing a required entitlement.
///
/// Wrapper for the `errSecMissingEntitlement` status code. See:
/// <https://developer.apple.com/documentation/security/errsecmissingentitlement>
#[fail(display = "missing application entitlement (errSecMissingEntitlement)")]
MissingEntitlement,
/// Certificate module unavailable.
///
/// Wrapper for the `errSecNoCertificateModule` status code. See:
/// <https://developer.apple.com/documentation/security/errsecnocertificatemodule>
#[fail(display = "no certificate module")]
NoCertificateModule,
/// Default keychain does not exist.
///
/// Wrapper for the `errSecNoDefaultKeychain` status code. See:
/// <https://developer.apple.com/documentation/security/errsecnodefaultkeychain>
#[fail(display = "no default keychain")]
NoDefaultKeychain,
/// Policy module unavailable.
///
/// Wrapper for the `errSecNoPolicyModule` status code. See:
/// <https://developer.apple.com/documentation/security/errsecnopolicymodule>
#[fail(display = "no policy module")]
NoPolicyModule,
/// Storage module unavailable.
///
/// Wrapper for the `errSecNoStorageModule` status code. See:
/// <https://developer.apple.com/documentation/security/errsecnostoragemodule>
#[fail(display = "no storage module")]
NoStorageModule,
/// Specified attribute does not exist.
///
/// Wrapper for the `errSecNoSuchAttr` status code. See:;
/// <https://developer.apple.com/documentation/security/errsecnosuchattr>
#[fail(display = "no such attr")]
NoSuchAttr,
/// Specified keychain item class does not exist.
///
/// Wrapper for the `errSecNoSuchClass` status code. See:
/// <https://developer.apple.com/documentation/security/errsecnosuchclass>
#[fail(display = "no such class")]
NoSuchClass,
/// Specified keychain does not exist.
///
/// Wrapper for the `errSecNoSuchKeychain` status code. See:
/// <https://developer.apple.com/documentation/security/errsecnosuchkeychain>
#[fail(display = "no such keychain")]
NoSuchKeychain,
/// Trust results not available.
///
/// Wrapper for the `errSecNotAvailable` status code. See:
/// <https://developer.apple.com/documentation/security/errsecnotavailable>
#[fail(display = "not available")]
NotAvailable,
/// Can't perform given action on read-only item.
///
/// Wrapper for the `errSecReadOnly` status code. See:
/// <https://developer.apple.com/documentation/security/errsecreadonly>
#[fail(display = "read-only")]
ReadOnly,
/// Can't perform action on read-only attribute
///
/// Wrapper for the `errSecReadOnlyAttr` status code. See:
/// <https://developer.apple.com/documentation/security/errsecreadonlyattr>
#[fail(display = "read-only attr")]
ReadOnlyAttr,
/// Invalid version.
///
/// Wrapper for the `errSecWrongSecVersion` status code. See:
/// <https://developer.apple.com/documentation/security/errsecwrongversion>
#[fail(display = "wrong version")]
WrongSecVersion,
/// Input/output errors.
///
/// Wrapper for errno codes we know/commonly encounter.
#[fail(display = "I/O error ({:?})", kind)]
Io {
/// `std::io::ErrorKind` value representing the I/O error
kind: io::ErrorKind,
},
/// Errors returned from CoreFoundation.
///
/// Codes correspond to the return value of the `CFErrorGetCode` function.
///
/// For more information, see:
/// <https://developer.apple.com/documentation/corefoundation/1494656-cferrorgetcode?language=objc>
#[fail(display = "Core Foundation error (code: {}, domain: {})", code, domain)]
CFError {
/// Code identifying this type of `CFError`.
///
/// See `CFErrorGetCode()` for more information:
/// <https://developer.apple.com/documentation/corefoundation/1494656-cferrorgetcode>
code: i64,
/// Domain associated with this error.
///
/// See `CFErrorGetDomain()` for more information:
/// <https://developer.apple.com/documentation/corefoundation/1494657-cferrorgetdomain>
domain: String,
},
/// Unix errno values we receive from the underlying OS
// TODO: create `ErrorKind` variants for ones we commonly encounter?
#[fail(display = "POSIX error (errno: {})", code)]
Errno {
/// Raw errno value
code: u8,
},
/// `OSStatus` codes which we can't otherwise decode.
#[fail(display = "unknown OS error (code: {})", code)]
OSError {
/// OS error code
code: i64,
},
}
impl From<CFErrorRef> for ErrorKind {
fn from(error_ref: CFErrorRef) -> ErrorKind {
ErrorKind::CFError {
code: unsafe { CFErrorGetCode(error_ref) } as i64,
domain: unsafe { CFString::wrap_under_get_rule(CFErrorGetDomain(error_ref)) }
.to_string(),
}
}
}
impl From<OSStatus> for ErrorKind {
fn from(status: OSStatus) -> ErrorKind {
match status {
errSecAuthFailed => ErrorKind::AuthFailed,
errSecBufferTooSmall => ErrorKind::BufferTooSmall,
errSecCreateChainFailed => ErrorKind::CreateChainFailed,
errSecDataTooLarge => ErrorKind::DataTooLarge,
errSecDataNotAvailable => ErrorKind::DataNotAvailable,
errSecDataNotModifiable => ErrorKind::DataNotModifiable,
errSecDuplicateCallback => ErrorKind::DuplicateCallback,
errSecDuplicateItem => ErrorKind::DuplicateItem,
errSecDuplicateKeychain => ErrorKind::DuplicateKeychain,
errSecInDarkWake => ErrorKind::InDarkWake,
errSecInteractionNotAllowed => ErrorKind::InteractionNotAllowed,
errSecInteractionRequired => ErrorKind::InteractionRequired,
errSecInvalidCallback => ErrorKind::InvalidCallback,
errSecInvalidItemRef => ErrorKind::InvalidItemRef,
errSecInvalidKeychain => ErrorKind::InvalidKeychain,
errSecInvalidPrefsDomain => ErrorKind::InvalidPrefsDomain,
errSecInvalidSearchRef => ErrorKind::InvalidSearchRef,
errSecItemNotFound => ErrorKind::ItemNotFound,
errSecKeySizeNotAllowed => ErrorKind::KeySizeNotAllowed,
errSecMissingEntitlement => ErrorKind::MissingEntitlement,
errSecNoCertificateModule => ErrorKind::NoCertificateModule,
errSecNoDefaultKeychain => ErrorKind::NoDefaultKeychain,
errSecNoPolicyModule => ErrorKind::NoPolicyModule,
errSecNoStorageModule => ErrorKind::NoStorageModule,
errSecNoSuchAttr => ErrorKind::NoSuchAttr,
errSecNoSuchClass => ErrorKind::NoSuchClass,
errSecNoSuchKeychain => ErrorKind::NoSuchKeychain,
errSecNotAvailable => ErrorKind::NotAvailable,
errSecReadOnly => ErrorKind::ReadOnly,
errSecReadOnlyAttr => ErrorKind::ReadOnlyAttr,
errSecWrongSecVersion => ErrorKind::WrongSecVersion,
errSecErrnoBase..=errSecErrnoLimit => match (status - errSecErrnoBase) as u8 {
1 => ErrorKind::Io {
kind: io::ErrorKind::PermissionDenied,
},
2 => ErrorKind::Io {
kind: io::ErrorKind::NotFound,
},
17 => ErrorKind::Io {
kind: io::ErrorKind::AlreadyExists,
},
code => ErrorKind::Errno { code },
},
_ => ErrorKind::OSError {
code: i64::from(status),
},
}
}
}

View File

@@ -0,0 +1,443 @@
use core_foundation::{
base::{CFAllocatorRef, CFIndex, CFOptionFlags, CFTypeID, CFTypeRef, OSStatus, TCFType},
data::CFDataRef,
dictionary::CFDictionaryRef,
error::CFErrorRef,
string::{CFString, CFStringRef},
};
use std::{
borrow::Cow,
fmt::{self, Debug},
os::raw::{c_char, c_void},
ptr, slice, str,
};
/// Four character codes used as identifiers. See:
/// <https://developer.apple.com/documentation/kernel/fourcharcode>
#[repr(transparent)]
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) struct FourCharacterCode(u32);
impl FourCharacterCode {
fn as_bytes(&self) -> &[u8; 4] {
unsafe { &*(self as *const FourCharacterCode as *const [u8; 4]) }
}
fn as_str(&self) -> &str {
str::from_utf8(self.as_bytes()).unwrap()
}
}
impl AsRef<str> for FourCharacterCode {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Debug for FourCharacterCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FourCharacterCode({})", self.as_str())
}
}
impl From<u32> for FourCharacterCode {
fn from(num: u32) -> FourCharacterCode {
FourCharacterCode(num)
}
}
impl From<[u8; 4]> for FourCharacterCode {
fn from(bytes: [u8; 4]) -> FourCharacterCode {
Self::from(&bytes)
}
}
impl<'a> From<&'a [u8; 4]> for FourCharacterCode {
fn from(bytes: &[u8; 4]) -> FourCharacterCode {
let mut result: u32 = 0;
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), (&mut result as *mut u32) as *mut u8, 4);
}
FourCharacterCode(result)
}
}
impl From<CFStringRef> for FourCharacterCode {
fn from(string_ref: CFStringRef) -> FourCharacterCode {
Self::from(&unsafe { CFString::wrap_under_get_rule(string_ref) })
}
}
impl<'a> From<&'a CFString> for FourCharacterCode {
fn from(string: &'a CFString) -> FourCharacterCode {
let string = Cow::from(string);
assert_eq!(string.as_bytes().len(), 4);
let mut code = [0u8; 4];
code.copy_from_slice(string.as_bytes());
code.into()
}
}
/// Reference to an access control policy.
///
/// See `SecAccessControlRef` documentation:
/// <https://developer.apple.com/documentation/security/secaccesscontrolref>
pub(crate) type AccessControlRef = CFTypeRef;
/// Reference to a `Key`
///
/// See `SecKeyRef` documentation:
/// <https://developer.apple.com/documentation/security/seckeyref>
pub(crate) type KeyRef = CFTypeRef;
/// Reference to a `Keychain`
///
/// See `SecKeychainRef` documentation:
/// <https://developer.apple.com/documentation/security/seckeychainref>
pub(crate) type KeychainRef = CFTypeRef;
/// Reference to a `keychain::Item`
///
/// See `SecKeychainItemRef` documentation:
/// <https://developer.apple.com/documentation/security/seckeychainitemref>
pub(crate) type ItemRef = CFTypeRef;
/// Attribute type codes.
///
/// Wrapper for `SecKeychainAttrType`. See:
/// <https://developer.apple.com/documentation/security/seckeychainattrtype>
pub(crate) type SecKeychainAttrType = FourCharacterCode;
/// Individual keychain attribute.
///
/// Wrapper for the `SecKeychainAttribute` struct. See:
/// <https://developer.apple.com/documentation/security/seckeychainattribute>
#[repr(C)]
pub(super) struct SecKeychainAttribute {
tag: SecKeychainAttrType,
length: u32,
data: *mut u8,
}
impl SecKeychainAttribute {
/// Get the `FourCharacterCode` tag identifying this attribute's type
pub(crate) fn tag(&self) -> SecKeychainAttrType {
self.tag
}
/// Get the data associated with this attribute as a byte slice.
pub(crate) fn data(&self) -> Option<&[u8]> {
if self.data.is_null() {
None
} else {
Some(unsafe { slice::from_raw_parts(self.data, self.length as usize) })
}
}
}
/// List of attributes (as returned from e.g. `SecKeychainItemCopyContent`).
///
/// NOTE: This type does not implement `Drop` as there are various ways it can
/// be allocated/deallocated. The caller must take care to free it!
///
/// Wrapper for the `SecKeychainAttributeList` struct. See:
/// <https://developer.apple.com/documentation/security/seckeychainattributelist>
#[repr(C)]
pub(super) struct SecKeychainAttributeList {
count: u32,
attr: *mut SecKeychainAttribute,
}
impl SecKeychainAttributeList {
/// Get an iterator over this attribute list.
pub(crate) fn iter(&self) -> slice::Iter<SecKeychainAttribute> {
self.as_slice().iter()
}
/// Get a slice of `Attribute` values
pub(crate) fn as_slice(&self) -> &[SecKeychainAttribute] {
unsafe { slice::from_raw_parts(self.attr, self.count as usize) }
}
}
#[link(name = "Security", kind = "framework")]
extern "C" {
pub(crate) static kSecAttrAccessControl: CFStringRef;
pub(crate) static kSecAttrAccessible: CFStringRef;
pub(crate) static kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: CFStringRef;
pub(crate) static kSecAttrAccessibleWhenUnlockedThisDeviceOnly: CFStringRef;
pub(crate) static kSecAttrAccessibleWhenUnlocked: CFStringRef;
pub(crate) static kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly: CFStringRef;
pub(crate) static kSecAttrAccessibleAfterFirstUnlock: CFStringRef;
pub(crate) static kSecAttrAccessibleAlwaysThisDeviceOnly: CFStringRef;
pub(crate) static kSecAttrAccessibleAlways: CFStringRef;
pub(crate) static kSecAttrAccount: CFStringRef;
pub(crate) static kSecAttrApplicationLabel: CFStringRef;
pub(crate) static kSecAttrApplicationTag: CFStringRef;
pub(crate) static kSecAttrCanEncrypt: CFStringRef;
pub(crate) static kSecAttrCanDecrypt: CFStringRef;
pub(crate) static kSecAttrCanDerive: CFStringRef;
pub(crate) static kSecAttrCanSign: CFStringRef;
pub(crate) static kSecAttrCanVerify: CFStringRef;
pub(crate) static kSecAttrCanWrap: CFStringRef;
pub(crate) static kSecAttrCanUnwrap: CFStringRef;
pub(crate) static kSecAttrIsExtractable: CFStringRef;
pub(crate) static kSecAttrIsPermanent: CFStringRef;
pub(crate) static kSecAttrIsSensitive: CFStringRef;
pub(crate) static kSecAttrKeyClass: CFStringRef;
pub(crate) static kSecAttrKeyClassPublic: CFStringRef;
pub(crate) static kSecAttrKeyClassPrivate: CFStringRef;
pub(crate) static kSecAttrKeyClassSymmetric: CFStringRef;
pub(crate) static kSecAttrKeyType: CFStringRef;
pub(crate) static kSecAttrKeyTypeAES: CFStringRef;
pub(crate) static kSecAttrKeyTypeRSA: CFStringRef;
pub(crate) static kSecAttrKeyTypeECSECPrimeRandom: CFStringRef;
pub(crate) static kSecAttrKeySizeInBits: CFStringRef;
pub(crate) static kSecAttrLabel: CFStringRef;
pub(crate) static kSecAttrProtocol: CFStringRef;
pub(crate) static kSecAttrProtocolFTP: CFStringRef;
pub(crate) static kSecAttrProtocolFTPAccount: CFStringRef;
pub(crate) static kSecAttrProtocolHTTP: CFStringRef;
pub(crate) static kSecAttrProtocolIRC: CFStringRef;
pub(crate) static kSecAttrProtocolNNTP: CFStringRef;
pub(crate) static kSecAttrProtocolPOP3: CFStringRef;
pub(crate) static kSecAttrProtocolSMTP: CFStringRef;
pub(crate) static kSecAttrProtocolSOCKS: CFStringRef;
pub(crate) static kSecAttrProtocolIMAP: CFStringRef;
pub(crate) static kSecAttrProtocolLDAP: CFStringRef;
pub(crate) static kSecAttrProtocolAppleTalk: CFStringRef;
pub(crate) static kSecAttrProtocolAFP: CFStringRef;
pub(crate) static kSecAttrProtocolTelnet: CFStringRef;
pub(crate) static kSecAttrProtocolSSH: CFStringRef;
pub(crate) static kSecAttrProtocolFTPS: CFStringRef;
pub(crate) static kSecAttrProtocolHTTPS: CFStringRef;
pub(crate) static kSecAttrProtocolHTTPProxy: CFStringRef;
pub(crate) static kSecAttrProtocolHTTPSProxy: CFStringRef;
pub(crate) static kSecAttrProtocolFTPProxy: CFStringRef;
pub(crate) static kSecAttrProtocolSMB: CFStringRef;
pub(crate) static kSecAttrProtocolRTSP: CFStringRef;
pub(crate) static kSecAttrProtocolRTSPProxy: CFStringRef;
pub(crate) static kSecAttrProtocolDAAP: CFStringRef;
pub(crate) static kSecAttrProtocolEPPC: CFStringRef;
pub(crate) static kSecAttrProtocolIPP: CFStringRef;
pub(crate) static kSecAttrProtocolNNTPS: CFStringRef;
pub(crate) static kSecAttrProtocolLDAPS: CFStringRef;
pub(crate) static kSecAttrProtocolTelnetS: CFStringRef;
pub(crate) static kSecAttrProtocolIMAPS: CFStringRef;
pub(crate) static kSecAttrProtocolIRCS: CFStringRef;
pub(crate) static kSecAttrProtocolPOP3S: CFStringRef;
pub(crate) static kSecAttrServer: CFStringRef;
pub(crate) static kSecAttrService: CFStringRef;
pub(crate) static kSecAttrSynchronizable: CFStringRef;
pub(crate) static kSecAttrTokenID: CFStringRef;
pub(crate) static kSecAttrTokenIDSecureEnclave: CFStringRef;
pub(crate) static kSecClass: CFStringRef;
pub(crate) static kSecClassGenericPassword: CFStringRef;
pub(crate) static kSecClassInternetPassword: CFStringRef;
pub(crate) static kSecClassCertificate: CFStringRef;
pub(crate) static kSecClassKey: CFStringRef;
pub(crate) static kSecClassIdentity: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA1AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA224AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA256AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA384AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA512AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA224AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA384AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA512AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA224AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA256AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA384AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA512AESGCM:
CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA1AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA224AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA384AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA512AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureRFC4754: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactor: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandard: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionRaw: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionPKCS1: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA224AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA384AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureRaw: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA512: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA1: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA224: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA256: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA384: CFStringRef;
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA512: CFStringRef;
pub(crate) static kSecKeyAlwaysSensitive: CFStringRef;
pub(crate) static kSecKeyDecrypt: CFStringRef;
pub(crate) static kSecKeyDerive: CFStringRef;
pub(crate) static kSecKeyEffectiveKeySize: CFStringRef;
pub(crate) static kSecKeyEncrypt: CFStringRef;
pub(crate) static kSecKeyEndDate: CFStringRef;
pub(crate) static kSecKeyExtractable: CFStringRef;
pub(crate) static kSecKeyKeySizeInBits: CFStringRef;
pub(crate) static kSecKeyKeyType: CFStringRef;
pub(crate) static kSecKeyModifiable: CFStringRef;
pub(crate) static kSecKeyNeverExtractable: CFStringRef;
pub(crate) static kSecKeyPermanent: CFStringRef;
pub(crate) static kSecKeyPrivate: CFStringRef;
pub(crate) static kSecKeySensitive: CFStringRef;
pub(crate) static kSecKeySign: CFStringRef;
pub(crate) static kSecKeyStartDate: CFStringRef;
pub(crate) static kSecKeyUnwrap: CFStringRef;
pub(crate) static kSecKeyVerify: CFStringRef;
pub(crate) static kSecKeyWrap: CFStringRef;
pub(crate) static kSecMatchLimit: CFStringRef;
pub(crate) static kSecMatchLimitOne: CFStringRef;
pub(crate) static kSecMatchLimitAll: CFStringRef;
pub(crate) static kSecPrivateKeyAttrs: CFStringRef;
pub(crate) static kSecReturnRef: CFStringRef;
pub(crate) static kSecUseKeychain: CFStringRef;
pub(crate) static kSecUseOperationPrompt: CFStringRef;
pub(crate) static kSecValueData: CFStringRef;
pub(crate) fn SecAccessControlCreateWithFlags(
allocator: CFAllocatorRef,
protection: CFTypeRef,
flags: CFOptionFlags,
error: *mut CFErrorRef,
) -> CFTypeRef;
pub(crate) fn SecAccessControlGetTypeID() -> CFTypeID;
pub(crate) fn SecCopyErrorMessageString(
status: OSStatus,
reserved: *const c_void,
) -> CFStringRef;
pub(crate) fn SecItemAdd(attributes: CFDictionaryRef, result: *mut CFTypeRef) -> OSStatus;
pub(crate) fn SecItemDelete(attributes: CFDictionaryRef) -> OSStatus;
pub(crate) fn SecItemCopyMatching(query: CFDictionaryRef, result: *mut CFTypeRef) -> OSStatus;
pub(crate) fn SecKeyCopyAttributes(key: KeyRef) -> CFDictionaryRef;
pub(crate) fn SecKeyCreateWithData(
keyData: CFDataRef,
attributes: CFDictionaryRef,
error: *mut CFErrorRef,
) -> KeyRef;
pub(crate) fn SecKeyCopyExternalRepresentation(
key: KeyRef,
error: *mut CFErrorRef,
) -> CFDataRef;
pub(crate) fn SecKeyCreateSignature(
key: KeyRef,
algorithm: CFTypeRef,
data_to_sign: CFDataRef,
error: *mut CFErrorRef,
) -> CFDataRef;
pub(crate) fn SecKeyVerifySignature(
key: KeyRef,
algorithm: CFTypeRef,
data_to_verify: CFDataRef,
signature: CFDataRef,
error: *mut CFErrorRef,
) -> u8;
pub(crate) fn SecKeyCreateEncryptedData(
key: KeyRef,
algorithm: CFTypeRef,
plaintext: CFDataRef,
error: *mut CFErrorRef,
) -> CFDataRef;
pub(crate) fn SecKeyCreateDecryptedData(
key: KeyRef,
algorithm: CFTypeRef,
ciphertext: CFDataRef,
error: *mut CFErrorRef,
) -> CFDataRef;
pub(crate) fn SecKeyGeneratePair(
parameters: CFDictionaryRef,
publicKey: *mut KeyRef,
privateKey: *mut KeyRef,
) -> OSStatus;
pub(crate) fn SecKeyCreateRandomKey(
parameters: CFDictionaryRef,
error: *mut CFErrorRef,
) -> KeyRef;
pub(crate) fn SecKeyIsAlgorithmSupported(
key: KeyRef,
operationType: CFIndex,
algorithm: CFTypeRef,
) -> u8;
pub(crate) fn SecKeyCopyPublicKey(privatekey: KeyRef) -> KeyRef;
pub(crate) fn SecKeyGetTypeID() -> CFTypeID;
pub(crate) fn SecKeychainCopyDefault(keychain: *mut KeychainRef) -> OSStatus;
pub(crate) fn SecKeychainCreate(
path_name: *const c_char,
password_length: u32,
password: *const c_char,
prompt_user: bool,
initial_access: CFTypeRef,
keychain: *mut KeychainRef,
) -> OSStatus;
pub(crate) fn SecKeychainDelete(keychain_or_array: KeychainRef) -> OSStatus;
pub(crate) fn SecKeychainGetTypeID() -> CFTypeID;
pub(crate) fn SecKeychainItemGetTypeID() -> CFTypeID;
pub(crate) fn SecKeychainItemCopyContent(
item_ref: ItemRef,
itemClass: *mut FourCharacterCode,
attr_list: *mut SecKeychainAttributeList,
data_length: *mut u32,
data_out: *mut *mut c_void,
) -> OSStatus;
pub(crate) fn SecKeychainItemFreeContent(
attr_list: *mut SecKeychainAttributeList,
data: *mut c_void,
) -> OSStatus;
}

View File

@@ -0,0 +1,83 @@
use crate::ffi::*;
use core_foundation::{base::TCFType, string::CFString};
/// Classes of keychain items supported by Keychain Services
/// (not to be confused with `SecAttrClass` or `SecType`)
///
/// Wrapper for the `kSecClass` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecclass>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Class {
/// Generic password items.
///
/// Wrapper for the `kSecClassGenericPassword` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecclassgenericpassword>
GenericPassword,
/// Internet passwords.
///
/// Wrapper for the `kSecClassInternetPassword` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecclassinternetpassword>
InternetPassword,
/// Certificates.
///
/// Wrapper for the `kSecClassCertificate` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecclasscertificate>
Certificate,
/// Cryptographic keys.
///
/// Wrapper for the `kSecClassKey` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecclasskey>
Key,
/// Identities.
///
/// Wrapper for the `kSecClassIdentity` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecclassidentity>
Identity,
}
impl Class {
/// Attempt to look up an attribute kind by its `FourCharacterCode`.
// TODO: cache `FourCharacterCodes`? e.g. as `lazy_static`
pub(crate) fn from_tag(tag: FourCharacterCode) -> Option<Self> {
let result = unsafe {
if tag == FourCharacterCode::from(kSecClassGenericPassword) {
Class::GenericPassword
} else if tag == FourCharacterCode::from(kSecClassInternetPassword) {
Class::InternetPassword
} else if tag == FourCharacterCode::from(kSecClassCertificate) {
Class::Certificate
} else if tag == FourCharacterCode::from(kSecClassKey) {
Class::Key
} else if tag == FourCharacterCode::from(kSecClassIdentity) {
Class::Identity
} else {
return None;
}
};
Some(result)
}
/// Get `CFString` containing the `kSecClass` dictionary value for
/// this particular `SecClass`.
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
Class::GenericPassword => kSecClassGenericPassword,
Class::InternetPassword => kSecClassInternetPassword,
Class::Certificate => kSecClassCertificate,
Class::Key => kSecClassKey,
Class::Identity => kSecClassIdentity,
})
}
}
}
impl From<FourCharacterCode> for Class {
fn from(tag: FourCharacterCode) -> Self {
Self::from_tag(tag).unwrap_or_else(|| panic!("invalid SecItemClass tag: {:?}", tag))
}
}

View File

@@ -0,0 +1,125 @@
//! Items stored in a keychain (e.g. certificates, keys, passwords)
mod class;
mod password;
mod query;
pub use self::{class::*, password::*, query::*};
use crate::{attr::AttrKind, error::*, ffi::*};
use core_foundation::base::TCFType;
use std::{mem, os::raw::c_void, ptr, slice};
declare_TCFType! {
/// Items stored in the keychain.
///
/// Wrapper for the `SecKeychainItem`/`SecKeychainItemRef` types:
/// <https://developer.apple.com/documentation/security/seckeychainitemref>
Item, ItemRef
}
impl_TCFType!(Item, ItemRef, SecKeychainItemGetTypeID);
impl Item {
/// Get the class of this item
pub fn class(&self) -> Class {
let mut result = FourCharacterCode::from(b"NULL");
Error::maybe_from_OSStatus(unsafe {
SecKeychainItemCopyContent(
self.as_concrete_TypeRef(),
&mut result,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
})
.unwrap();
result.into()
}
/// Get the raw data associated with this keychain item
pub(crate) fn data(&self) -> Result<Vec<u8>, Error> {
let result_ptr: *mut u8 = ptr::null_mut();
let mut length = 0;
let status = unsafe {
SecKeychainItemCopyContent(
self.as_concrete_TypeRef(),
ptr::null_mut(),
ptr::null_mut(),
&mut length,
&mut (result_ptr as *mut c_void),
)
};
if let Some(e) = Error::maybe_from_OSStatus(status) {
Err(e)
} else if result_ptr.is_null() {
Err(Error::new(
ErrorKind::MissingEntitlement,
"SecKeychainItemCopyContent refused to return data",
))
} else {
// Copy the data into a vector we've allocated
let result = Vec::from(unsafe { slice::from_raw_parts(result_ptr, length as usize) });
// Free the original data
Error::maybe_from_OSStatus(unsafe {
SecKeychainItemFreeContent(ptr::null_mut(), result_ptr as *mut c_void)
})
.unwrap();
Ok(result)
}
}
/// Get an attribute of this item as a `String`.
// TODO: handle attribute types other than `String`?
pub(crate) fn attribute(&self, attr_kind: AttrKind) -> Result<String, Error> {
let mut attrs = unsafe { self.attributes() }?;
let result = attrs
.iter()
.find(|attr| {
if let Some(kind) = AttrKind::from_tag(attr.tag()) {
kind == attr_kind
} else {
false
}
})
.map(|attr| String::from_utf8(attr.data().unwrap().into()).unwrap());
Error::maybe_from_OSStatus(unsafe {
SecKeychainItemFreeContent(&mut attrs, ptr::null_mut())
})
.unwrap();
result.ok_or_else(|| {
Error::new(
ErrorKind::NoSuchAttr,
&format!("missing attribute {:?}", attr_kind),
)
})
}
/// Get the attributes of a keychain item. Note that this does not handle
/// deallocating the attribute list so the caller must take care to do so.
unsafe fn attributes(&self) -> Result<SecKeychainAttributeList, Error> {
let mut result: SecKeychainAttributeList = mem::zeroed();
let status = SecKeychainItemCopyContent(
self.as_concrete_TypeRef(),
ptr::null_mut(),
&mut result,
ptr::null_mut(),
ptr::null_mut(),
);
if let Some(e) = Error::maybe_from_OSStatus(status) {
Err(e)
} else {
Ok(result)
}
}
}

View File

@@ -0,0 +1,140 @@
use crate::{attr::*, dictionary::DictionaryBuilder, error::Error, ffi::*, keychain::*};
use std::str;
use zeroize::Zeroize;
/// Generic passwords
pub struct GenericPassword(Item);
impl GenericPassword {
/// Create a new generic password item in the given keychain.
pub fn create(
keychain: &Keychain,
service: &str,
account: &str,
password: &str,
) -> Result<Self, Error> {
let mut attrs = DictionaryBuilder::new();
attrs.add_class(item::Class::GenericPassword);
attrs.add_string(AttrKind::Service, service);
attrs.add_string(AttrKind::Account, account);
attrs.add_string(unsafe { kSecValueData }, password);
Ok(GenericPassword(keychain.add_item(attrs)?))
}
/// Find a generic password in the given keychain.
pub fn find(keychain: &Keychain, service: &str, account: &str) -> Result<Self, Error> {
let mut attrs = DictionaryBuilder::new();
attrs.add_class(item::Class::GenericPassword);
attrs.add_string(AttrKind::Service, service);
attrs.add_string(AttrKind::Account, account);
Ok(GenericPassword(keychain.find_item(attrs)?))
}
/// Get the account this password is associated with
pub fn account(&self) -> Result<String, Error> {
self.0.attribute(AttrKind::Account)
}
/// Get the service this password is associated with
pub fn service(&self) -> Result<String, Error> {
self.0.attribute(AttrKind::Service)
}
/// Get the raw password value
pub fn password(&self) -> Result<PasswordData, Error> {
Ok(PasswordData(self.0.data()?))
}
}
/// Internet passwords
pub struct InternetPassword(Item);
impl InternetPassword {
/// Create a new Internet password item in the given keychain.
pub fn create(
keychain: &Keychain,
server: &str,
account: &str,
password: &str,
) -> Result<Self, Error> {
let mut attrs = DictionaryBuilder::new();
attrs.add_class(item::Class::InternetPassword);
attrs.add_string(AttrKind::Server, server);
attrs.add_string(AttrKind::Account, account);
attrs.add_string(unsafe { kSecValueData }, password);
Ok(InternetPassword(keychain.add_item(attrs)?))
}
/// Find an Internet password in the given keychain.
pub fn find(
keychain: &Keychain,
server: &str,
account: &str,
protocol: Option<AttrProtocol>,
) -> Result<Self, Error> {
let mut attrs = DictionaryBuilder::new();
attrs.add_class(item::Class::InternetPassword);
attrs.add_string(AttrKind::Server, server);
attrs.add_string(AttrKind::Account, account);
if let Some(proto) = protocol {
attrs.add_attr(&proto);
}
Ok(InternetPassword(keychain.find_item(attrs)?))
}
/// Get the account this password is associated with
pub fn account(&self) -> Result<String, Error> {
self.0.attribute(AttrKind::Account)
}
/// Get the service this password is associated with
pub fn server(&self) -> Result<String, Error> {
self.0.attribute(AttrKind::Server)
}
/// Get the raw password value
pub fn password(&self) -> Result<PasswordData, Error> {
Ok(PasswordData(self.0.data()?))
}
}
/// Wrapper around password data that ensures it is cleared from memory after
/// being used.
#[derive(Clone)]
pub struct PasswordData(Vec<u8>);
impl PasswordData {
/// Borrow the password as a byte slice
pub fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
/// Borrow the password as a `str` (if valid UTF-8), panicking if the
/// UTF-8 conversion fails.
pub fn as_str(&self) -> &str {
self.try_as_str().expect("password contained invalid UTF-8")
}
/// Borrow the password as a `str` (if valid UTF-8), returning a
/// `Utf8Error` if the UTF-8 conversion failed.
pub fn try_as_str(&self) -> Result<&str, str::Utf8Error> {
str::from_utf8(self.as_bytes())
}
}
impl AsRef<[u8]> for PasswordData {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Drop for PasswordData {
fn drop(&mut self) {
self.0.zeroize();
}
}

View File

@@ -0,0 +1,170 @@
//! Query the keychain, looking for particular items
use crate::{attr::*, dictionary::DictionaryBuilder, ffi::*};
use core_foundation::{
base::{CFType, TCFType},
number::CFNumber,
string::CFString,
};
/// Limit the number of matched items to one or an unlimited number.
///
/// Wrapper for the `kSecMatchLimit` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecmatchlimit>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MatchLimit {
/// Match exactly one item.
///
/// Wrapper for the `kSecMatchLimitOne` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecmatchlimitone>
One,
/// Match the specified number of items.
///
/// Equivalent to passing a `CFNumberRef` as the value for
/// `kSecMatchLimit`. See:
/// <https://developer.apple.com/documentation/security/ksecmatchlimit>
Number(usize),
/// Match an unlimited number of items.
///
/// Wrapper for the `kSecMatchLimitAll` attribute value. See:
/// <https://developer.apple.com/documentation/security/ksecmatchlimitall>
All,
}
impl MatchLimit {
/// Get `CFType` containing the `kSecMatchLimit` dictionary value for
/// this particular `SecMatchLimit`.
pub fn as_CFType(self) -> CFType {
match self {
MatchLimit::One => {
unsafe { CFString::wrap_under_get_rule(kSecMatchLimitOne) }.as_CFType()
}
MatchLimit::Number(n) => CFNumber::from(n as i64).as_CFType(),
MatchLimit::All => {
unsafe { CFString::wrap_under_get_rule(kSecMatchLimitAll) }.as_CFType()
}
}
}
}
/// Query builder for locating particular keychain items.
///
/// For more information, see "Search Attribute Keys and Values":
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/search_attribute_keys_and_values>
#[derive(Default, Debug)]
pub struct Query(DictionaryBuilder);
impl Query {
/// Create a new keychain item query builder
pub fn new() -> Self {
Self::default()
}
/// Query for keychain items with the provided `SecAttrApplicationLabel`
/// (not to be confused with a `SecAttrLabel`), i.e. the hash/fingerprint
/// of a public key in the keychain.
///
/// Both the private and public key in a keypair have a
/// `SecAttrApplicationLabel` set to the public key's fingerprint.
///
/// Wrapper for the `kSecAttrApplicationLabel` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
pub fn application_label<L: Into<AttrApplicationLabel>>(mut self, label: L) -> Self {
self.0.add_attr(&label.into());
self
}
/// Query for keychain items with the provided `SecAttrApplicationTag`.
///
/// Wrapper for the `kSecAttrApplicationTag` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
pub fn application_tag<T>(mut self, tag: T) -> Self
where
T: Into<AttrApplicationTag>,
{
self.0.add_attr(&tag.into());
self
}
/// Query for keys with the given `SecAttrKeyClass`.
///
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
pub fn key_class(mut self, key_class: AttrKeyClass) -> Self {
self.0.add_attr(&key_class);
self
}
/// Query for keys with the given `SecAttrKeyType`.
///
/// Wrapper for the `kSecAttrKeyType` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeytype>
pub fn key_type(mut self, key_type: AttrKeyType) -> Self {
self.0.add_attr(&key_type);
self
}
/// Query for a particular (human-meaningful) label on keys
///
/// Wrapper for the `kSecAttrLabel` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
pub fn label<L: Into<AttrLabel>>(mut self, label: L) -> Self {
self.0.add_attr(&label.into());
self
}
/// Query for keys which are or not permanent members of the default keychain.
///
/// Wrapper for the `kSecAttrIsPermanent` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrispermanent>
pub fn permanent(mut self, value: bool) -> Self {
self.0.add_boolean(AttrKind::Permanent, value);
self
}
/// Query for keys which are or are not synchronizable.
///
/// Wrapper for the `kSecAttrSynchronizable` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrsynchronizable>
pub fn synchronizable(mut self, value: bool) -> Self {
self.0.add_boolean(AttrKind::Synchronizable, value);
self
}
/// Query for keys which are or are not sensitive.
///
/// Wrapper for the `kSecAttrIsSensitive` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrissensitive>
pub fn sensitive(mut self, value: bool) -> Self {
self.0.add_boolean(AttrKind::Sensitive, value);
self
}
/// Query for keys stored in an external token i.e. the
/// Secure Enclave Processor (SEP).
///
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
pub fn token_id(mut self, value: AttrTokenId) -> Self {
self.0.add_attr(&value);
self
}
/// Prompt the user with the given custom message when using keys returned
/// from this query.
///
/// Wrapper for the `kSecUseOperationPrompt`. See:
/// <https://developer.apple.com/documentation/security/ksecuseoperationprompt>
pub fn use_operation_prompt(mut self, value: &str) -> Self {
self.0.add_string(unsafe { kSecUseOperationPrompt }, value);
self
}
}
impl From<Query> for DictionaryBuilder {
fn from(params: Query) -> DictionaryBuilder {
params.0
}
}

View File

@@ -0,0 +1,451 @@
use crate::ffi::*;
use core_foundation::{base::TCFType, string::CFString};
/// Cryptographic algorithms for use with keys stored in the keychain.
///
/// Wrapper for `SecKeyAlgorithm`. See:
/// <https://developer.apple.com/documentation/security/seckeyalgorithm>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum KeyAlgorithm {
/// Elliptic Curve Encryption Standard X963
ECIESEncryptionStandardX963SHA1AESGCM,
/// Elliptic Curve Encryption Standard X963
ECIESEncryptionStandardX963SHA224AESGCM,
/// Elliptic Curve Encryption Standard X963
ECIESEncryptionStandardX963SHA256AESGCM,
/// Elliptic Curve Encryption Standard X963
ECIESEncryptionStandardX963SHA384AESGCM,
/// Elliptic Curve Encryption Standard X963
ECIESEncryptionStandardX963SHA512AESGCM,
/// Elliptic Curve Encryption Standard Variable IVX963
ECIESEncryptionStandardVariableIVX963SHA224AESGCM,
/// Elliptic Curve Encryption Standard Variable IVX963
ECIESEncryptionStandardVariableIVX963SHA256AESGCM,
/// Elliptic Curve Encryption Standard Variable IVX963
ECIESEncryptionStandardVariableIVX963SHA384AESGCM,
/// Elliptic Curve Encryption Standard Variable IVX963
ECIESEncryptionStandardVariableIVX963SHA512AESGCM,
/// Elliptic Curve Encryption Cofactor Variable IVX963
ECIESEncryptionCofactorVariableIVX963SHA224AESGCM,
/// Elliptic Curve Encryption Cofactor Variable IVX963
ECIESEncryptionCofactorVariableIVX963SHA256AESGCM,
/// Elliptic Curve Encryption Cofactor Variable IVX963
ECIESEncryptionCofactorVariableIVX963SHA384AESGCM,
/// Elliptic Curve Encryption Cofactor Variable IVX963
ECIESEncryptionCofactorVariableIVX963SHA512AESGCM,
/// Elliptic Curve Encryption Cofactor X963
ECIESEncryptionCofactorX963SHA1AESGCM,
/// Elliptic Curve Encryption Cofactor X963
ECIESEncryptionCofactorX963SHA224AESGCM,
/// Elliptic Curve Encryption Cofactor X963
ECIESEncryptionCofactorX963SHA256AESGCM,
/// Elliptic Curve Encryption Cofactor X963
ECIESEncryptionCofactorX963SHA384AESGCM,
/// Elliptic Curve Encryption Cofactor X963
ECIESEncryptionCofactorX963SHA512AESGCM,
/// Elliptic Curve Signature RFC4754
ECDSASignatureRFC4754,
/// Elliptic Curve Signature Digest X962
ECDSASignatureDigestX962,
/// Elliptic Curve Signature Digest X962
ECDSASignatureDigestX962SHA1,
/// Elliptic Curve Signature Digest X962
ECDSASignatureDigestX962SHA224,
/// Elliptic Curve Signature Digest X962
ECDSASignatureDigestX962SHA256,
/// Elliptic Curve Signature Digest X962
ECDSASignatureDigestX962SHA384,
/// Elliptic Curve Signature Digest X962
ECDSASignatureDigestX962SHA512,
/// Elliptic Curve Signature Message X962
ECDSASignatureMessageX962SHA1,
/// Elliptic Curve Signature Digest X962
ECDSASignatureMessageX962SHA224,
/// Elliptic Curve Signature Digest X962
ECDSASignatureMessageX962SHA256,
/// Elliptic Curve Signature Digest X962
ECDSASignatureMessageX962SHA384,
/// Elliptic Curve Signature Digest X962
ECDSASignatureMessageX962SHA512,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeCofactor,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeStandard,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeCofactorX963SHA1,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeStandardX963SHA1,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeCofactorX963SHA224,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeCofactorX963SHA256,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeCofactorX963SHA384,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeCofactorX963SHA512,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeStandardX963SHA224,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeStandardX963SHA256,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeStandardX963SHA384,
/// Elliptic Curve Key Exchange
ECDHKeyExchangeStandardX963SHA512,
/// RSA Encryption
RSAEncryptionRaw,
/// RSA Encryption
RSAEncryptionPKCS1,
/// RSA Encryption OAEP
RSAEncryptionOAEPSHA1,
/// RSA Encryption OAEP
RSAEncryptionOAEPSHA224,
/// RSA Encryption OAEP
RSAEncryptionOAEPSHA256,
/// RSA Encryption OAEP
RSAEncryptionOAEPSHA384,
/// RSA Encryption OAEP
RSAEncryptionOAEPSHA512,
/// RSA Encryption OAEP AES-GCM
RSAEncryptionOAEPSHA1AESGCM,
/// RSA Encryption OAEP AES-GCM
RSAEncryptionOAEPSHA224AESGCM,
/// RSA Encryption OAEP AES-GCM
RSAEncryptionOAEPSHA256AESGCM,
/// RSA Encryption OAEP AES-GCM
RSAEncryptionOAEPSHA384AESGCM,
/// RSA Encryption OAEP AES-GCM
RSAEncryptionOAEPSHA512AESGCM,
/// RSA Signature Raw
RSASignatureRaw,
/// RSA Signature Digest PKCS1v15
RSASignatureDigestPKCS1v15Raw,
/// RSA Signature Digest PKCS1v15
RSASignatureDigestPKCS1v15SHA1,
/// RSA Signature Digest PKCS1v15
RSASignatureDigestPKCS1v15SHA224,
/// RSA Signature Digest PKCS1v15
RSASignatureDigestPKCS1v15SHA256,
/// RSA Signature Digest PKCS1v15
RSASignatureDigestPKCS1v15SHA384,
/// RSA Signature Digest PKCS1v15
RSASignatureDigestPKCS1v15SHA512,
/// RSA Signature Message PKCS1v15
RSASignatureMessagePKCS1v15SHA1,
/// RSA Signature Digest PKCS1v15
RSASignatureMessagePKCS1v15SHA224,
/// RSA Signature Digest PKCS1v15
RSASignatureMessagePKCS1v15SHA256,
/// RSA Signature Digest PKCS1v15
RSASignatureMessagePKCS1v15SHA384,
/// RSA Signature Digest PKCS1v15
RSASignatureMessagePKCS1v15SHA512,
/// RSA Signature Digest PSS
RSASignatureDigestPSSSHA1,
/// RSA Signature Digest PSS
RSASignatureDigestPSSSHA224,
/// RSA Signature Digest PSS
RSASignatureDigestPSSSHA256,
/// RSA Signature Digest PSS
RSASignatureDigestPSSSHA384,
/// RSA Signature Digest PSS
RSASignatureDigestPSSSHA512,
/// RSA Signature Message PSS
RSASignatureMessagePSSSHA1,
/// RSA Signature Message PSS
RSASignatureMessagePSSSHA224,
/// RSA Signature Message PSS
RSASignatureMessagePSSSHA256,
/// RSA Signature Message PSS
RSASignatureMessagePSSSHA384,
/// RSA Signature Message PSS
RSASignatureMessagePSSSHA512,
}
impl KeyAlgorithm {
/// Get `CFString` containing the `kSecKeyAlgorithm` dictionary value for
/// a particular cryptographic algorithm.
pub fn as_CFString(self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(match self {
KeyAlgorithm::ECIESEncryptionStandardX963SHA1AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardX963SHA1AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardX963SHA224AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardX963SHA224AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardX963SHA256AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardX963SHA256AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardX963SHA384AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardX963SHA384AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardX963SHA512AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardX963SHA512AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA224AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA224AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA256AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA384AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA384AESGCM
}
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA512AESGCM => {
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA512AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA224AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA224AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA256AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA256AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA384AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA384AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA512AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA512AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorX963SHA1AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA1AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorX963SHA224AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA224AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorX963SHA256AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorX963SHA384AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA384AESGCM
}
KeyAlgorithm::ECIESEncryptionCofactorX963SHA512AESGCM => {
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA512AESGCM
}
KeyAlgorithm::ECDSASignatureRFC4754 => kSecKeyAlgorithmECDSASignatureRFC4754,
KeyAlgorithm::ECDSASignatureDigestX962 => kSecKeyAlgorithmECDSASignatureDigestX962,
KeyAlgorithm::ECDSASignatureDigestX962SHA1 => {
kSecKeyAlgorithmECDSASignatureDigestX962SHA1
}
KeyAlgorithm::ECDSASignatureDigestX962SHA224 => {
kSecKeyAlgorithmECDSASignatureDigestX962SHA224
}
KeyAlgorithm::ECDSASignatureDigestX962SHA256 => {
kSecKeyAlgorithmECDSASignatureDigestX962SHA256
}
KeyAlgorithm::ECDSASignatureDigestX962SHA384 => {
kSecKeyAlgorithmECDSASignatureDigestX962SHA384
}
KeyAlgorithm::ECDSASignatureDigestX962SHA512 => {
kSecKeyAlgorithmECDSASignatureDigestX962SHA512
}
KeyAlgorithm::ECDSASignatureMessageX962SHA1 => {
kSecKeyAlgorithmECDSASignatureMessageX962SHA1
}
KeyAlgorithm::ECDSASignatureMessageX962SHA224 => {
kSecKeyAlgorithmECDSASignatureMessageX962SHA224
}
KeyAlgorithm::ECDSASignatureMessageX962SHA256 => {
kSecKeyAlgorithmECDSASignatureMessageX962SHA256
}
KeyAlgorithm::ECDSASignatureMessageX962SHA384 => {
kSecKeyAlgorithmECDSASignatureMessageX962SHA384
}
KeyAlgorithm::ECDSASignatureMessageX962SHA512 => {
kSecKeyAlgorithmECDSASignatureMessageX962SHA512
}
KeyAlgorithm::ECDHKeyExchangeCofactor => kSecKeyAlgorithmECDHKeyExchangeCofactor,
KeyAlgorithm::ECDHKeyExchangeStandard => kSecKeyAlgorithmECDHKeyExchangeStandard,
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA1 => {
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA1
}
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA1 => {
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1
}
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA224 => {
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA224
}
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA256 => {
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA256
}
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA384 => {
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA384
}
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA512 => {
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA512
}
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA224 => {
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA224
}
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA256 => {
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA256
}
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA384 => {
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA384
}
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA512 => {
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA512
}
KeyAlgorithm::RSAEncryptionRaw => kSecKeyAlgorithmRSAEncryptionRaw,
KeyAlgorithm::RSAEncryptionPKCS1 => kSecKeyAlgorithmRSAEncryptionPKCS1,
KeyAlgorithm::RSAEncryptionOAEPSHA1 => kSecKeyAlgorithmRSAEncryptionOAEPSHA1,
KeyAlgorithm::RSAEncryptionOAEPSHA224 => kSecKeyAlgorithmRSAEncryptionOAEPSHA224,
KeyAlgorithm::RSAEncryptionOAEPSHA256 => kSecKeyAlgorithmRSAEncryptionOAEPSHA256,
KeyAlgorithm::RSAEncryptionOAEPSHA384 => kSecKeyAlgorithmRSAEncryptionOAEPSHA384,
KeyAlgorithm::RSAEncryptionOAEPSHA512 => kSecKeyAlgorithmRSAEncryptionOAEPSHA512,
KeyAlgorithm::RSAEncryptionOAEPSHA1AESGCM => {
kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM
}
KeyAlgorithm::RSAEncryptionOAEPSHA224AESGCM => {
kSecKeyAlgorithmRSAEncryptionOAEPSHA224AESGCM
}
KeyAlgorithm::RSAEncryptionOAEPSHA256AESGCM => {
kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM
}
KeyAlgorithm::RSAEncryptionOAEPSHA384AESGCM => {
kSecKeyAlgorithmRSAEncryptionOAEPSHA384AESGCM
}
KeyAlgorithm::RSAEncryptionOAEPSHA512AESGCM => {
kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM
}
KeyAlgorithm::RSASignatureRaw => kSecKeyAlgorithmRSASignatureRaw,
KeyAlgorithm::RSASignatureDigestPKCS1v15Raw => {
kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw
}
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA1 => {
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
}
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA224 => {
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA224
}
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA256 => {
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256
}
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA384 => {
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384
}
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA512 => {
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512
}
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA1 => {
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1
}
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA224 => {
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224
}
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA256 => {
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256
}
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA384 => {
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384
}
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA512 => {
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512
}
KeyAlgorithm::RSASignatureDigestPSSSHA1 => {
kSecKeyAlgorithmRSASignatureDigestPSSSHA1
}
KeyAlgorithm::RSASignatureDigestPSSSHA224 => {
kSecKeyAlgorithmRSASignatureDigestPSSSHA224
}
KeyAlgorithm::RSASignatureDigestPSSSHA256 => {
kSecKeyAlgorithmRSASignatureDigestPSSSHA256
}
KeyAlgorithm::RSASignatureDigestPSSSHA384 => {
kSecKeyAlgorithmRSASignatureDigestPSSSHA384
}
KeyAlgorithm::RSASignatureDigestPSSSHA512 => {
kSecKeyAlgorithmRSASignatureDigestPSSSHA512
}
KeyAlgorithm::RSASignatureMessagePSSSHA1 => {
kSecKeyAlgorithmRSASignatureMessagePSSSHA1
}
KeyAlgorithm::RSASignatureMessagePSSSHA224 => {
kSecKeyAlgorithmRSASignatureMessagePSSSHA224
}
KeyAlgorithm::RSASignatureMessagePSSSHA256 => {
kSecKeyAlgorithmRSASignatureMessagePSSSHA256
}
KeyAlgorithm::RSASignatureMessagePSSSHA384 => {
kSecKeyAlgorithmRSASignatureMessagePSSSHA384
}
KeyAlgorithm::RSASignatureMessagePSSSHA512 => {
kSecKeyAlgorithmRSASignatureMessagePSSSHA512
}
})
}
}
}

View File

@@ -0,0 +1,313 @@
//! Keys stored in macOS Keychain Services.
mod algorithm;
mod operation;
mod pair;
pub use self::{algorithm::*, operation::*, pair::*};
use crate::{
attr::*,
ciphertext::Ciphertext,
dictionary::{Dictionary, DictionaryBuilder},
error::Error,
ffi::*,
keychain::item::{self, MatchLimit},
signature::Signature,
};
use core_foundation::{
base::{CFIndexConvertible, CFTypeRef, TCFType},
data::{CFData, CFDataRef},
error::CFErrorRef,
string::{CFString, CFStringRef},
};
use std::{
fmt::{self, Debug},
ptr,
};
declare_TCFType! {
/// Object which represents a cryptographic key.
///
/// Wrapper for the `SecKey`/`SecKeyRef` types:
/// <https://developer.apple.com/documentation/security/seckeyref>
Key, KeyRef
}
impl_TCFType!(Key, KeyRef, SecKeyGetTypeID);
impl Key {
/// Find a `Key` in the keyring using the given `ItemQuery`.
///
/// Wrapper for `SecItemCopyMatching`. See:
/// <https://developer.apple.com/documentation/security/1398306-secitemcopymatching>
pub fn find(query: item::Query) -> Result<Self, Error> {
let mut params = DictionaryBuilder::from(query);
params.add(unsafe { kSecClass }, &item::Class::Key.as_CFString());
params.add(unsafe { kSecMatchLimit }, &MatchLimit::One.as_CFType());
params.add_boolean(unsafe { kSecReturnRef }, true);
let mut result: KeyRef = ptr::null_mut();
let status = unsafe {
SecItemCopyMatching(
Dictionary::from(params).as_concrete_TypeRef(),
&mut result as &mut CFTypeRef,
)
};
// Return an error if the status was unsuccessful
if let Some(e) = Error::maybe_from_OSStatus(status) {
return Err(e);
}
Ok(unsafe { Key::wrap_under_create_rule(result) })
}
/// Get the `AttrApplicationLabel` for this `Key`.
pub fn application_label(&self) -> Option<AttrApplicationLabel> {
self.attributes()
.find(AttrKind::ApplicationLabel)
.map(|tag| {
AttrApplicationLabel(unsafe {
CFData::wrap_under_get_rule(tag.as_CFTypeRef() as CFDataRef)
})
})
}
/// Get the `AttrApplicationTag` for this `Key`.
pub fn application_tag(&self) -> Option<AttrApplicationTag> {
self.attributes().find(AttrKind::ApplicationTag).map(|tag| {
AttrApplicationTag(unsafe {
CFData::wrap_under_get_rule(tag.as_CFTypeRef() as CFDataRef)
})
})
}
/// Get the `AttrLabel` for this `Key`.
pub fn label(&self) -> Option<AttrLabel> {
self.attributes().find(AttrKind::Label).map(|label| {
AttrLabel(unsafe { CFString::wrap_under_get_rule(label.as_CFTypeRef() as CFStringRef) })
})
}
/// Get the `AttrKeyClass` for this `Key`.
pub fn class(&self) -> Option<AttrKeyClass> {
self.attributes()
.find(AttrKind::KeyClass)
.map(|class| AttrKeyClass::from(class.as_CFTypeRef() as CFStringRef))
}
/// Get the `AttrKeyType` for this `Key`.
pub fn key_type(&self) -> Option<AttrKeyType> {
self.attributes()
.find(AttrKind::KeyType)
.map(|keytype| AttrKeyType::from(keytype.as_CFTypeRef() as CFStringRef))
}
/// Determine whether a key is suitable for an operation using a certain algorithm
///
/// Wrapper for the `SecKeyIsAlgorithmSupported` function. See:
/// <https://developer.apple.com/documentation/security/1644057-seckeyisalgorithmsupported>
pub fn is_supported(&self, operation: KeyOperation, alg: KeyAlgorithm) -> bool {
let res = unsafe {
SecKeyIsAlgorithmSupported(
self.as_concrete_TypeRef(),
operation.to_CFIndex(),
alg.as_CFString().as_CFTypeRef(),
)
};
res == 1
}
/// Create a cryptographic signature of the given data using this key.
///
/// Wrapper for the `SecKeyCreateSignature` function. See:
/// <https://developer.apple.com/documentation/security/1643916-seckeycreatesignature>
pub fn sign(&self, alg: KeyAlgorithm, data: &[u8]) -> Result<Signature, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let signature = unsafe {
SecKeyCreateSignature(
self.as_concrete_TypeRef(),
alg.as_CFString().as_CFTypeRef(),
CFData::from_buffer(data).as_concrete_TypeRef(),
&mut error,
)
};
if error.is_null() {
let bytes = unsafe { CFData::wrap_under_create_rule(signature) }.to_vec();
Ok(Signature::new(alg, bytes))
} else {
Err(error.into())
}
}
/// Verifies the cryptographic signature of the given data using this key.
///
/// Wrapper for the `SecKeyVerifySignature` function. See:
/// <https://developer.apple.com/documentation/security/1643715-seckeyverifysignature>
pub fn verify(&self, signed_data: &[u8], signature: &Signature) -> Result<bool, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let result = unsafe {
SecKeyVerifySignature(
self.as_concrete_TypeRef(),
signature.algorithm().as_CFString().as_CFTypeRef(),
CFData::from_buffer(signed_data).as_concrete_TypeRef(),
CFData::from_buffer(signature.as_bytes()).as_concrete_TypeRef(),
&mut error,
)
};
if error.is_null() {
Ok(result == 0x1)
} else {
Err(error.into())
}
}
/// Encrypts a block of data using a public key and specified algorithm
///
/// Wrapper for the `SecKeyCreateEncryptedData` function. See:
/// <https://developer.apple.com/documentation/security/1643957-seckeycreateencrypteddata>
pub fn encrypt(&self, alg: KeyAlgorithm, plaintext: &[u8]) -> Result<Ciphertext, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let ciphertext = unsafe {
SecKeyCreateEncryptedData(
self.as_concrete_TypeRef(),
alg.as_CFString().as_CFTypeRef(),
CFData::from_buffer(plaintext).as_concrete_TypeRef(),
&mut error,
)
};
if error.is_null() {
let bytes = unsafe { CFData::wrap_under_create_rule(ciphertext) }.to_vec();
Ok(Ciphertext::new(alg, bytes))
} else {
Err(error.into())
}
}
/// Decrypts a block of data using a private key and specified algorithm
///
/// Wrapper for the `SecKeyCreateDecryptedData` function. See:
/// <https://developer.apple.com/documentation/security/1644043-seckeycreatedecrypteddata>
pub fn decrypt(&self, ciphertext: Ciphertext) -> Result<Vec<u8>, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let plaintext = unsafe {
SecKeyCreateDecryptedData(
self.as_concrete_TypeRef(),
ciphertext.algorithm().as_CFString().as_CFTypeRef(),
CFData::from_buffer(ciphertext.as_ref()).as_concrete_TypeRef(),
&mut error,
)
};
if error.is_null() {
let bytes = unsafe { CFData::wrap_under_create_rule(plaintext) }.to_vec();
Ok(bytes)
} else {
Err(error.into())
}
}
/// Delete this key from the keychain
///
/// Wrapper for `SecItemDelete` function. See:
/// <https://developer.apple.com/documentation/security/1395547-secitemdelete>
pub fn delete(self) -> Result<(), Error> {
let mut query = DictionaryBuilder::new();
let key_class = self.class().unwrap();
query.add(unsafe { kSecClass }, &item::Class::Key.as_CFString());
query.add(unsafe { kSecAttrKeyClass }, &key_class.as_CFString());
if key_class == AttrKeyClass::Public {
query.add(unsafe { kSecAttrKeyType }, &self.key_type().unwrap().as_CFString());
query.add(
unsafe { kSecAttrApplicationTag },
&self.application_tag().unwrap().as_CFType(),
);
} else if key_class == AttrKeyClass::Private {
query.add(
unsafe { kSecAttrApplicationLabel },
&self.application_label().unwrap().as_CFType(),
);
query.add_boolean(unsafe { kSecReturnRef }, true);
}
let status = unsafe { SecItemDelete(Dictionary::from(query).as_concrete_TypeRef()) };
if let Some(e) = Error::maybe_from_OSStatus(status) {
Err(e)
} else {
Ok(())
}
}
/// Export this key as an external representation.
///
/// If the key is not exportable the operation will fail (e.g. if it
/// was generated inside of the Secure Enclave, or if the "Extractable"
/// flag is set to NO).
///
/// The data returned depends on the key type:
///
/// - RSA: PKCS#1 format
/// - EC: ANSI X9.63 bytestring:
/// - Public key: `04 || X || Y`
/// - Private key: Concatenation of public key with big endian encoding
/// of the secret scalar, i.e. `04 || X || Y || K`
///
/// All representations use fixed-size integers with leading zeroes.
///
/// Wrapper for the `SecKeyCopyExternalRepresentation` function. See:
/// <https://developer.apple.com/documentation/security/1643698-seckeycopyexternalrepresentation>
pub fn to_external_representation(&self) -> Result<Vec<u8>, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let data =
unsafe { SecKeyCopyExternalRepresentation(self.as_concrete_TypeRef(), &mut error) };
if error.is_null() {
Ok(unsafe { CFData::wrap_under_create_rule(data) }.to_vec())
} else {
Err(error.into())
}
}
/// Restores a key from an external representation of that key.
///
/// Wrapper for the `SecKeyCreateWithData` function. See:
/// <https://developer.apple.com/documentation/security/1643701-seckeycreatewithdata>
pub fn from_external_representation(params: RestoreKeyParams) -> Result<Self, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let data = unsafe {
SecKeyCreateWithData(
CFData::from_buffer(params.as_bytes()).as_concrete_TypeRef(),
params.attributes().as_concrete_TypeRef(),
&mut error,
)
};
if error.is_null() {
Ok(unsafe { Key::wrap_under_create_rule(data) })
} else {
Err(error.into())
}
}
/// Fetch attributes for this `Key`.
///
/// Wrapper for `SecKeyCopyAttributes`. See:
/// <https://developer.apple.com/documentation/security/1643699-seckeycopyattributes>
fn attributes(&self) -> Dictionary {
unsafe { Dictionary::wrap_under_get_rule(SecKeyCopyAttributes(self.as_concrete_TypeRef())) }
}
}
impl Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"SecKey {{ application_label: {:?}, application_tag: {:?}, label: {:?} }}",
self.application_label(),
self.application_tag(),
self.label()
)
}
}

View File

@@ -0,0 +1,33 @@
use core_foundation::base::{CFIndex, CFIndexConvertible};
use self::KeyOperation::*;
/// Types of operations that a cryptographic key can perform
///
/// Wrapper for `SecKeyOperationType`. See:
/// <https://developer.apple.com/documentation/security/seckeyoperationtype>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum KeyOperation {
/// Decrypt operation
Decrypt,
/// Encrypt operation
Encrypt,
/// KeyExchange operation
KeyExchange,
/// Sign operation
Sign,
/// Verify operation
Verify,
}
impl CFIndexConvertible for KeyOperation {
fn to_CFIndex(self) -> CFIndex {
let i = match self {
Decrypt => 3,
Encrypt => 2,
KeyExchange => 4,
Sign => 0,
Verify => 1,
};
i as CFIndex
}
}

View File

@@ -0,0 +1,305 @@
use super::*;
use crate::{access::AccessControl, dictionary::*, error::Error};
use core_foundation::base::TCFType;
use std::ptr;
/// Public key pairs (i.e. public and private key) stored in the keychain.
#[derive(Debug)]
pub struct KeyPair {
/// Public key
pub public_key: Key,
/// Private key
pub private_key: Key,
}
impl KeyPair {
/// An asymmetric cryptographic key pair is composed of a public and a private key that are generated together.
/// The public key can be distributed freely, but keep the private key secret.
/// One or both may be stored in a keychain for safekeeping.
///
/// Wrapper for the `SecKeyCreateRandomKey` function see:
/// <https://developer.apple.com/documentation/security/1823694-seckeycreaterandomkey>
pub fn create(params: KeyPairGenerateParams) -> Result<KeyPair, Error> {
let mut error: CFErrorRef = ptr::null_mut();
let private_key_ref: KeyRef = unsafe {
SecKeyCreateRandomKey(Dictionary::from(params).as_concrete_TypeRef(), &mut error)
};
if private_key_ref.is_null() {
Err(error.into())
} else {
let public_key_ref = unsafe { SecKeyCopyPublicKey(private_key_ref) };
assert!(!public_key_ref.is_null());
assert!(!private_key_ref.is_null());
Ok(unsafe {
KeyPair {
public_key: Key::wrap_under_create_rule(public_key_ref),
private_key: Key::wrap_under_create_rule(private_key_ref),
}
})
}
}
/// Generate a public/private `KeyPair` using the given
/// `GeneratePairParams`.
///
/// Wrapper for the `SecKeyGeneratePair` function. See:
/// <https://developer.apple.com/documentation/security/1395339-seckeygeneratepair>
pub fn generate(params: KeyPairGenerateParams) -> Result<KeyPair, Error> {
let mut public_key_ref: KeyRef = ptr::null_mut();
let mut private_key_ref: KeyRef = ptr::null_mut();
let status = unsafe {
SecKeyGeneratePair(
Dictionary::from(params).as_concrete_TypeRef(),
&mut public_key_ref,
&mut private_key_ref,
)
};
// Return an error if the status was unsuccessful
if let Some(e) = Error::maybe_from_OSStatus(status) {
return Err(e);
}
assert!(!public_key_ref.is_null());
assert!(!private_key_ref.is_null());
Ok(unsafe {
KeyPair {
public_key: Key::wrap_under_create_rule(public_key_ref),
private_key: Key::wrap_under_create_rule(private_key_ref),
}
})
}
}
/// Builder for key generation parameters (passed to the underlying
/// `SecKeyGeneratePair` function)
///
/// For more information on generating cryptographic keys in a keychain, see:
/// <https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/generating_new_cryptographic_keys>
#[derive(Clone, Debug)]
pub struct KeyPairGenerateParams {
key_type: AttrKeyType,
key_size: usize,
attrs: DictionaryBuilder,
}
impl KeyPairGenerateParams {
/// Create a new `GeneratePairParams`
pub fn new(key_type: AttrKeyType, key_size: usize) -> Self {
Self {
key_type,
key_size,
attrs: <_>::default(),
}
}
/// Set the access control policy (a.k.a. ACL) for the `Key`.
///
/// Wrapper for the `kSecAttrAccessControl` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattraccesscontrol>
pub fn access_control(mut self, access_control: &AccessControl) -> Self {
self.attrs.add(AttrKind::AccessControl, access_control);
self
}
/// Set a tag (private, application-specific identifier) on this key.
/// Tags are useful as the "primary key" for looking up keychain items.
///
/// Wrapper for `kSecAttrApplicationTag` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
pub fn application_tag<T>(mut self, tag: T) -> Self
where
T: Into<AttrApplicationTag>,
{
self.attrs.add_attr(&tag.into());
self
}
/// Set whether this key can be used in a key derivation operation
///
/// Wrapper for the `kSecKeyDerive` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeyderive>
pub fn can_derive(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Derive, value);
self
}
/// Set whether this key can be used in a decrypt operation.
///
/// Wrapper for the `kSecKeyDecrypt` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeydecrypt>
pub fn can_decrypt(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Decrypt, value);
self
}
/// Set whether this key can be used in a encrypt operation.
///
/// Wrapper for the `kSecKeyEncrypt` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeyencrypt>
pub fn can_encrypt(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Encrypt, value);
self
}
/// Set whether this key can be used in a signing operation.
///
/// Wrapper for the `kSecKeySign` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeysign>
pub fn can_sign(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Sign, value);
self
}
/// Set whether this key can be used to verify a signatures.
///
/// Wrapper for the `kSecKeyVerify` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeyverify>
pub fn can_verify(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Verify, value);
self
}
/// Set whether this key can be used to wrap another key.
///
/// Wrapper for the `kSecKeyWrap` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeywrap>
pub fn can_wrap(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Wrap, value);
self
}
/// Set whether this key can be used to unwrap another key.
///
/// Wrapper for the `kSecKeyUnwrap` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeyunwrap>
pub fn can_unwrap(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Unwrap, value);
self
}
/// Set a key's cryptographic class.
///
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
pub fn key_class(mut self, value: AttrKeyClass) -> Self {
self.attrs.add(AttrKind::KeyClass, &value.as_CFString());
self
}
/// Set whether this key can be extractable when wrapped
///
/// Wrapper for the `kSecKeyExtractable` attribute key. See:
/// <https://developer.apple.com/documentation/security/kseckeyextractable>
pub fn extractable(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Extractable, value);
self
}
/// Set whether this key is stored permanently in the keychain (default: false).
///
/// Wrapper for the `kSecAttrIsPermanent` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrispermanent>
pub fn permanent(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Permanent, value);
self
}
/// Set whether this key can be wrapped with NONE algorithm. True
/// means it cannot be wrapped with NONE, false means it can.
///
/// Wrapper for `kSecKeySensitive` attribute key. See
/// <https://developer.apple.com/documentation/security/kseckeysensitive>
pub fn sensitive(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Sensitive, value);
self
}
/// Set a string label on this key. SecAttrLabels are useful for providing
/// additional descriptions or context on keys.
///
/// Wrapper for the `kSecAttrLabel` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
pub fn label<L: Into<AttrLabel>>(mut self, label: L) -> Self {
self.attrs.add_attr(&label.into());
self
}
/// Set whether this key can be synchronized with other devices owned by
/// the same account (default: false).
///
/// Wrapper for the `kSecAttrSynchronizable` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrsynchronizable>
pub fn synchronizable(mut self, value: bool) -> Self {
self.attrs.add_boolean(AttrKind::Synchronizable, value);
self
}
/// Store this key in an external token i.e. Secure Enclave Processor (SEP).
///
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
pub fn token_id(mut self, value: AttrTokenId) -> Self {
self.attrs.add_attr(&value);
self
}
}
impl From<KeyPairGenerateParams> for Dictionary {
fn from(params: KeyPairGenerateParams) -> Dictionary {
let mut result = DictionaryBuilder::new();
result.add_attr(&params.key_type);
result.add_number(AttrKind::KeySizeInBits, params.key_size as i64);
result.add(
unsafe { kSecPrivateKeyAttrs },
&Dictionary::from(params.attrs),
);
result.into()
}
}
/// Builder for restoring a key from an external representation of that key parameters
/// (passed to the underlying `SecKeyCreateWithData` function).
///
/// The key must have already been imported or generated.
///
/// For more information on restoring cryptographic keys in keychain, see
/// <https://developer.apple.com/documentation/security/1643701-seckeycreatewithdata>
#[derive(Clone, Debug)]
pub struct RestoreKeyParams {
/// The category the key fits (public, private, or symmetric)
pub key_class: AttrKeyClass,
/// Data representing the key. The format of the data depends on the type of key
/// being created.
///
/// - RSA: PKCS#1 format
/// - EC: ANSI X9.63 bytestring:
/// - Public key: `04 || X || Y`
/// - Private key: Concatenation of public key with big endian encoding
/// of the secret scalar, i.e. `04 || X || Y || K`
///
/// All representations use fixed-size integers with leading zeroes.
pub key_data: Vec<u8>,
/// The type of key algorithm
pub key_type: AttrKeyType,
}
impl RestoreKeyParams {
/// Return the attributes that will be used to restore the key
pub fn attributes(&self) -> Dictionary {
let mut result = DictionaryBuilder::new();
result.add_attr(&self.key_type);
result.add(AttrKind::KeyClass, &self.key_class.as_CFString());
result.add_number(AttrKind::KeySizeInBits, (self.key_data.len() * 8) as i64);
result.into()
}
/// Return the `key_data` as a slice
pub fn as_bytes(&self) -> &[u8] {
self.key_data.as_slice()
}
}

View File

@@ -0,0 +1,151 @@
//! Keychains
pub mod item;
pub mod key;
use self::item::MatchLimit;
pub use self::{item::Item, key::Key};
use crate::dictionary::*;
use crate::error::Error;
use crate::ffi::*;
use core_foundation::base::{CFTypeRef, TCFType};
use std::{ffi::CString, os::raw::c_char, os::unix::ffi::OsStrExt, path::Path, ptr};
declare_TCFType! {
/// Keychains which store cryptographic keys, passwords, and other secrets.
///
/// Wrapper for the `SecKeychain`/`SecKeychainRef` types:
/// <https://developer.apple.com/documentation/security/seckeychainref>
Keychain, KeychainRef
}
impl_TCFType!(Keychain, KeychainRef, SecKeychainGetTypeID);
impl Keychain {
/// Find the default keychain. Returns an `Error` result with a kind of
/// `ErrorKind::NoDefaultKeychain` if there is no default keychain.
///
/// This is a non-panicking alternative to `Keychain::default()`.
///
/// Wrapper for the `SecKeychainCopyDefault` function. See:
/// <https://developer.apple.com/documentation/security/1400743-seckeychaincopydefault>
pub fn find_default() -> Result<Keychain, Error> {
let mut result: KeychainRef = ptr::null_mut();
let status = unsafe { SecKeychainCopyDefault(&mut result) };
if let Some(e) = Error::maybe_from_OSStatus(status) {
Err(e)
} else {
Ok(unsafe { Keychain::wrap_under_create_rule(result) })
}
}
/// Create a new keychain. Accepts a path where the new keychain will be
/// located along with an optional password. If no password is given, the
/// user will be prompted for a password.
///
/// Wrapper for the `SecKeychainCreate` function. See:
/// <https://developer.apple.com/documentation/security/1401214-seckeychaincreate>
pub fn create(path: &Path, password: Option<&str>) -> Result<Keychain, Error> {
let path_cstring = CString::new(path.as_os_str().as_bytes()).unwrap();
let mut result: KeychainRef = ptr::null_mut();
let status = match password {
Some(pw) => unsafe {
SecKeychainCreate(
path_cstring.as_ptr() as *const c_char,
pw.len() as u32,
pw.as_bytes().as_ptr() as *const c_char,
false,
ptr::null(),
&mut result,
)
},
None => unsafe {
SecKeychainCreate(
path_cstring.as_ptr() as *const c_char,
0,
ptr::null(),
true,
ptr::null(),
&mut result,
)
},
};
if let Some(e) = Error::maybe_from_OSStatus(status) {
Err(e)
} else {
Ok(unsafe { Keychain::wrap_under_create_rule(result) })
}
}
/// Delete this keychain.
///
/// Wrapper for the `SecKeychainDelete` function. See:
/// <https://developer.apple.com/documentation/security/1395206-seckeychaindelete>
pub fn delete(self) -> Result<(), Error> {
let status = unsafe { SecKeychainDelete(self.as_concrete_TypeRef()) };
if let Some(e) = Error::maybe_from_OSStatus(status) {
Err(e)
} else {
Ok(())
}
}
/// Find an item in this keychain.
///
/// This is a private method we wrap using builders for querying various
/// keychain item types.
///
/// Wrapper for `SecItemCopyMatching`. See:
/// <https://developer.apple.com/documentation/security/1398306-secitemcopymatching>
fn find_item(&self, mut attrs: DictionaryBuilder) -> Result<Item, Error> {
attrs.add(unsafe { kSecMatchLimit }, &MatchLimit::One.as_CFType());
attrs.add_boolean(unsafe { kSecReturnRef }, true);
let mut result: ItemRef = ptr::null_mut();
let status = unsafe {
SecItemCopyMatching(
Dictionary::from(attrs).as_concrete_TypeRef(),
&mut result as &mut CFTypeRef,
)
};
// Return an error if the status was unsuccessful
if let Some(e) = Error::maybe_from_OSStatus(status) {
return Err(e);
}
Ok(unsafe { Item::wrap_under_create_rule(result) })
}
/// Add an item to this keychain.
///
/// This is a private method we wrap using builders for various keychain
/// item types.
///
/// Wrapper for the `SecItemAdd` function. See:
/// <https://developer.apple.com/documentation/security/1401659-secitemadd>
fn add_item(&self, mut attrs: DictionaryBuilder) -> Result<Item, Error> {
attrs.add(unsafe { kSecUseKeychain }, self);
attrs.add_boolean(unsafe { kSecReturnRef }, true);
let mut result: ItemRef = ptr::null_mut();
let status =
unsafe { SecItemAdd(Dictionary::from(attrs).as_concrete_TypeRef(), &mut result) };
if let Some(e) = Error::maybe_from_OSStatus(status) {
Err(e)
} else {
Ok(unsafe { Item::wrap_under_create_rule(result) })
}
}
}
impl Default for Keychain {
fn default() -> Keychain {
Self::find_default().expect("no default keychain available")
}
}

View File

@@ -0,0 +1,67 @@
//! macOS Keychain Services wrapper for accessing the system and user's
//! cryptographic keychains, as well as keys stored in the Secure Enclave
//! Processor (SEP).
//!
//! This crate provides a thin, low-level binding with a safe, mostly idiomatic
//! Rust API. Ideally however, it should be wrapped up in higher level, easy-to-use
//! libraries, as the API it presents is rather complicated and arcane.
//!
//! For more information on Keychain Services`, see:
//! <https://developer.apple.com/documentation/security/keychain_services/keychains>
//!
//! ## Code Signing
//!
//! The Keychain Service API requires signed code to access much of its
//! functionality. Accessing many APIs from an unsigned app will return
//! an error with a kind of `ErrorKind::MissingEntitlement`.
//!
//! Follow the instructions here to create a self-signed code signing certificate:
//! <https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html>
//!
//! You will need to use the [codesign] command-line utility (or XCode) to sign
//! your code before it will be able to access most Keychain Services API
//! functionality. When you sign, you will need an entitlements file which
//! grants access to the Keychain Services API. Below is an example:
//!
//! ```xml
//! <?xml version="1.0" encoding="UTF-8"?>
//! <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
//! <plist version="1.0">
//! <dict>
//! <key>keychain-access-groups</key>
//! <array>
//! <string>$(AppIdentifierPrefix)com.example.MyApplication</string>
//! </array>
//! </dict>
//! </plist>
//! ```
//!
//! [codesign]: https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW4
#![crate_name = "keychain_services"]
#![crate_type = "rlib"]
#![allow(non_snake_case, non_upper_case_globals)]
#![deny(warnings, missing_docs, unused_import_braces, unused_qualifications)]
#[cfg(not(target_os = "macos"))]
compile_error!("This crate presently only compiles on macOS (see GH issue #5 for iOS support)");
#[macro_use]
extern crate core_foundation;
mod access;
mod attr;
mod ciphertext;
mod dictionary;
mod error;
mod ffi;
pub mod keychain;
mod signature;
pub use crate::access::*;
pub use crate::attr::*;
pub use crate::ciphertext::*;
pub use crate::error::*;
pub use crate::key::*;
pub use crate::keychain::*;
pub use crate::signature::*;

View File

@@ -0,0 +1,48 @@
//! Signatures produced by this library.
//!
//! This type doesn't map directly to any type in the Keychain Services API,
//! but instead provides a newtype for signatures this binding produces.
use crate::key::KeyAlgorithm;
/// Cryptographic signatures
#[derive(Clone, Debug)]
pub struct Signature {
alg: KeyAlgorithm,
bytes: Vec<u8>,
}
impl Signature {
/// Create a new `Signature`
pub(crate) fn new(alg: KeyAlgorithm, bytes: Vec<u8>) -> Self {
// TODO: restrict valid algorithms to signature algorithms?
Self { alg, bytes }
}
/// Get the algorithm which produced this signature
pub fn algorithm(&self) -> KeyAlgorithm {
self.alg
}
/// Borrow the signature data as bytes
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
/// Convert into a byte vector
pub fn into_vec(self) -> Vec<u8> {
self.bytes
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl From<Signature> for Vec<u8> {
fn from(sig: Signature) -> Vec<u8> {
sig.into_vec()
}
}