Skip to content

Instantly share code, notes, and snippets.

@bergusman
Last active November 16, 2022 00:38
Show Gist options
  • Save bergusman/ed3243c920915ecdf7a2bccfb247c8c1 to your computer and use it in GitHub Desktop.
Save bergusman/ed3243c920915ecdf7a2bccfb247c8c1 to your computer and use it in GitHub Desktop.
Keychain
let classAttributes: [CFString: [CFString]] = [
// https://developer.apple.com/documentation/security/ksecclassgenericpassword
kSecClassGenericPassword: [
kSecAttrAccessControl,
kSecAttrAccessGroup,
kSecAttrAccessible,
kSecAttrCreationDate,
kSecAttrModificationDate,
kSecAttrDescription,
kSecAttrComment,
kSecAttrCreator,
kSecAttrType,
kSecAttrLabel,
kSecAttrIsInvisible,
kSecAttrIsNegative,
kSecAttrAccount,
kSecAttrService,
kSecAttrGeneric,
kSecAttrSynchronizable
],
// https://developer.apple.com/documentation/security/ksecclassinternetpassword
kSecClassInternetPassword: [
kSecAttrAccessGroup,
kSecAttrAccessible,
kSecAttrCreationDate,
kSecAttrModificationDate,
kSecAttrDescription,
kSecAttrComment,
kSecAttrCreator,
kSecAttrType,
kSecAttrLabel,
kSecAttrIsInvisible,
kSecAttrIsNegative,
kSecAttrAccount,
kSecAttrSecurityDomain,
kSecAttrServer,
kSecAttrProtocol,
kSecAttrAuthenticationType,
kSecAttrPort,
kSecAttrPath,
kSecAttrSynchronizable
],
// https://developer.apple.com/documentation/security/ksecclasscertificate
kSecClassCertificate: [
kSecAttrAccessGroup,
kSecAttrAccessible,
kSecAttrLabel,
kSecAttrCertificateType,
kSecAttrCertificateEncoding,
kSecAttrSubject,
kSecAttrIssuer,
kSecAttrSerialNumber,
kSecAttrSubjectKeyID,
kSecAttrPublicKeyHash
],
// https://developer.apple.com/documentation/security/ksecclasskey
kSecClassKey: [
kSecAttrAccessGroup,
kSecAttrAccessible,
kSecAttrLabel,
kSecAttrKeyClass,
kSecAttrApplicationLabel,
kSecAttrIsPermanent,
kSecAttrApplicationTag,
kSecAttrKeyType,
kSecAttrKeySizeInBits,
kSecAttrEffectiveKeySize,
kSecAttrCanEncrypt,
kSecAttrCanDecrypt,
kSecAttrCanDerive,
kSecAttrCanSign,
kSecAttrCanVerify,
kSecAttrCanWrap,
kSecAttrCanUnwrap
],
// https://developer.apple.com/documentation/security/ksecclassidentity
kSecClassIdentity: [
kSecAttrAccessGroup,
kSecAttrAccessible,
kSecAttrLabel,
kSecAttrKeyClass,
kSecAttrApplicationLabel,
kSecAttrIsPermanent,
kSecAttrApplicationTag,
kSecAttrKeyType,
kSecAttrKeySizeInBits,
kSecAttrEffectiveKeySize,
kSecAttrCanEncrypt,
kSecAttrCanDecrypt,
kSecAttrCanDerive,
kSecAttrCanSign,
kSecAttrCanVerify,
kSecAttrCanWrap,
kSecAttrCanUnwrap,
kSecAttrCertificateType,
kSecAttrCertificateEncoding,
kSecAttrSubject,
kSecAttrIssuer,
kSecAttrSerialNumber,
kSecAttrSubjectKeyID,
kSecAttrPublicKeyHash
]
]
class KeychainDumper {
class func key(_ key: CFString, class: CFString? = nil) -> String {
if key == kSecAttrType {
if `class` == kSecClassKey || `class` == kSecClassIdentity {
return "KeyType"
} else {
return "Type"
}
}
if let name = [
// Item Class Keys and Values
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_class_keys_and_values
kSecClass: "Class",
// Item Attribute Keys and Values
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_attribute_keys_and_values
// General Item Attribute Keys
kSecAttrAccessControl: "AccessControl",
kSecAttrAccessible: "Accessible",
kSecAttrAccessGroup: "AccessGroup",
kSecAttrSynchronizable: "Synchronizable",
kSecAttrCreationDate: "CreationDate",
kSecAttrModificationDate: "ModificationDate",
kSecAttrDescription: "Description",
kSecAttrComment: "Comment",
kSecAttrCreator: "Creator",
kSecAttrLabel: "Label",
kSecAttrIsInvisible: "IsInvisible",
kSecAttrIsNegative: "IsNegative",
kSecAttrSyncViewHint: "SyncViewHint",
kSecAttrPersistentReference: "PersistentReference",
// Password Attribute Keys
kSecAttrAccount: "Account",
kSecAttrService: "Service",
kSecAttrGeneric: "Generic",
kSecAttrSecurityDomain: "SecurityDomain",
kSecAttrServer: "Server",
kSecAttrProtocol: "Protocol",
kSecAttrAuthenticationType: "AuthenticationType",
kSecAttrPort: "Port",
kSecAttrPath: "Path",
// Certificate Attribute Keys
kSecAttrSubject: "Subject",
kSecAttrIssuer: "Issuer",
kSecAttrSerialNumber: "SerialNumber",
kSecAttrSubjectKeyID: "SubjectKeyID",
kSecAttrPublicKeyHash: "PublicKeyHash",
kSecAttrCertificateType: "CertificateType",
kSecAttrCertificateEncoding: "CertificateEncoding",
// Cryptographic Key Attribute Keys
kSecAttrKeyClass: "KeyClass",
kSecAttrApplicationLabel: "ApplicationLabel",
kSecAttrApplicationTag: "ApplicationTag",
kSecAttrKeySizeInBits: "KeySizeInBits",
kSecAttrEffectiveKeySize: "EffectiveKeySize",
kSecAttrTokenID: "TokenID",
kSecAttrIsPermanent: "IsPermanent",
kSecAttrIsSensitive: "IsSensitive",
kSecAttrIsExtractable: "IsExtractable",
kSecAttrCanEncrypt: "CanEncrypt",
kSecAttrCanDecrypt: "CanDecrypt",
kSecAttrCanDerive: "CanDerive",
kSecAttrCanSign: "CanSign",
kSecAttrCanVerify: "CanVerify",
kSecAttrCanWrap: "CanWrap",
kSecAttrCanUnwrap: "CanUnwrap",
// Search Attribute Keys and Values
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/search_attribute_keys_and_values
// Item Search Matching Keys
kSecMatchPolicy: "MatchPolicy",
kSecMatchItemList: "MatchItemList",
kSecMatchSearchList: "MatchSearchList",
kSecMatchIssuers: "MatchIssuers",
kSecMatchEmailAddressIfPresent: "MatchEmailAddressIfPresent",
kSecMatchSubjectContains: "MatchSubjectContains",
kSecMatchCaseInsensitive: "MatchCaseInsensitive",
kSecMatchTrustedOnly: "MatchTrustedOnly",
kSecMatchValidOnDate: "MatchValidOnDate",
kSecMatchLimit: "MatchLimit",
// Additional Item Search Keys
"u_ItemList": "UseItemList", // kSecUseItemList
kSecUseOperationPrompt: "UseOperationPrompt",
"u_NoAuthUI": "UseNoAuthenticationUI", // kSecUseNoAuthenticationUI
kSecUseAuthenticationUI: "UseAuthenticationUI",
kSecUseAuthenticationContext: "UseAuthenticationContext",
kSecUseDataProtectionKeychain: "UseDataProtectionKeychain",
// Item Return Result Keys
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_return_result_keys
// Item Result Keys
kSecReturnData: "ReturnData",
kSecReturnAttributes: "ReturnAttributes",
kSecReturnRef: "ReturnRef",
kSecReturnPersistentRef: "ReturnPersistentRef",
// Item Value Type Keys
kSecValueData: "ValueData",
kSecValueRef: "ValueRef",
kSecValuePersistentRef: "ValuePersistentRef"
][key] {
return name
}
return key as String
}
class func value(_ value: Any, for key: CFString, class: CFString? = nil) -> Any {
let value = value as CFTypeRef
if key == kSecClass {
if let value = [
kSecClassKey: "Key",
kSecClassIdentity: "Identity",
kSecClassCertificate: "Certificate",
kSecClassGenericPassword: "GenericPassword",
kSecClassInternetPassword: "InternetPassword"
][value as! CFString] {
return value
}
}
if key == kSecAttrAccessible {
if let value = [
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: "WhenPasscodeSetThisDeviceOnly",
kSecAttrAccessibleWhenUnlockedThisDeviceOnly: "WhenUnlockedThisDeviceOnly",
kSecAttrAccessibleWhenUnlocked: "WhenUnlocked",
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly: "AfterFirstUnlockThisDeviceOnly",
kSecAttrAccessibleAfterFirstUnlock: "AfterFirstUnlock",
"dku" as CFString: "AlwaysThisDeviceOnly", // kSecAttrAccessibleAlwaysThisDeviceOnly
"dk" as CFString: "Always" // kSecAttrAccessibleAlways
][value as! CFString] {
return value
}
}
if key == kSecAttrKeyClass {
if let value = [
kSecAttrKeyClassPublic: "Public",
kSecAttrKeyClassPrivate: "Private",
kSecAttrKeyClassSymmetric: "Symmetric"
]["\(value)"] {
return value
}
}
if key == kSecAttrKeyType && `class` == kSecClassKey || `class` == kSecClassIdentity {
if let value = [
kSecAttrKeyTypeRSA: "RSA",
kSecAttrKeyTypeEC: "EC"
]["\(value)"] {
return value
}
}
return value
}
class func attributes(for dict: [CFString: Any]) -> [(key: String, value: Any)] {
var order: [String] = [
kSecClass,
// Item Search Matching Keys
kSecMatchPolicy,
kSecMatchItemList,
kSecMatchSearchList,
kSecMatchIssuers,
kSecMatchEmailAddressIfPresent,
kSecMatchSubjectContains,
kSecMatchCaseInsensitive,
kSecMatchTrustedOnly,
kSecMatchValidOnDate,
kSecMatchLimit,
// Additional Item Search Keys
"u_ItemList" as CFString, // kSecUseItemList
kSecUseOperationPrompt,
"u_NoAuthUI" as CFString, // kSecUseNoAuthenticationUI
kSecUseAuthenticationUI,
kSecUseAuthenticationContext,
kSecUseDataProtectionKeychain,
// Item Result Keys
kSecReturnData,
kSecReturnAttributes,
kSecReturnRef,
kSecReturnPersistentRef,
// General Item Attribute Keys
kSecAttrAccessControl,
kSecAttrAccessible,
kSecAttrAccessGroup,
kSecAttrSynchronizable,
kSecAttrCreationDate,
kSecAttrModificationDate,
kSecAttrDescription,
kSecAttrComment,
kSecAttrCreator,
kSecAttrLabel,
kSecAttrIsInvisible,
kSecAttrIsNegative,
kSecAttrSyncViewHint,
kSecAttrPersistentReference,
// Password Attribute Keys
kSecAttrAccount,
kSecAttrService,
kSecAttrGeneric,
kSecAttrSecurityDomain,
kSecAttrServer,
kSecAttrProtocol,
kSecAttrAuthenticationType,
kSecAttrPort,
kSecAttrPath,
// Certificate Attribute Keys
kSecAttrSubject,
kSecAttrIssuer,
kSecAttrSerialNumber,
kSecAttrSubjectKeyID,
kSecAttrPublicKeyHash,
kSecAttrCertificateType,
kSecAttrCertificateEncoding,
// Cryptographic Key Attribute Keys
kSecAttrKeyClass,
kSecAttrApplicationLabel,
kSecAttrApplicationTag,
kSecAttrKeySizeInBits,
kSecAttrEffectiveKeySize,
kSecAttrTokenID,
kSecAttrIsPermanent,
kSecAttrIsSensitive,
kSecAttrIsExtractable,
kSecAttrCanEncrypt,
kSecAttrCanDecrypt,
kSecAttrCanDerive,
kSecAttrCanSign,
kSecAttrCanVerify,
kSecAttrCanWrap,
kSecAttrCanUnwrap,
// Item Value Type Keys
kSecValueData,
kSecValueRef,
kSecValuePersistentRef
] as [String]
var `class`: CFString?
if let value = dict[kSecClass] as? String {
`class` = value as CFString
}
if `class` == kSecClassKey || `class` == kSecClassIdentity {
order.insert(kSecAttrKeyType as String, at: order.firstIndex(of: kSecAttrApplicationTag as String)! + 1)
} else {
order.insert(kSecAttrType as String, at: order.firstIndex(of: kSecAttrCreator as String)! + 1)
}
var knownPairs: [(String, Any)] = []
var unknownPairs: [(String, Any)] = []
for (key, value) in dict {
let key = key as String
if order.contains(key) {
knownPairs.append((key, value))
} else {
unknownPairs.append((key, value))
}
}
knownPairs.sort { order.firstIndex(of: $0.0)! < order.firstIndex(of: $1.0)! }
unknownPairs.sort { $0.0 < $1.0 }
let pairs = (knownPairs + unknownPairs).map {
(key($0.0 as CFString, class: `class`), value($0.1, for: $0.0 as CFString, class: `class`))
}
return pairs
}
class func item(_ item: Any) -> Any {
if let item = item as? [CFString: Any] {
return attributes(for: item)
}
if let items = item as? [Any] {
return items.map { self.item($0) }
}
return item
}
class func dump(_ item: Any) {
func dump(_ item: Any) {
if let attrs = item as? [(String, Any)] {
for attr in attrs {
print("\(attr.0):", attr.1)
}
print()
} else if let items = item as? [Any] {
for item in items {
dump(item)
}
} else {
print(item)
}
}
dump(self.item(item))
}
}
func dump() {
let query: [CFString: Any] = [
kSecMatchLimit: kSecMatchLimitAll,
kSecAttrSynchronizable: kSecAttrSynchronizableAny,
kSecReturnAttributes: true,
kSecReturnRef: true,
kSecReturnData: true,
kSecReturnPersistentRef: true
]
print("KEYCHAIN")
print("========")
print()
for `class` in [
kSecClassKey,
kSecClassIdentity,
kSecClassCertificate,
kSecClassGenericPassword,
kSecClassInternetPassword
] {
var query = query
query[kSecClass] = `class`
let `class` = KeychainDumper.value(`class`, for: kSecClass) as! String
print(`class`)
print(String(repeating: "-", count: `class`.count))
var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status != errSecSuccess && status != errSecItemNotFound {
if let message = SecCopyErrorMessageString(status, nil) {
print("Error(\(status)): \(message)")
} else {
print("Error: \(status)")
}
return
}
if let item = result {
KeychainDumper.dump(item)
} else {
print()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment