Skip to content
This repository has been archived by the owner on Nov 4, 2023. It is now read-only.

Fingerprint unlock: backoff time between retries #443

Merged
merged 2 commits into from
Jul 14, 2022
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
2 changes: 1 addition & 1 deletion debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export QT_SELECT=qt5
DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH)

# Skip tests on the archs they are known to be flaky with current configuration
testskip_architectures := arm64 powerpc ppc64el s390x
testskip_architectures := powerpc ppc64el s390x

%:
dh $@ --parallel --fail-missing --with python3
Expand Down
35 changes: 24 additions & 11 deletions qml/Greeter/Greeter.qml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ Showable {

property int failedLoginsDelayAttempts: 7 // number of failed logins
property real failedLoginsDelayMinutes: 5 // minutes of forced waiting
property int failedFingerprintLoginsDisableAttempts: 3 // number of failed fingerprint logins
property int failedFingerprintLoginsDisableAttempts: 5 // number of failed fingerprint logins
property int failedFingerprintReaderRetryDelay: 250 // time to wait before retrying a failed fingerprint read [ms]

readonly property bool animating: loader.item ? loader.item.animating : false

Expand Down Expand Up @@ -559,6 +560,14 @@ Showable {
onLanguageChanged: LightDMService.infographic.readyForDataChange()
}

Timer {
id: fpRetryTimer
running: false
repeat: false
onTriggered: biometryd.startOperation()
interval: failedFingerprintReaderRetryDelay
}

Observer {
id: biometryd
objectName: "biometryd"
Expand All @@ -570,6 +579,14 @@ Showable {
Biometryd.available &&
AccountsService.enableFingerprintIdentification

function startOperation() {
if (idEnabled) {
var identifier = Biometryd.defaultDevice.identifier;
operation = identifier.identifyUser();
operation.start(biometryd);
}
}

function cancelOperation() {
if (operation) {
operation.cancel();
Expand All @@ -579,27 +596,23 @@ Showable {

function restartOperation() {
cancelOperation();

if (idEnabled) {
var identifier = Biometryd.defaultDevice.identifier;
operation = identifier.identifyUser();
operation.start(biometryd);
if (failedFingerprintReaderRetryDelay > 0) {
fpRetryTimer.running = true;
} else {
startOperation();
}
}

function failOperation(reason) {
console.log("Failed to identify user by fingerprint:", reason);
restartOperation();
if (!d.secureFingerprint) {
d.startUnlock(false /* toTheRight */); // use normal login instead
}
var msg = d.secureFingerprint ? i18n.tr("Try again") :
d.alphanumeric ? i18n.tr("Enter passphrase to unlock") :
i18n.tr("Enter passcode to unlock");
d.showFingerprintMessage(msg);
}

Component.onCompleted: restartOperation()
Component.onCompleted: startOperation()
Component.onDestruction: cancelOperation()
onIdEnabledChanged: restartOperation()

Expand All @@ -623,7 +636,7 @@ Showable {
onFailed: {
if (!d.secureFingerprint) {
failOperation("fingerprint reader is locked");
} else {
} else if (reason !== "ERROR_CANCELED") {
AccountsService.failedFingerprintLogins++;
failOperation(reason);
}
Expand Down
35 changes: 21 additions & 14 deletions tests/qmltests/Greeter/tst_Greeter.qml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Item {
height: loader.height
background: defaultBackground
viewSource: Qt.resolvedUrl("TestView.qml")

failedFingerprintReaderRetryDelay: 0
Component.onDestruction: {
loader.itemDestroyed = true;
}
Expand Down Expand Up @@ -548,7 +548,8 @@ Item {
verify(biometryd.operation.running);

biometryd.operation.mockSuccess(LightDM.Users.data(index, LightDM.UserRoles.UidRole));
viewTryToUnlockSpy.wait();
viewShowErrorMessageSpy.wait();
compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Enter passphrase to unlock"));
verify(greeter.locked);
}

Expand Down Expand Up @@ -577,22 +578,29 @@ Item {
compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Try again"));
}

function repeatBiometrydMockFailure(operationHost, count) {
for (var i = 0; i < count; i++) {
operationHost.operation.mockFailure("error");
}
}

function test_fingerprintTooManyFailures() {
var index = selectUser("has-password");
unlockAndShowGreeter(); // turn on lockscreen mode

var biometryd = findInvisibleChild(greeter, "biometryd");
biometryd.operation.mockFailure("error");
biometryd.operation.mockFailure("error");
repeatBiometrydMockFailure(biometryd, 4);
compare(viewTryToUnlockSpy.count, 0);

biometryd.operation.mockFailure("error");
viewTryToUnlockSpy.wait();
viewTryToUnlockSpy.clear();
repeatBiometrydMockFailure(biometryd, 1);
viewShowErrorMessageSpy.wait();
compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Try again"));
viewShowErrorMessageSpy.clear();

// Confirm that we are stuck in this mode until next login
biometryd.operation.mockSuccess(LightDM.Users.data(index, LightDM.UserRoles.UidRole));
viewTryToUnlockSpy.wait();
viewShowErrorMessageSpy.wait();
compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Enter passphrase to unlock"));

unlockAndShowGreeter();

Expand All @@ -605,17 +613,16 @@ Item {
unlockAndShowGreeter(); // turn on lockscreen mode

var biometryd = findInvisibleChild(greeter, "biometryd");
biometryd.operation.mockFailure("error");
biometryd.operation.mockFailure("error");
repeatBiometrydMockFailure(biometryd, 4);
compare(viewTryToUnlockSpy.count, 0);

unlockAndShowGreeter();
biometryd.operation.mockFailure("error");
biometryd.operation.mockFailure("error");
repeatBiometrydMockFailure(biometryd, 4);
compare(viewTryToUnlockSpy.count, 0);

biometryd.operation.mockFailure("error");
viewTryToUnlockSpy.wait();
repeatBiometrydMockFailure(biometryd, 1);
viewShowErrorMessageSpy.wait();
compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Try again"));
}

function test_fingerprintWrongUid() {
Expand Down