Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/SignalServiceKit/SecureValueRecovery/LocalStorage/SVRLocalStorage.swift
1 views
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation

public protocol SVRLocalStorage {
    func getIsMasterKeyBackedUp(_ transaction: DBReadTransaction) -> Bool

    func clearStorageServiceKeys(_ transaction: DBWriteTransaction)
}

public protocol SVRLocalStorageInternal: SVRLocalStorage {
    func getNeedsMasterKeyBackup(_ transaction: DBReadTransaction) -> Bool

    func getPinType(_ transaction: DBReadTransaction) -> SVR.PinType?

    func getSVR2MrEnclaveStringValue(_ transaction: DBReadTransaction) -> String?

    // MARK: - Setters

    func setNeedsMasterKeyBackup(_ value: Bool, _ transaction: DBWriteTransaction)

    func setIsMasterKeyBackedUp(_ value: Bool, _ transaction: DBWriteTransaction)

    func setPinType(_ value: SVR.PinType, _ transaction: DBWriteTransaction)

    func setSVR2MrEnclaveStringValue(_ value: String?, _ transaction: DBWriteTransaction)

    // MARK: - Clearing Keys

    func clearSVRKeys(_ transaction: DBWriteTransaction)

    // MARK: - Cleanup

    func cleanupDeadKeys(_ transaction: DBWriteTransaction)
}

/// Stores state related to SVR independent of enclave; e.g. do we have backups at all,
/// what type is our pin, etc.
class SVRLocalStorageImpl: SVRLocalStorageInternal {
    private let svrKvStore: KeyValueStore

    init() {
        // Collection name must not be changed; matches that historically kept in KeyBackupServiceImpl.
        self.svrKvStore = KeyValueStore(collection: "kOWSKeyBackupService_Keys")
    }

    // MARK: - Getters

    func getNeedsMasterKeyBackup(_ transaction: DBReadTransaction) -> Bool {
        return svrKvStore.getBool(Keys.needsMasterKeyBackup, defaultValue: false, transaction: transaction)
    }

    func getIsMasterKeyBackedUp(_ transaction: DBReadTransaction) -> Bool {
        return svrKvStore.getBool(Keys.isMasterKeyBackedUp, defaultValue: false, transaction: transaction)
    }

    func getPinType(_ transaction: DBReadTransaction) -> SVR.PinType? {
        guard let raw = svrKvStore.getInt(Keys.pinType, transaction: transaction) else {
            return nil
        }
        return SVR.PinType(rawValue: raw)
    }

    func getSVR2MrEnclaveStringValue(_ transaction: DBReadTransaction) -> String? {
        return svrKvStore.getString(Keys.svr2MrEnclaveStringValue, transaction: transaction)
    }

    // MARK: - Setters

    func setNeedsMasterKeyBackup(_ value: Bool, _ transaction: DBWriteTransaction) {
        return svrKvStore.setBool(value, key: Keys.needsMasterKeyBackup, transaction: transaction)
    }

    func setIsMasterKeyBackedUp(_ value: Bool, _ transaction: DBWriteTransaction) {
        svrKvStore.setBool(value, key: Keys.isMasterKeyBackedUp, transaction: transaction)
    }

    func setPinType(_ value: SVR.PinType, _ transaction: DBWriteTransaction) {
        svrKvStore.setInt(value.rawValue, key: Keys.pinType, transaction: transaction)
    }

    func setSVR2MrEnclaveStringValue(_ value: String?, _ transaction: DBWriteTransaction) {
        svrKvStore.setString(value, key: Keys.svr2MrEnclaveStringValue, transaction: transaction)
    }

    // MARK: - Clearing Keys

    func clearSVRKeys(_ transaction: DBWriteTransaction) {
        svrKvStore.removeValues(
            forKeys: [
                Keys.pinType,
                Keys.isMasterKeyBackedUp,
                Keys.syncedStorageServiceKey,
                Keys.legacy_svr1EnclaveName,
                Keys.svr2MrEnclaveStringValue,
                Keys.needsMasterKeyBackup,
            ],
            transaction: transaction,
        )
    }

    func clearStorageServiceKeys(_ transaction: DBWriteTransaction) {
        svrKvStore.removeValue(forKey: Keys.syncedStorageServiceKey, transaction: transaction)
    }

    // MARK: - Cleanup

    func cleanupDeadKeys(_ transaction: DBWriteTransaction) {
        svrKvStore.removeValues(
            forKeys: [
                Keys.legacy_svr1EnclaveName,
            ],
            transaction: transaction,
        )
    }

    // MARK: - Identifiers

    private enum Keys {
        // These must not change, they match what was historically in KeyBackupServiceImpl.
        static let pinType = "pinType"
        static let isMasterKeyBackedUp = "isMasterKeyBackedUp"
        static let needsMasterKeyBackup = "needsMasterKeyBackup"
        static let syncedStorageServiceKey = "Storage Service Encryption"
        // Kept around because its existence indicates we had an svr1 backup.
        // TODO: Remove after Nov 1, 2024
        static let legacy_svr1EnclaveName = "enclaveName"
        static let svr2MrEnclaveStringValue = "svr2_mrenclaveStringValue"
    }
}

#if TESTABLE_BUILD
public class SVRLocalStorageMock: SVRLocalStorage {

    var isMasterKeyBackedUp: Bool = false

    public func clearStorageServiceKeys(_ transaction: DBWriteTransaction) {
        fatalError("not implemented")
    }

    public func getIsMasterKeyBackedUp(_ transaction: DBReadTransaction) -> Bool {
        return isMasterKeyBackedUp
    }
}
#endif