259 lines
9.2 KiB
Swift
259 lines
9.2 KiB
Swift
//
|
|
// Copyright 2020 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import Foundation
|
|
|
|
extension OWS2FAManager {
|
|
|
|
@objc
|
|
public static var isRegistrationLockV2EnabledKey = "isRegistrationLockV2Enabled"
|
|
|
|
@objc
|
|
public var isRegistrationLockEnabled: Bool {
|
|
switch mode {
|
|
case .V2:
|
|
return isRegistrationLockV2Enabled
|
|
case .V1:
|
|
return true // In v1 reg lock and 2fa are the same thing.
|
|
case .disabled:
|
|
return false
|
|
}
|
|
}
|
|
|
|
@objc
|
|
public var isRegistrationLockV2Enabled: Bool {
|
|
return databaseStorage.read { transaction in
|
|
OWS2FAManager.keyValueStore().getBool(
|
|
OWS2FAManager.isRegistrationLockV2EnabledKey,
|
|
defaultValue: false,
|
|
transaction: transaction
|
|
)
|
|
}
|
|
}
|
|
|
|
public func isRegistrationLockV2Enabled(transaction: SDSAnyReadTransaction) -> Bool {
|
|
return OWS2FAManager.keyValueStore().getBool(
|
|
OWS2FAManager.isRegistrationLockV2EnabledKey,
|
|
defaultValue: false,
|
|
transaction: transaction
|
|
)
|
|
}
|
|
|
|
public func requestEnable2FA(withPin pin: String, mode: OWS2FAMode) -> Promise<Void> {
|
|
return Promise { future in
|
|
requestEnable2FA(withPin: pin, mode: mode, success: {
|
|
future.resolve()
|
|
}) { error in
|
|
future.reject(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc
|
|
@available(swift, obsoleted: 1.0)
|
|
public func enableRegistrationLockV2() -> AnyPromise {
|
|
return AnyPromise(enableRegistrationLockV2())
|
|
}
|
|
|
|
public func enableRegistrationLockV2() -> Promise<Void> {
|
|
return DispatchQueue.global().async(.promise) { () -> String in
|
|
let token = Self.databaseStorage.read { tx in
|
|
return DependenciesBridge.shared.svr.data(
|
|
for: .registrationLock,
|
|
transaction: tx.asV2Read
|
|
)?.canonicalStringRepresentation
|
|
}
|
|
guard let token else {
|
|
throw OWSAssertionError("Cannot enable registration lock without an existing PIN")
|
|
}
|
|
return token
|
|
}.then { token -> Promise<HTTPResponse> in
|
|
let request = OWSRequestFactory.enableRegistrationLockV2Request(token: token)
|
|
return self.networkManager.makePromise(request: request)
|
|
}.done { _ in
|
|
self.databaseStorage.write { transaction in
|
|
OWS2FAManager.keyValueStore().setBool(
|
|
true,
|
|
key: OWS2FAManager.isRegistrationLockV2EnabledKey,
|
|
transaction: transaction
|
|
)
|
|
}
|
|
firstly {
|
|
return Promise.wrapAsync {
|
|
try await DependenciesBridge.shared.accountAttributesUpdater.updateAccountAttributes(authedAccount: .implicit())
|
|
}
|
|
}.catch { error in
|
|
Logger.error("Error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
public func markRegistrationLockV2Enabled(transaction: SDSAnyWriteTransaction) {
|
|
guard !DependenciesBridge.shared.tsAccountManager.registrationState(tx: transaction.asV2Read).isRegistered else {
|
|
return owsFailDebug("Unexpectedly attempted to mark reglock as enabled after registration")
|
|
}
|
|
|
|
OWS2FAManager.keyValueStore().setBool(
|
|
true,
|
|
key: OWS2FAManager.isRegistrationLockV2EnabledKey,
|
|
transaction: transaction
|
|
)
|
|
}
|
|
|
|
@objc
|
|
@available(swift, obsoleted: 1.0)
|
|
public func disableRegistrationLockV2() -> AnyPromise {
|
|
return AnyPromise(disableRegistrationLockV2())
|
|
}
|
|
|
|
public func disableRegistrationLockV2() -> Promise<Void> {
|
|
return firstly { () -> Promise<HTTPResponse> in
|
|
let request = OWSRequestFactory.disableRegistrationLockV2Request()
|
|
return self.networkManager.makePromise(request: request)
|
|
}.done { _ in
|
|
self.databaseStorage.write { transaction in
|
|
OWS2FAManager.keyValueStore().removeValue(
|
|
forKey: OWS2FAManager.isRegistrationLockV2EnabledKey,
|
|
transaction: transaction
|
|
)
|
|
}
|
|
firstly {
|
|
return Promise.wrapAsync {
|
|
try await DependenciesBridge.shared.accountAttributesUpdater.updateAccountAttributes(authedAccount: .implicit())
|
|
}
|
|
}.catch { error in
|
|
Logger.error("Error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
public func markRegistrationLockV2Disabled(transaction: SDSAnyWriteTransaction) {
|
|
guard !DependenciesBridge.shared.tsAccountManager.registrationState(tx: transaction.asV2Read).isRegistered else {
|
|
return owsFailDebug("Unexpectedly attempted to mark reglock as disabled after registration")
|
|
}
|
|
|
|
OWS2FAManager.keyValueStore().removeValue(
|
|
forKey: OWS2FAManager.isRegistrationLockV2EnabledKey,
|
|
transaction: transaction
|
|
)
|
|
}
|
|
|
|
@objc
|
|
@available(swift, obsoleted: 1.0)
|
|
public func migrateToRegistrationLockV2() -> AnyPromise {
|
|
return AnyPromise(migrateToRegistrationLockV2())
|
|
}
|
|
|
|
public func migrateToRegistrationLockV2() -> Promise<Void> {
|
|
guard let pinCode = pinCode else {
|
|
return Promise(error: OWSAssertionError("tried to migrate to registration lock V2 without legacy PIN"))
|
|
}
|
|
|
|
return firstly {
|
|
return requestEnable2FA(withPin: pinCode, mode: .V2)
|
|
}.then {
|
|
return self.enableRegistrationLockV2()
|
|
}
|
|
}
|
|
|
|
@objc
|
|
public func enable2FAV1(pin: String,
|
|
success: (() -> Void)?,
|
|
failure: ((Error) -> Void)?) {
|
|
// Convert the pin to arabic numerals, we never want to
|
|
// operate with pins in other numbering systems.
|
|
let request = OWSRequestFactory.enable2FARequest(withPin: pin.ensureArabicNumerals)
|
|
firstly {
|
|
Self.networkManager.makePromise(request: request)
|
|
}.done(on: DispatchQueue.main) { _ in
|
|
Self.databaseStorage.write { transaction in
|
|
self.markEnabled(pin: pin, transaction: transaction)
|
|
}
|
|
success?()
|
|
}.catch(on: DispatchQueue.main) { error in
|
|
owsFailDebugUnlessNetworkFailure(error)
|
|
failure?(error)
|
|
}
|
|
}
|
|
|
|
@objc
|
|
public func disable2FAV1(success: OWS2FASuccess?,
|
|
failure: OWS2FAFailure?) {
|
|
let request = OWSRequestFactory.disable2FARequest()
|
|
firstly {
|
|
Self.networkManager.makePromise(request: request)
|
|
}.done(on: DispatchQueue.main) { _ in
|
|
Self.databaseStorage.write { transaction in
|
|
self.markDisabled(transaction: transaction)
|
|
}
|
|
success?()
|
|
}.catch(on: DispatchQueue.main) { error in
|
|
owsFailDebugUnlessNetworkFailure(error)
|
|
failure?(error)
|
|
}
|
|
}
|
|
|
|
public static func isWeakPin(_ pin: String) -> Bool {
|
|
let normalizedPin = SVRUtil.normalizePin(pin)
|
|
|
|
guard pin.count >= kMin2FAv2PinLength else { return true }
|
|
|
|
// We only check numeric pins for weakness
|
|
guard normalizedPin.digitsOnly() == normalizedPin else { return false }
|
|
|
|
var allTheSame = true
|
|
var forwardSequential = true
|
|
var reverseSequential = true
|
|
|
|
var previousWholeNumberValue: Int?
|
|
for character in normalizedPin {
|
|
guard let current = character.wholeNumberValue else {
|
|
owsFailDebug("numeric pin unexpectedly contatined non-numeric characters")
|
|
break
|
|
}
|
|
|
|
defer { previousWholeNumberValue = current }
|
|
guard let previous = previousWholeNumberValue else { continue }
|
|
|
|
if previous != current { allTheSame = false }
|
|
if previous + 1 != current { forwardSequential = false }
|
|
if previous - 1 != current { reverseSequential = false }
|
|
|
|
if !allTheSame && !forwardSequential && !reverseSequential { break }
|
|
}
|
|
|
|
return allTheSame || forwardSequential || reverseSequential
|
|
}
|
|
|
|
@objc
|
|
public func clearLocalPinCode(transaction: SDSAnyWriteTransaction) {
|
|
Self.keyValueStore().removeValue(forKey: kOWS2FAManager_PinCode, transaction: transaction)
|
|
}
|
|
|
|
// MARK: - KeyBackupService Wrappers/Helpers
|
|
|
|
@objc
|
|
public func hasBackedUpMasterKey(transaction: SDSAnyReadTransaction) -> Bool {
|
|
return DependenciesBridge.shared.svr.hasBackedUpMasterKey(transaction: transaction.asV2Read)
|
|
}
|
|
|
|
@objc(generateAndBackupKeysWithPin:)
|
|
@available(swift, obsoleted: 1.0)
|
|
public func generateAndBackupKeys(with pin: String) -> AnyPromise {
|
|
let promise = DependenciesBridge.shared.svr.generateAndBackupKeys(pin: pin, authMethod: .implicit)
|
|
return AnyPromise(promise)
|
|
}
|
|
|
|
@objc
|
|
public func verifyKBSPin(_ pin: String, resultHandler: @escaping (Bool) -> Void) {
|
|
DependenciesBridge.shared.svr.verifyPin(pin, resultHandler: resultHandler)
|
|
}
|
|
|
|
@objc(deleteKeys)
|
|
public func deleteKBSKeys() -> AnyPromise {
|
|
return AnyPromise(DependenciesBridge.shared.svr.deleteKeys())
|
|
}
|
|
}
|