aboutsummaryrefslogtreecommitdiff
path: root/source/ui/PinView.mc
diff options
context:
space:
mode:
Diffstat (limited to 'source/ui/PinView.mc')
-rw-r--r--source/ui/PinView.mc87
1 files changed, 64 insertions, 23 deletions
diff --git a/source/ui/PinView.mc b/source/ui/PinView.mc
index 4502919..fa58bfe 100644
--- a/source/ui/PinView.mc
+++ b/source/ui/PinView.mc
@@ -2,11 +2,18 @@
// Summary: PIN entry for unlocking, creating and changing the PIN: touch keypad or button digit selector, lockout display.
//
// README 13.1 and 13.4:
-// * Touch screens get a 3x4 keypad (digits, delete, OK). Watches without a
-// touch screen get a digit selector: UP/DOWN change the digit, START
-// adds it (or confirms on "OK"), BACK deletes.
+// * A PIN has exactly 4 digits (KeyManager.PIN_DIGITS) and is submitted
+// as soon as the 4th is entered; there is no OK key.
+// * Touch screens get a keypad (1-9, delete, 0). The START button does
+// nothing there: a tap can also arrive as START, which used to submit a
+// PIN early. Watches without a touch screen get a digit selector:
+// UP/DOWN change the digit, START adds it, BACK deletes.
+// * A legacy PIN (4 to 10 digits, set before the 4-digit rule) is entered
+// the old way, with an OK key (and START on touch screens, "OK" in the
+// selector); after it unlocks, the screen asks for a new 4-digit PIN
+// and then continues to what was opened.
// * Entered digits show as dots. A wrong PIN vibrates and shows any wait.
-// * Creating or changing the PIN asks twice and warns below 6 digits.
+// * Creating or changing the PIN asks twice.
// * Key derivation (about 1 s) runs as a job on a timer, so the screen
// shows "Checking..." and the watchdog never trips.
// The typed digits are a ByteArray that is wiped after every attempt.
@@ -23,18 +30,19 @@ class PinView extends WatchUi.View {
static const MODE_CREATE = 1;
static const MODE_CHANGE = 2;
- // Selector positions 0-9 are digits; this one is "OK".
+ // Selector positions 0-9 are digits; this one is "OK" (legacy PINs only).
static const SELECT_OK = 10;
- // Keypad keys besides the digits.
+ // Keypad keys besides the digits (OK for legacy PINs only).
static const KEY_DELETE = -1;
static const KEY_OK = -2;
- static const RECOMMENDED_DIGITS = 6;
// Calibrate the PIN derivation to about this long on this watch.
static const DERIVATION_TARGET_MS = 1000;
static const CALIBRATION_SAMPLE = 200;
var mode as Number;
var busy as Boolean = false;
+ // Unlocking a legacy PIN: 4 to 10 digits, submitted with OK.
+ var legacy as Boolean = false;
private var _onSuccess as Method() as Void or Null;
private var _digits as ByteArray = []b;
@@ -57,6 +65,7 @@ class PinView extends WatchUi.View {
View.initialize();
mode = pinMode;
_onSuccess = onSuccess;
+ legacy = pinMode == MODE_UNLOCK && AppState.theVault().isLegacyPin();
}
function onShow() as Void {
@@ -85,7 +94,8 @@ class PinView extends WatchUi.View {
// ---- Input (called by PinDelegate) ----------------------------------
- // A keypad key: 0-9, KEY_DELETE or KEY_OK.
+ // A keypad key: 0-9, KEY_DELETE or KEY_OK (legacy PINs only). The
+ // PIN_DIGITS-th digit of a 4-digit PIN submits it.
function press(key as Number) as Void {
if (busy || waitSeconds() > 0) {
return;
@@ -93,17 +103,23 @@ class PinView extends WatchUi.View {
if (key == KEY_DELETE) {
deleteDigit();
} else if (key == KEY_OK) {
- submit();
- } else if (_digits.size() < KeyManager.MAX_PIN_DIGITS) {
+ if (legacy) {
+ submit();
+ }
+ } else if (_digits.size() < (legacy ? KeyManager.MAX_PIN_DIGITS : KeyManager.PIN_DIGITS)) {
_digits.add(0x30 + key);
_message = null;
+ if (!legacy && _digits.size() == KeyManager.PIN_DIGITS) {
+ submit();
+ }
}
WatchUi.requestUpdate();
}
- // Selector: UP/DOWN.
+ // Selector: UP/DOWN, over the digits (and "OK" for a legacy PIN).
function cycle(delta as Number) as Void {
- _selector = ((_selector + delta) % (SELECT_OK + 1) + SELECT_OK + 1) % (SELECT_OK + 1);
+ var positions = legacy ? SELECT_OK + 1 : SELECT_OK;
+ _selector = ((_selector + delta) % positions + positions) % positions;
WatchUi.requestUpdate();
}
@@ -146,19 +162,16 @@ class PinView extends WatchUi.View {
// ---- Checking and setting the PIN ------------------------------------
+ // A complete entry: check it (unlock) or keep it for the repeat (new PIN).
private function submit() as Void {
- if (!KeyManager.isValidPin(_digits)) {
- showMessage("4 to 10 digits", true);
- return;
+ if (!(legacy ? KeyManager.isValidPin(_digits) : KeyManager.isValidNewPin(_digits))) {
+ return; // a legacy OK with fewer than 4 digits
}
if (mode == MODE_UNLOCK) {
startUnlock();
} else if (_first == null) {
_first = _digits;
_digits = []b;
- if (_first.size() < RECOMMENDED_DIGITS) {
- showMessage("6+ digits are safer", false);
- }
} else if (Bytes.equal(_first as ByteArray, _digits)) {
startSetPin();
} else {
@@ -181,6 +194,7 @@ class PinView extends WatchUi.View {
clearDigits();
if (e.code == VaultError.NO_PIN) {
mode = MODE_CREATE;
+ legacy = false;
showMessage("Set a PIN first", false);
} else if (e.code != VaultError.LOCKED_OUT) {
showMessage("PIN data damaged", true);
@@ -201,7 +215,7 @@ class PinView extends WatchUi.View {
} catch (e instanceof VaultError) {
Bytes.wipe(_jobPin);
_jobPin = null;
- showMessage("Unlock first", true);
+ showMessage(e.code == VaultError.LOCKED ? "Unlock first" : KeyManager.PIN_DIGITS + " digits", true);
return;
}
startJob(method(:onSetDone));
@@ -225,8 +239,13 @@ class PinView extends WatchUi.View {
if (vault.finishUnlock(_job as PinJob)) {
_job = null;
vault.promotePending();
+ ChangeQueue.applyLocks(vault);
// Unlocked: "Encrypt names" can now reach locked entries.
AppState.processSettings();
+ if (legacy) {
+ askForNewPin();
+ return;
+ }
succeed();
return;
}
@@ -249,9 +268,23 @@ class PinView extends WatchUi.View {
vault.finishSetPin(_job as PinJob);
_job = null;
vault.promotePending();
+ ChangeQueue.applyLocks(vault);
succeed();
}
+ // After a legacy PIN unlocked: the same screen asks for a new 4-digit
+ // PIN, and onSuccess runs once that is set. BACK leaves it unlocked
+ // with the old PIN, which asks again at the next unlock.
+ private function askForNewPin() as Void {
+ AppState.activity();
+ legacy = false;
+ mode = MODE_CHANGE;
+ if (_selector == SELECT_OK) {
+ _selector = 0;
+ }
+ showMessage("Set a new " + KeyManager.PIN_DIGITS + "-digit PIN", false);
+ }
+
private function succeed() as Void {
AppState.activity();
var next = _onSuccess;
@@ -307,8 +340,8 @@ class PinView extends WatchUi.View {
2 * Ui.halfWidthAt(dc, y)), Graphics.TEXT_JUSTIFY_CENTER);
y += dc.getFontHeight(Graphics.FONT_SMALL) + 4;
- // Dots: entered digits filled, at least RECOMMENDED_DIGITS shown.
- var slots = _digits.size() > RECOMMENDED_DIGITS ? _digits.size() : RECOMMENDED_DIGITS;
+ // Dots: entered digits filled, PIN_DIGITS slots (more for a long legacy PIN).
+ var slots = _digits.size() > KeyManager.PIN_DIGITS ? _digits.size() : KeyManager.PIN_DIGITS;
var r = h / 60 > 3 ? h / 60 : 3;
var step = r * 3;
var x = cx - (slots - 1) * step / 2;
@@ -367,6 +400,9 @@ class PinView extends WatchUi.View {
_keys = [] as Array<Array<Number>>;
var log = "";
for (var i = 0; i < 12; i++) {
+ if (values[i] == KEY_OK && !legacy) {
+ continue; // no OK key: the 4th digit submits; the slot stays empty
+ }
var kx = left + (i % 3) * keyW;
var ky = top + (i / 3) * keyH;
_keys.add([kx, ky, keyW, keyH, values[i] as Number]);
@@ -468,11 +504,16 @@ class PinDelegate extends WatchUi.BehaviorDelegate {
return true;
}
+ // START on a touch screen submits only a legacy PIN. Otherwise it does
+ // nothing: one tap can arrive both here and in onTap, and submitting
+ // the digits typed so far was a wrong PIN.
function onKey(event as WatchUi.KeyEvent) as Boolean {
var v = view();
if (v != null && Ui.isTouch() && event.getKey() == WatchUi.KEY_ENTER) {
- v.markChanged();
- v.press(PinView.KEY_OK);
+ if (v.legacy) {
+ v.markChanged();
+ v.press(PinView.KEY_OK);
+ }
return true;
}
return false;