Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  KeychainManager.swift   Sprache: JAVA

 

import CocoaLumberjackSwift
import Foundation
import RemoteSecretProtocol
import ThreemaEssentials

/// Manager to access all items stored in Keychain.
///
/// The KeychainManager manages only generics passwords (`kSecClass` -> `kSecClassGenericPassword`),
/// the primary keys include `kSecAttrAccount` and `kSecAttrService`!
public final class KeychainManager: NSObject, KeychainManagerProtocol {

    public enum KeychainManagerError: Error {
        case myIdentityMissing
        case threemaSafeServerMissing
        case invalidIdentityBackupData
    }

    // TODO: (IOS-5364) Check if `RemoteSecretCryptoProtocol` would also be sufficient here
    private let remoteSecretManager: RemoteSecretManagerProtocol
    
    private let keychainProvider: KeychainProviding

    // MARK: - Lifecycle

    override private init() {
        // no-op
        fatalError("Not supported")
    }
    
    public init(remoteSecretManager: RemoteSecretManagerProtocol) {
        self.remoteSecretManager = remoteSecretManager
        self.keychainProvider = KeychainProvider()
        super.init()
    }
    
    required init(
        remoteSecretManager: RemoteSecretManagerProtocol,
        keychainProvider: KeychainProviding = KeychainProvider()
    ) {
        self.remoteSecretManager = remoteSecretManager
        self.keychainProvider = keychainProvider
        super.init()
    }

    // MARK: - Public functions
    
    // MARK: - Locking Check
    
    /// Is Keychain locked for first unlock accesses?
    public static var isKeychainLocked: Bool {
        do {
            _ = try KeychainProvider().load(.identity())
            return false
        }
        catch let KeychainProviderError.loadFailed(osStatus: osStatus)
            where osStatus == errSecInteractionNotAllowed {
            return true
        }
        catch {
            return false
        }
    }
    
    // MARK: - Remote Secret
    
    public static func loadRemoteSecret() throws -> (
        authenticationToken: Data,
        identityHash: Data
    )? {
        guard let result = try KeychainProvider().load(.remoteSecret()),
              let authenticationToken = result.password, let identityHash = result.generic else {
            return nil
        }
        
        return (authenticationToken, identityHash)
    }
    
    public static func hasRemoteSecretInStore() -> Bool {
        do {
            return try KeychainProvider().load(.remoteSecret()) != nil
        }
        catch {
            DDLogError("[KeychainManager] RS check failed.")
            return false
        }
    }
    
    public static func storeRemoteSecret(authenticationToken: Data, identityHash: Data) throws {
        let item = KeychainItem.remoteSecret()
        try KeychainProvider().store(
            item,
            account: item.label,
            password: authenticationToken,
            generic: identityHash
        )
    }
    
    public static func deleteRemoteSecret() throws {
        try KeychainProvider().delete(.remoteSecret())
    }
    
    // MARK: - Threema Identity
    
    public static func loadThreemaIdentity() throws -> ThreemaIdentity? {
        guard let result = try KeychainProvider().load(.identity()),
              let identity = result.account else {
            return nil
        }
        
        return ThreemaIdentity(identity)
    }

    // MARK: - Identity

    public func loadIdentity() throws -> MyIdentity? {
        guard let result = try keychainProvider.load(.identity()),
              let account = result.account,
              let password = result.password,
              let generic = result.generic,
              let service = result.service else {
            return nil
        }
        
        let retrievedPassword = remoteSecretManager.decryptDataIfNeeded(password)
        let retrievedGeneric = remoteSecretManager.decryptDataIfNeeded(generic)
        
        guard let retrievedService = try remoteSecretManager.decryptBase64StringIfNeeded(service) else {
            return nil
        }
        
        return MyIdentity(
            identity: ThreemaIdentity(account),
            clientKey: ThreemaClientKey(retrievedPassword),
            publicKey: ThreemaPublicKey(retrievedGeneric),
            serverGroup: ServerGroup(retrievedService)
        )
    }

    public func storeIdentity(_ myIdentity: MyIdentity, thisDeviceOnly: Bool) throws {
        let storingPassword =
            remoteSecretManager.encryptDataIfNeeded(myIdentity.$clientKey)
        let storingGeneric =
            remoteSecretManager.encryptDataIfNeeded(myIdentity.$publicKey)
        let storingService =
            remoteSecretManager.encryptToBase64StringIfNeeded(myIdentity.$serverGroup)
        
        try keychainProvider.store(
            .identity(thisDeviceOnly: thisDeviceOnly),
            account: myIdentity.$identity,
            password: storingPassword,
            generic: storingGeneric,
            service: storingService
        )
    }

    public func deleteIdentity() throws {
        try keychainProvider.delete(.identity())
    }
    
    public func isIdentityThisDeviceOnly() throws -> Bool {
        guard let result = try keychainProvider.load(.identity()) else {
            throw KeychainManagerError.myIdentityMissing
        }
        
        return result.accessibility == kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
    }
    
    public func changeIdentityAccessibility(thisDeviceOnly: Bool) throws {
        guard let identity = try? loadIdentity() else {
            throw KeychainManagerError.myIdentityMissing
        }

        guard try isIdentityThisDeviceOnly() != thisDeviceOnly else {
            return
        }

        try storeIdentity(identity, thisDeviceOnly: thisDeviceOnly)
    }

    // MARK: - Identity Backup

    public static func loadIdentityBackup() throws -> String {
        guard let result = try KeychainProvider().load(.identityBackup()),
              let identityBackupData = result.password
        else {
            throw KeychainProviderError.loadFailed(osStatus: errSecItemNotFound)
        }
                
        guard let identityBackup = String(data: identityBackupData, encoding: .ascii) else {
            throw KeychainManagerError.invalidIdentityBackupData
        }
        
        return identityBackup
    }

    public static func storeIdentityBackup(_ data: String) throws {
        let item = KeychainItem.identityBackup()
        
        guard let identityData = data.data(using: .ascii) else {
            throw KeychainManagerError.invalidIdentityBackupData
        }
                
        try KeychainProvider().store(
            item,
            account: item.label,
            password: identityData
        )
    }

    public static func deleteIdentityBackup() throws {
        try KeychainProvider().delete(.identityBackup())
    }

    // MARK: - Device Cookie

    public func loadDeviceCookie() throws -> Data? {
        guard let result = try keychainProvider.load(.deviceCookie()),
              let deviceCookie = result.password else {
            return nil
        }
        
        let loadingDeviceCookie = remoteSecretManager.decryptDataIfNeeded(deviceCookie)
        
        return loadingDeviceCookie
    }

    public func storeDeviceCookie(_ cookie: Data) throws {
        let item = KeychainItem.deviceCookie()
        
        let storingCookie = remoteSecretManager.encryptDataIfNeeded(cookie)
        
        try keychainProvider.store(
            item,
            account: item.label,
            password: storingCookie
        )
    }

    public static func deleteDeviceCookie() throws {
        try KeychainProvider().delete(.deviceCookie())
    }

    // MARK: - Device Group Key

    public func loadMultiDeviceGroupKey() throws -> Data? {
        guard let result = try keychainProvider.load(.multiDeviceGroupKey()),
              let multiDeviceGroupKey = result.password else {
            return nil
        }
        
        let loadingMultiDeviceGroupKey = remoteSecretManager.decryptDataIfNeeded(multiDeviceGroupKey)
        
        return loadingMultiDeviceGroupKey
    }

    public func storeMultiDeviceGroupKey(key: Data) throws {
        let item = KeychainItem.multiDeviceGroupKey()
        
        let storingKey = remoteSecretManager.encryptDataIfNeeded(key)
        
        try keychainProvider.store(
            item,
            account: item.label,
            password: storingKey
        )
    }
    
    public static func deleteMultiDeviceGroupKey() throws {
        try KeychainProvider().delete(.multiDeviceGroupKey())
    }

    // MARK: - Device ID for Multi Device

    @available(swift, obsoleted: 1.0, renamed: "loadMultiDeviceID()", message: "Only use from Objective-C")
    @objc public func loadMultiDeviceIDObjC() -> Data? {
        do {
            return try loadMultiDeviceID()
        }
        catch {
            return nil
        }
    }

    public func loadMultiDeviceID() throws -> Data? {
        guard let result = try keychainProvider.load(.multiDeviceID()),
              let multiDeviceID = result.password else {
            return nil
        }
        
        let loadingMultiDeviceID = remoteSecretManager.decryptDataIfNeeded(multiDeviceID)
        
        return loadingMultiDeviceID
    }

    @objc public func storeMultiDeviceID(id: Data) throws {
        let item = KeychainItem.multiDeviceID()
        
        let storingID = remoteSecretManager.encryptDataIfNeeded(id)
        
        try keychainProvider.store(
            item,
            account: item.label,
            password: storingID
        )
    }

    public func deleteMultiDeviceID() throws {
        try keychainProvider.delete(.multiDeviceID())
    }

    // MARK: - Forward Security Key

    public func loadForwardSecurityWrappingKey() throws -> Data? {
        guard let result = try keychainProvider.load(.forwardSecurityWrappingKey()),
              let forwardSecurityWrappingKey = result.password else {
            return nil
        }
        
        let loadingKey = remoteSecretManager.decryptDataIfNeeded(forwardSecurityWrappingKey)
        
        return loadingKey
    }

    public func storeForwardSecurityWrappingKey(_ key: Data) throws {
        let item = KeychainItem.forwardSecurityWrappingKey()
        
        let storingKey = remoteSecretManager.encryptDataIfNeeded(key)
        
        try keychainProvider.store(
            item,
            account: item.label,
            password: storingKey
        )
    }

    public static func deleteForwardSecurityKey() throws {
        try KeychainProvider().delete(.forwardSecurityWrappingKey())
    }

    // MARK: - License (Work and OnPrem)

    public static func loadOnPremServer() throws -> String? {
        guard let result = try KeychainProvider().load(.license()),
              let server = result.service else {
            return nil
        }

        return server
    }

    /// Loads the license if it is stored unencrypted
    ///
    /// Use this before the app is set up, where no remote secret manager exists: a remote secret is
    /// tied to a Threema ID. A license stored while remote secret was enabled is encrypted and cannot
    /// be decrypted without it, so this returns `nil` as long as there is a remote secret in the
    /// keychain. Without one, ``storeLicense(_:)`` stores all values unencrypted.
    /// - Returns: The stored license, or `nil` if there is none or it is encrypted
    public static func loadUnencryptedLicense() throws -> ThreemaLicense? {
        try loadUnencryptedLicense(keychainProvider: KeychainProvider())
    }

    static func loadUnencryptedLicense(keychainProvider: KeychainProviding) throws -> ThreemaLicense? {
        guard try keychainProvider.load(.remoteSecret()) == nil else {
            return nil
        }

        guard let result = try keychainProvider.load(.license()),
              let user = result.account,
              let passwordData = result.password,
              let password = String(data: passwordData, encoding: .utf8)
        else {
            return nil
        }

        let deviceID = result.generic.flatMap {
            String(data: $0, encoding: .utf8)
        }

        return ThreemaLicense(
            user: user,
            password: password,
            deviceID: deviceID,
            onPremServer: result.service
        )
    }

    public func loadLicense() throws -> ThreemaLicense? {
        guard let result = try keychainProvider.load(.license()),
              let loadingUser = try remoteSecretManager.decryptBase64StringIfNeeded(result.account),
              let loadingPassword = try remoteSecretManager.decryptDataToStringIfNeeded(result.password)
        else {
            return nil
        }
        
        let loadingDeviceID = try remoteSecretManager.decryptDataToStringIfNeeded(result.generic)

        return ThreemaLicense(
            user: loadingUser,
            password: loadingPassword,
            deviceID: loadingDeviceID,
            onPremServer: result.service
        )
    }

    public func storeLicense(_ license: ThreemaLicense) throws {
        let storingUser = remoteSecretManager.encryptToBase64StringIfNeeded(license.user)
        let storingPassword = remoteSecretManager.encryptStringIfNeeded(license.password)
        let storingGeneric = remoteSecretManager.encryptStringIfNeeded(license.deviceID)

        try keychainProvider.store(
            .license(),
            account: storingUser,
            password: storingPassword,
            generic: storingGeneric,
            service: license.onPremServer
        )
    }

    @objc public func deleteLicense() throws {
        try keychainProvider.delete(.license())
    }

    // MARK: - Threema Safe Key

    public func loadThreemaSafeKey() throws -> Data? {
        guard let result = try keychainProvider.load(.threemaSafeKey()),
              let key = result.password else {
            return nil
        }

        let loadingKey = remoteSecretManager.decryptDataIfNeeded(key)
        
        return loadingKey
    }

    public func storeThreemaSafeKey(key: Data) throws {
        let storingKey = remoteSecretManager.encryptDataIfNeeded(key)
        
        let keychainItem = KeychainItem.threemaSafeKey()
        try keychainProvider.store(
            keychainItem,
            account: keychainItem.label,
            password: storingKey
        )
    }

    public func deleteThreemaSafeKey() throws {
        try keychainProvider.delete(.threemaSafeKey())
    }

    // MARK: - Threema Safe Server

    public func loadThreemaSafeServer() throws -> ThreemaSafeServerInfo? {
        guard let result = try keychainProvider.load(.threemaSafeServer()) else {
            return nil
        }

        guard let loadingServer = try remoteSecretManager.decryptBase64StringIfNeeded(result.service) else {
            throw KeychainManagerError.threemaSafeServerMissing
        }
        
        let loadingUser = try remoteSecretManager.decryptBase64StringIfNeeded(result.account)
        let loadingPassword = try remoteSecretManager.decryptDataToStringIfNeeded(result.password)
        
        return ThreemaSafeServerInfo(
            user: loadingUser,
            password: loadingPassword,
            server: loadingServer
        )
    }

    public func storeThreemaSafeServer(_ server: ThreemaSafeServerInfo) throws {
        let storingUser = remoteSecretManager.encryptToBase64StringIfNeeded(server.user)
        let storingPassword = remoteSecretManager.encryptStringIfNeeded(server.password)
        let storingServer = remoteSecretManager.encryptToBase64StringIfNeeded(server.server)

        try keychainProvider.store(
            .threemaSafeServer(),
            account: storingUser,
            password: storingPassword,
            service: storingServer
        )
    }

    public func deleteThreemaSafeServer() throws {
        try keychainProvider.delete(.threemaSafeServer())
    }

    // MARK: - Data Backup Recovery Key

    public func loadDataBackupRecoveryKey() throws -> Data? {
        guard let result = try keychainProvider.load(.dataBackupRecoveryKey()),
              let key = result.password else {
            return nil
        }

        let loadingKey = remoteSecretManager.decryptDataIfNeeded(key)

        return loadingKey
    }

    public func storeDataBackupRecoveryKey(_ key: Data) throws {
        let storingKey = remoteSecretManager.encryptDataIfNeeded(key)

        let keychainItem = KeychainItem.dataBackupRecoveryKey()
        try keychainProvider.store(
            keychainItem,
            account: keychainItem.label,
            password: storingKey
        )
    }

    public func deleteDataBackupRecoveryKey() throws {
        try keychainProvider.delete(.dataBackupRecoveryKey())
    }

    // MARK: - Push Notification Encryption Key

    @available(
        swift,
        obsoleted: 1.0,
        renamed: "loadPushNotificationEncryptionKey()",
        message: "Only use from Objective-C"
    )
    @objc public static func loadPushNotificationEncryptionKeyObjC() -> Data? {
        do {
            return try loadPushNotificationEncryptionKey()
        }
        catch {
            DDLogError("Unable to load push notification encryption key: \(error)")
            return nil
        }
    }

    public static func loadPushNotificationEncryptionKey() throws -> Data? {
        try KeychainProvider().load(.pushNotificationEncryptionKey())?.password
    }

    public static func storePushNotificationEncryptionKey(_ key: Data) throws {
        let item = KeychainItem.pushNotificationEncryptionKey()
        try KeychainProvider().store(
            item,
            account: item.label,
            password: key
        )
    }

    // MARK: - Migrate Keychain

    public func migrateToDowngrade() throws {
        try KeychainMigration.migrateToDowngrade()
    }

    public func migrateToVersion0() throws {
        try KeychainMigration.migrateToVersion0()
    }

    public func migrateToVersion1(myIdentity: ThreemaEssentials.ThreemaIdentity) throws {
        try KeychainMigration.migrateToVersion1(myIdentity: myIdentity)
    }
    
    // MARK: - Deletion
    
    public static func deleteAllItems() throws {
        let keychainProvider = KeychainProvider()
        for item in KeychainItem.allCases {
            try keychainProvider.delete(item)
        }
    }
    
    public static func deleteAllThisDeviceOnlyItems() throws {
        let keychainProvider = KeychainProvider()
        for item in KeychainItem.allCases where item.accessibility == kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly {
            try keychainProvider.delete(item)
        }
    }
    
    public static func deleteAllEncryptedItems() throws {
        guard hasRemoteSecretInStore() else {
            return
        }
        
        let keychainProvider = KeychainProvider()
        for item in KeychainItem.allCases where item.mightContainEncryptedData {
            try keychainProvider.delete(item)
        }
    }

    // MARK: - Debug stuff

    #if DEBUG
        public static func printKeychainItems() {
            let classes = [
                kSecClassGenericPassword,
                kSecClassInternetPassword,
                kSecClassCertificate,
                kSecClassIdentity,
                kSecClassKey,
            ]

            for secClass in classes {
                do {
                    guard let items = try KeychainMigration.allItems(of: secClass) else {
                        continue
                    }

                    print("Found \(items.count) items")

                    for item in items {
                        print("Found item: \(item)")
                    }
                }
                catch {
                    print(error)
                }
            }
        }

        public static func deleteKeychainItemsExceptIdentity() {
            do {
                try KeychainMigration.migrateToDowngrade()
            }
            catch {
                print(error)
            }
        }
    #endif
}

Messung
CHZG
84829687

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417692