Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add password input delay to slow down brute force attacks #749

Merged
merged 3 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ class StartScreenViewController: UIViewController, CAAnimationDelegate {
} catch let error {
DispatchQueue.main.async {
if error.isInvalidPassphraseError {
dialogDelegate?.displayError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
dialogDelegate?.displayPassphraseError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
} else {
dialogDelegate?.displayError(errorMessage: error.localizedDescription)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ typealias SimpleTextInputDialogCallback = (_ userInput: String, _ dialogDelegate
protocol InputDialogDelegate {
func dismissDialog()
func displayError(errorMessage: String)
func displayPassphraseError(errorMessage: String)
}

extension InputDialogDelegate {
func displayPassphraseError(errorMessage: String) {

}
}

class SimpleTextInputDialog: UIViewController {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class SignMessageViewController: UIViewController, FloatingPlaceholderTextViewDe

} else if error!.isInvalidPassphraseError {
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.wallet.id_)
dialogDelegate?.displayError(errorMessage: errorMessage)
dialogDelegate?.displayPassphraseError(errorMessage: errorMessage)
} else {
print("sign error:", error!.localizedDescription)
dialogDelegate?.dismissDialog()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class SettingsController: UITableViewController {
} catch let error {
DispatchQueue.main.async {
if error.isInvalidPassphraseError {
dialogDelegate?.displayError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
dialogDelegate?.displayPassphraseError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
} else {
dialogDelegate?.displayError(errorMessage: error.localizedDescription)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class RequestPasswordViewController: SecurityCodeRequestBaseViewController, UITe
@IBOutlet weak var btnSubmit: Button!

var dissmisSenderOnCancel = false

var passwordTrials = 0

override func viewDidLoad() {
super.viewDidLoad()
Expand Down Expand Up @@ -200,9 +202,37 @@ class RequestPasswordViewController: SecurityCodeRequestBaseViewController, UITe
return true
}

override func showPassphraseError(text: String) {
passwordTrials = passwordTrials + 1

_showError(text: text)
self.passwordInput.isEnabled = false
self.btnSubmit.isEnabled = false

DispatchQueue.global(qos: .userInitiated).async {
var delay = 2.0
if (self.passwordTrials % 2 == 0) {
delay = 5.0
}

let delayTime = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: delayTime) {
self.passwordInput.text = ""
self.hideError()
self.passwordInput.isEnabled = true
self.passwordInput.becomeFirstResponder()
}

}
}

override func showError(text: String) {
super.showError(text: text)

_showError(text: text)
}

func _showError(text: String) {
self.passwordErrorLabel.text = text
self.passwordErrorLabel.isHidden = false

Expand Down
30 changes: 30 additions & 0 deletions Decred Wallet/Features/Security/RequestPinViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class RequestPinViewController: SecurityCodeRequestBaseViewController {
textfield.textContentType = .password
return textfield
}()

var pinTrials = 0

override func viewDidLoad() {
super.viewDidLoad()
Expand Down Expand Up @@ -218,10 +220,38 @@ class RequestPinViewController: SecurityCodeRequestBaseViewController {
self.dismissView()
}
}

override func showPassphraseError(text: String) {
pinTrials = pinTrials + 1

_showError(text: text)
self.pinHiddenInput.isEnabled = false
self.btnSubmit.isEnabled = false

DispatchQueue.global(qos: .userInitiated).async {

var delay = 2.0
if (self.pinTrials % 2 == 0) {
delay = 5.0
}

let delayTime = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: delayTime) {
self.pinHiddenInput.text = ""
self.hideError()
self.pinHiddenInput.isEnabled = true
self.pinHiddenInput.becomeFirstResponder()
}
}
}

override func showError(text: String) {
super.showError(text: text)

_showError(text: text)
}

private func _showError(text: String){
self.errorLabel.text = text
self.errorLabel.isHidden = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class SecurityCodeRequestBaseViewController: UIViewController {
func hideError() {
self.isInErrorState = false
}

func showPassphraseError(text: String) {
beansgum marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@C-ollins nothing seems to be done inside this function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

extension SecurityCodeRequestBaseViewController: InputDialogDelegate {
Expand All @@ -60,6 +63,10 @@ extension SecurityCodeRequestBaseViewController: InputDialogDelegate {
func displayError(errorMessage: String) {
self.showError(text: errorMessage)
}

func displayPassphraseError(errorMessage: String) {
self.showPassphraseError(text: errorMessage)
}
}

extension SecurityCodeRequestBaseViewController: KeyboardVisibilityDelegate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,8 @@ class SeedBackupVerifyViewController: UIViewController {
} catch {
DispatchQueue.main.async {
if error.isInvalidPassphraseError {
print(error.localizedDescription)
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.walletID)
dialogDelegate?.displayError(errorMessage: errorMessage)
dialogDelegate?.displayPassphraseError(errorMessage: errorMessage)
self.groupedSeedWordsTableView?.isUserInteractionEnabled = true
} else if error.localizedDescription == DcrlibwalletErrInvalid {
self.groupedSeedWordsTableView?.isUserInteractionEnabled = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,20 @@ class SeedWordsDisplayViewController: UIViewController {
.with(submitBtnText: LocalizedStrings.confirm)
.should(showCancelButton: true)
.requestCurrentCode(sender: self, dismissSenderOnCancel: true) { privatePass, _, dialogDelegate in
var errorOrNil: NSError? = nil
if let decryptedSeed = WalletLoader.shared.multiWallet.wallet(withID: self.walletID)?.decryptSeed(privatePass.utf8Bits, error: &errorOrNil) {
if errorOrNil == nil {
var error: NSError? = nil
if let decryptedSeed = WalletLoader.shared.multiWallet.wallet(withID: self.walletID)?.decryptSeed(privatePass.utf8Bits, error: &error) {
if error == nil {
self.seed = decryptedSeed
dialogDelegate?.dismissDialog()
self.displaySeed()
} else {
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.walletID)
dialogDelegate?.displayError(errorMessage: errorMessage)
if error!.isInvalidPassphraseError {
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.walletID)
dialogDelegate?.displayPassphraseError(errorMessage: errorMessage)
}else {
dialogDelegate?.displayError(errorMessage: error!.localizedDescription)
}

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class ConfirmToSendFundViewController: UIViewController, UITextFieldDelegate {
self.onSendCompleted?()
} else if error!.isInvalidPassphraseError {
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.sourceWalletID)
dialogDelegate?.displayError(errorMessage: errorMessage)
dialogDelegate?.displayPassphraseError(errorMessage: errorMessage)
} else {
print("send error:", error!.localizedDescription)
dialogDelegate?.dismissDialog()
Expand Down
16 changes: 10 additions & 6 deletions Decred Wallet/Features/Wallets/WalletSettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,13 @@ class WalletSettingsViewController: UIViewController {
}
} catch let error {
DispatchQueue.main.async {
var errorMessage = error.localizedDescription
if error.isInvalidPassphraseError {
errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.wallet.id_)
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.wallet.id_)
dialogDelegate?.displayPassphraseError(errorMessage: errorMessage)
} else {
dialogDelegate?.displayError(errorMessage: error.localizedDescription)
}
dialogDelegate?.displayError(errorMessage: errorMessage)

}
}
}
Expand All @@ -132,11 +134,13 @@ class WalletSettingsViewController: UIViewController {
}
} catch let error {
DispatchQueue.main.async {
var errorMessage = error.localizedDescription
if error.isInvalidPassphraseError {
errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.wallet.id_)
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: self.wallet.id_)
dialogDelegate?.displayPassphraseError(errorMessage: errorMessage)
} else {
dialogDelegate?.displayError(errorMessage: error.localizedDescription)
}
dialogDelegate?.displayError(errorMessage: errorMessage)

}
}
}
Expand Down
9 changes: 7 additions & 2 deletions Decred Wallet/Features/Wallets/WalletsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class WalletsViewController: UIViewController {
onVerifiedSuccess()
} catch let error {
if error.isInvalidPassphraseError {
completion?.displayError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
completion?.displayPassphraseError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
} else {
completion?.displayError(errorMessage: error.localizedDescription)
}
Expand Down Expand Up @@ -335,7 +335,12 @@ extension WalletsViewController: WalletInfoTableViewCellDelegate {
}
} catch {
DispatchQueue.main.async {
completion?.displayError(errorMessage: error.localizedDescription)
if error.isInvalidPassphraseError {
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: wallet.id)
completion?.displayPassphraseError(errorMessage: errorMessage)
} else {
completion?.displayError(errorMessage: error.localizedDescription)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class SingleToMultiWalletMigration {

DispatchQueue.main.async {
if error.isInvalidPassphraseError {
dialogDelegate?.displayError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
dialogDelegate?.displayPassphraseError(errorMessage: StartupPinOrPassword.invalidSecurityCodeMessage())
} else {
dialogDelegate?.displayError(errorMessage: error.localizedDescription)
}
Expand Down
2 changes: 1 addition & 1 deletion Decred Wallet/Wallet Utils/SpendingPinOrPassword.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ struct SpendingPinOrPassword {
DispatchQueue.main.async {
if error.isInvalidPassphraseError {
newCodeRequestDelegate?.dismissDialog()
currentCodeRequestDelegate?.displayError(errorMessage: self.invalidSecurityCodeMessage(for: walletID))
currentCodeRequestDelegate?.displayPassphraseError(errorMessage: self.invalidSecurityCodeMessage(for: walletID))
} else {
newCodeRequestDelegate?.displayError(errorMessage: error.localizedDescription)
}
Expand Down
2 changes: 1 addition & 1 deletion Decred Wallet/Wallet Utils/StartupPinOrPassword.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ struct StartupPinOrPassword {
DispatchQueue.main.async {
if error.isInvalidPassphraseError {
newCodeRequestDelegate?.dismissDialog()
currentCodeRequestDelegate?.displayError(errorMessage: self.invalidSecurityCodeMessage())
currentCodeRequestDelegate?.displayPassphraseError(errorMessage: self.invalidSecurityCodeMessage())
} else {
newCodeRequestDelegate?.displayError(errorMessage: error.localizedDescription)
}
Expand Down
11 changes: 6 additions & 5 deletions Decred Wallet/Wallet Utils/SyncManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,14 @@ class SyncManager: NSObject {
waitGroup.leave()
}
} catch let error {
var errorMessage = error.localizedDescription
if error.isInvalidPassphraseError {
errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: wallet)
}

DispatchQueue.main.async {
dialogDelegate?.displayError(errorMessage: errorMessage)
if error.isInvalidPassphraseError {
let errorMessage = SpendingPinOrPassword.invalidSecurityCodeMessage(for: wallet)
dialogDelegate?.displayPassphraseError(errorMessage: errorMessage)
} else {
dialogDelegate?.displayError(errorMessage: error.localizedDescription)
}
}
}
}
Expand Down