// // Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import CryptoKit import Foundation import LibSignalClient public enum SVR { static let maximumKeyAttempts: UInt32 = 10 public enum KeysError: Error { case missingAep case missingMrbk } public enum DerivedKey: Hashable { /// The key required to bypass reglock and register or change number /// into an owned account. case registrationLock /// The key required to bypass sms verification when registering for an account. /// Independent from reglock; if reglock is present it is _also_ required, if not /// this token is still required. case registrationRecoveryPassword case storageService /// The key required to decrypt the Storage Service manifest with the /// given version. /// /// - Note /// The manifest contains identifiers and additional key data that are /// used to locate and decrypt Storage Service records. case storageServiceManifest(version: UInt64) /// A key used to hash values used for logging. case loggingKey /// Today, Storage Service records are encrypted using a key stored in /// the manifest. However, in the past they were encrypted using an /// SVR-derived key. This case represents the key formerly used to /// encrypt Storage Service records, which is preserved for the time /// being so that records that have not yet been re-encrypted with the /// new scheme can still be decrypted. /// /// Once all Storage Service records should be encrypted using the new /// scheme, we can remove this case. /// /// - Important /// This case should only be used for decryption, and never for /// encryption! case legacy_storageServiceRecord(identifier: StorageService.StorageIdentifier) } /// An auth credential is needed to talk to the SVR server. /// This defines how we should get that auth credential public indirect enum AuthMethod: Equatable { /// Explicitly provide an auth credential to use directly with SVR. /// note: if it fails, will fall back to the backup or implicit if unset. case svrAuth(SVRAuthCredential, backup: AuthMethod?) /// Get an SVR auth credential from the chat server first with the /// provided credentials, then use it to talk to the SVR server. case chatServerAuth(AuthedAccount) /// Use whatever SVR auth credential we have cached; if unavailable or /// if invalid, falls back to getting a SVR auth credential from the chat server /// with the chat server auth credentials we have cached. case implicit } public enum RestoreKeysResult { case success(MasterKey) case invalidPin(remainingAttempts: UInt32) // This could mean there was never a backup, or it's been // deleted due to using up all pin attempts. case backupMissing case networkError(Error) // Some other issue. case genericError(Error) } public struct DerivedKeyData { /// Can never be empty data; instances would fail to initialize. public let rawData: Data public let type: DerivedKey public var canonicalStringRepresentation: String { switch type { case .storageService, .storageServiceManifest, .legacy_storageServiceRecord, .registrationRecoveryPassword: return rawData.base64EncodedString() case .registrationLock, .loggingKey: return rawData.hexadecimalString } } } } public protocol SecureValueRecovery { /// Performs pending backups, migrations, and deletions. func refreshBackupIfNecessary() async throws /// Performs periodic credential refresh to help with re-registration. func refreshCredentialsIfNecessary() async throws /// Loads the users key, if any, from the SVR into the database. func restoreKeys(pin: String, authMethod: SVR.AuthMethod) async -> SVR.RestoreKeysResult /// Backs up the user's master key to SVR. func backupMasterKey(pin: String, masterKey: MasterKey, force: Bool, authMethod: SVR.AuthMethod) async throws func storeKeys( fromKeysSyncMessage syncMessage: SSKProtoSyncMessageKeys, authedDevice: AuthedDevice, tx: DBWriteTransaction, ) throws(SVR.KeysError) func storeKeys( fromProvisioningMessage provisioningMessage: LinkingProvisioningMessage, authedDevice: AuthedDevice, tx: DBWriteTransaction, ) }