initial commit

This commit is contained in:
Jon Vanvik 2026-05-31 14:11:29 +02:00
commit 8de962eeb8
44 changed files with 4843 additions and 0 deletions

54
KeychainStore.swift Normal file
View file

@ -0,0 +1,54 @@
import Foundation
import Security
/// Minimal Keychain wrapper for storing the MCMS credentials. Stores the
/// password (and optionally host/email) keyed by a service + account. Never
/// use UserDefaults for secrets.
struct KeychainStore {
let service: String
init(service: String = "com.example.mcmsmobile.credentials") {
self.service = service
}
@discardableResult
func set(_ value: String, account: String) -> Bool {
guard let data = value.data(using: .utf8) else { return false }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
SecItemDelete(query as CFDictionary)
var attributes = query
attributes[kSecValueData as String] = data
attributes[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
return SecItemAdd(attributes as CFDictionary, nil) == errSecSuccess
}
func get(account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess,
let data = item as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
@discardableResult
func delete(account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
return SecItemDelete(query as CFDictionary) == errSecSuccess
}
}