// // Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation public protocol SVRLocalStorage { func getIsMasterKeyBackedUp(_ transaction: DBReadTransaction) -> Bool } public protocol SVRLocalStorageInternal: SVRLocalStorage { func getMasterKey(_ transaction: DBReadTransaction) -> Data? func getPinType(_ transaction: DBReadTransaction) -> SVR.PinType? func getEncodedPINVerificationString(_ transaction: DBReadTransaction) -> String? // Linked devices get the storage service key and store it locally. The primary doesn't do this. func getSyncedStorageServiceKey(_ transaction: DBReadTransaction) -> Data? func getSVR2MrEnclaveStringValue(_ transaction: DBReadTransaction) -> String? func hadSVR1Enclave(_ transaction: DBReadTransaction) -> Bool // MARK: - Setters func setIsMasterKeyBackedUp(_ value: Bool, _ transaction: DBWriteTransaction) func setMasterKey(_ value: Data?, _ transaction: DBWriteTransaction) func setPinType(_ value: SVR.PinType, _ transaction: DBWriteTransaction) func setEncodedPINVerificationString(_ value: String?, _ transaction: DBWriteTransaction) // Linked devices get the storage service key and store it locally. The primary doesn't do this. func setSyncedStorageServiceKey(_ value: Data?, _ transaction: DBWriteTransaction) // Linked devices get the backup key and store it locally. The primary doesn't do this. func setSyncedBackupKey(_ value: Data?, _ transaction: DBWriteTransaction) func setSVR2MrEnclaveStringValue(_ value: String?, _ transaction: DBWriteTransaction) // MARK: - Clearing Keys func clearKeys(_ transaction: DBWriteTransaction) } /// Stores state related to SVR independent of enclave; e.g. do we have backups at all, /// what type is our pin, etc. internal class SVRLocalStorageImpl: SVRLocalStorageInternal { private let keyValueStore: KeyValueStore public init( keyValueStoreFactory: KeyValueStoreFactory ) { // Collection name must not be changed; matches that historically kept in KeyBackupServiceImpl. self.keyValueStore = keyValueStoreFactory.keyValueStore(collection: "kOWSKeyBackupService_Keys") } // MARK: - Getters public func getIsMasterKeyBackedUp(_ transaction: DBReadTransaction) -> Bool { return keyValueStore.getBool(Keys.isMasterKeyBackedUp, defaultValue: false, transaction: transaction) } public func getMasterKey(_ transaction: DBReadTransaction) -> Data? { return keyValueStore.getData(Keys.masterKey, transaction: transaction) } public func getPinType(_ transaction: DBReadTransaction) -> SVR.PinType? { guard let raw = keyValueStore.getInt(Keys.pinType, transaction: transaction) else { return nil } return SVR.PinType(rawValue: raw) } public func getEncodedPINVerificationString(_ transaction: DBReadTransaction) -> String? { return keyValueStore.getString(Keys.encodedPINVerificationString, transaction: transaction) } // Linked devices get the storage service key and store it locally. The primary doesn't do this. // TODO: By 10/2024, we can remove this method. Starting in 10/2023, we started sending // master keys in syncs. A year later, any primary that has not yet delivered a master // key must not have launched and is therefore deregistered; we are ok to ignore the // storage service key and take the master key or bust. public func getSyncedStorageServiceKey(_ transaction: DBReadTransaction) -> Data? { return keyValueStore.getData(Keys.syncedStorageServiceKey, transaction: transaction) } public func getSVR2MrEnclaveStringValue(_ transaction: DBReadTransaction) -> String? { return keyValueStore.getString(Keys.svr2MrEnclaveStringValue, transaction: transaction) } public func hadSVR1Enclave(_ transaction: DBReadTransaction) -> Bool { return keyValueStore.hasValue(Keys.legacy_svr1EnclaveName, transaction: transaction) } // MARK: - Setters public func setIsMasterKeyBackedUp(_ value: Bool, _ transaction: DBWriteTransaction) { keyValueStore.setBool(value, key: Keys.isMasterKeyBackedUp, transaction: transaction) } public func setMasterKey(_ value: Data?, _ transaction: DBWriteTransaction) { keyValueStore.setData(value, key: Keys.masterKey, transaction: transaction) } public func setPinType(_ value: SVR.PinType, _ transaction: DBWriteTransaction) { keyValueStore.setInt(value.rawValue, key: Keys.pinType, transaction: transaction) } public func setEncodedPINVerificationString(_ value: String?, _ transaction: DBWriteTransaction) { keyValueStore.setString(value, key: Keys.encodedPINVerificationString, transaction: transaction) } // Linked devices get the storage service key and store it locally. The primary doesn't do this. public func setSyncedStorageServiceKey(_ value: Data?, _ transaction: DBWriteTransaction) { keyValueStore.setData(value, key: Keys.syncedStorageServiceKey, transaction: transaction) } // Linked devices get the backup key and store it locally. The primary doesn't do this. public func setSyncedBackupKey(_ value: Data?, _ transaction: DBWriteTransaction) { keyValueStore.setData(value, key: Keys.syncedBackupKey, transaction: transaction) } public func setSVR2MrEnclaveStringValue(_ value: String?, _ transaction: DBWriteTransaction) { keyValueStore.setString(value, key: Keys.svr2MrEnclaveStringValue, transaction: transaction) } // MARK: - Clearing Keys public func clearKeys(_ transaction: DBWriteTransaction) { keyValueStore.removeValues( forKeys: [ Keys.masterKey, Keys.pinType, Keys.encodedPINVerificationString, Keys.isMasterKeyBackedUp, Keys.syncedStorageServiceKey, Keys.legacy_svr1EnclaveName, Keys.svr2MrEnclaveStringValue ], transaction: transaction ) } // MARK: - Identifiers private enum Keys { // These must not change, they match what was historically in KeyBackupServiceImpl. static let masterKey = "masterKey" static let pinType = "pinType" static let encodedPINVerificationString = "encodedVerificationString" static let isMasterKeyBackedUp = "isMasterKeyBackedUp" static let syncedStorageServiceKey = "Storage Service Encryption" static let syncedBackupKey = "Backup Key" // Kept around because its existence indicates we had an svr1 backup. static let legacy_svr1EnclaveName = "enclaveName" static let svr2MrEnclaveStringValue = "svr2_mrenclaveStringValue" } }