diff --git a/android-client/web/app.html b/android-client/web/app.html index 37d864b..877ca11 100644 --- a/android-client/web/app.html +++ b/android-client/web/app.html @@ -395,6 +395,9 @@
+ +

Diagnostics

@@ -1648,14 +1650,49 @@ document.getElementById('histBody').addEventListener('click', function (ev) { document.getElementById('sLog').addEventListener('click', function () { showLog(document.getElementById('log').hidden); }); -document.getElementById('sPinSet').addEventListener('click', function () { - var v = document.getElementById('sPinNew').value; - if (!v) { toast('Type a code first'); return; } - // DEMANDE EXPLICITE, EFFET IMMEDIAT. Le code ne se depose plus dans un champ - // que l'ouverture suivante appliquerait : Delphi refuse si le coffre n'est - // pas ouvert, ce qui prouve que le code courant est connu. +// FACON D'OUVRIR. Delphi dit laquelle est en place (PM.setUnlockMode) ; chaque +// bouton envoie la commande qui existe deja pour la poser. Revenir au +// verrouillage d'ecran, c'est retirer le code : il faut donc le code courant. +var unlockMode = 'none', canDeviceLock = false, hasContainer = false; +function syncUnlockUi() { + ['device', 'code', 'password'].forEach(function (m) { + var id = { device: 'sModeDevice', code: 'sModeCode', password: 'sModePwd' }[m]; + document.getElementById(id).classList.toggle('main', unlockMode === m); + }); + document.getElementById('sModeDevice').disabled = !canDeviceLock; + document.getElementById('sModeNote').textContent = + { device: 'Now: screen lock. Changing the screen lock or adding a fingerprint never deletes the vault.', + code: 'Now: app code. To go back to the screen lock, type the current code above.', + password: 'Now: vault password. To go back to the screen lock, type it above.', + none: 'Now: no protection - anyone holding the phone unlocked can open the vault.' }[unlockMode] + + (canDeviceLock ? '' : ' This phone has no screen lock.'); + document.getElementById('btnUsePwd').hidden = + !(authMode === 'open' && unlockMode === 'device' && hasContainer); +} +function modeField() { + var el = document.getElementById('sPinNew'), v = el.value; + el.value = ''; + return v; +} +document.getElementById('sModeCode').addEventListener('click', function () { + var v = modeField(); + if (!v) { toast('Type the new code above first'); return; } send('pinset?c=' + encodeURIComponent(v)); - document.getElementById('sPinNew').value = ''; +}); +document.getElementById('sModePwd').addEventListener('click', function () { + var v = modeField(); + if (!v) { toast('Type the vault password above first'); return; } + send('pinset?pw=1&c=' + encodeURIComponent(v)); +}); +document.getElementById('sModeDevice').addEventListener('click', function () { + if (unlockMode === 'device') { toast('Already using the screen lock'); return; } + var v = modeField(); + if (!v && unlockMode !== 'none') { toast('Type the current code or password above first'); return; } + send('pinclear?c=' + encodeURIComponent(v)); +}); +document.getElementById('btnUsePwd').addEventListener('click', function () { + PM.setAuthMode('login'); + document.getElementById('pass').focus(); }); document.getElementById('sWipe').addEventListener('click', function (ev) { // DEUX APPUIS, comme pour retirer le code : un effacement sans retour ne se @@ -1675,27 +1712,6 @@ document.getElementById('sWipe').addEventListener('click', function (ev) { send('wipelocal'); closeSettings(); }); -document.getElementById('sPinClear').addEventListener('click', function (ev) { - var b = ev.currentTarget; - if (b.getAttribute('data-armed') === null) { - b.setAttribute('data-armed', '1'); - b.textContent = 'Really remove - tap again'; - setTimeout(function () { - b.removeAttribute('data-armed'); - b.textContent = 'Remove the unlock code'; - }, 4000); - return; - } - b.removeAttribute('data-armed'); - b.textContent = 'Remove the unlock code'; - // LE CODE COURANT VOYAGE AVEC LA DEMANDE. Le coffre ouvert ne prouve rien : - // il peut l'etre depuis une heure et rester a l'ecran. Delphi compare a ce - // sous quoi le cache est reellement scelle, et refuse sinon. - send('pinclear?c=' + encodeURIComponent( - document.getElementById('sPinNew').value)); - document.getElementById('sPinNew').value = ''; -}); - function goList() { closeMenu(); document.body.classList.remove('on-detail'); @@ -1826,6 +1842,12 @@ var PM = { syncPinbox(); document.getElementById('btnAuth').textContent = (mode === 'register') ? 'Create vault' : 'Sign in'; + syncUnlockUi(); + }, + + setUnlockMode: function (mode, canDevice, container) { + unlockMode = mode; canDeviceLock = canDevice; hasContainer = container; + syncUnlockUi(); }, setPinNeeded: function (on) { diff --git a/mainApk.pas b/mainApk.pas index 3a2b117..d1f4112 100644 --- a/mainApk.pas +++ b/mainApk.pas @@ -337,6 +337,7 @@ type /// le code courant deja connu - c'est ce qui empeche un tiers d'en poser un /// que le proprietaire ne connaitrait pas. procedure SetUnlockCode(const ANew: string); + procedure PushUnlockMode; /// Va chercher l'icone du site de l'entree ouverte, ou l'efface. procedure SetEntryIcon(AFetch: Boolean); /// Le depot a bouge, ou ne l'a plus : LE DRAPEAU ET SON TEMOIN ENSEMBLE. @@ -534,6 +535,7 @@ begin WebCall('PM.setAuthMode("login")') else WebCall('PM.setAuthMode("register")'); + PushUnlockMode; LCanOpen := HasVault and not FVaultUnreadable; if LCanOpen then @@ -1701,7 +1703,10 @@ begin Exit // Touch a deja eu lieu plus haut else if LAction = 'state' then - FWebView := Param('view') + begin + FWebView := Param('view'); + if FWebView = 'settings' then PushUnlockMode; + end else if LAction = 'open' then OpenEntry(StrToIntDef(Param('i'), -1)) @@ -1789,7 +1794,16 @@ begin {$ENDIF} else if LAction = 'pinset' then - SetUnlockCode(TNetEncoding.URL.Decode(Param('c'))) + begin + // MODE MOT DE PASSE = le code EST le mot de passe du coffre. On le + // verifie : sinon une faute de frappe scellerait sous un "mot de passe" + // que l'utilisateur ne retapera jamais a l'identique. + if (Param('pw') = '1') and + ((FPlain = '') or (TNetEncoding.URL.Decode(Param('c')) <> CachePwd)) then + WebCall('PM.toast("Wrong vault password")') + else + SetUnlockCode(TNetEncoding.URL.Decode(Param('c'))); + end else if LAction = 'pinclear' then ClearUnlockCode(TNetEncoding.URL.Decode(Param('c'))) @@ -3070,6 +3084,7 @@ begin Log('Unlock code set'); WebCall('PM.toast("Unlock code set")'); WebCall('PM.pinChanged()'); + PushUnlockMode; except on E: Exception do begin @@ -3079,6 +3094,28 @@ begin end; end; +/// Dit a la page comment le cache est scelle. 'password' = scelle sous un code +/// egal au mot de passe du coffre - connu seulement une fois ouvert (FPinSealed). +procedure TForm8.PushUnlockMode; +var + LMode: string; +begin + if VaultNeedsPin then + begin + if (FPinSealed <> '') and (FPinSealed = CachePwd) then + LMode := 'password' + else + LMode := 'code'; + end + else if VaultNeedsAuth then + LMode := 'device' + else + LMode := 'none'; + WebCall(Format('PM.setUnlockMode("%s", %s, %s)', + [LMode, LowerCase(BoolToStr(BiometricAvailable, True)), + LowerCase(BoolToStr(HasLocalContainer, True))])); +end; + function TForm8.CachePwd: string; begin if HasLocalContainer then Result := FVaultPwd else Result := FSyncPwd; @@ -3117,7 +3154,10 @@ begin Exit; end; try - SaveVault(FPlain, FETag, CachePwd, BiometricAvailable, False, '', + // Cle NEUVE : le verrouillage d'ecran a pu changer pendant que le code + // protegeait, et une ancienne paire invalidee scellerait un fichier + // impossible a rouvrir. + SaveVault(FPlain, FETag, CachePwd, BiometricAvailable, True, '', FKey, FKeySalt, FKeyIters, True); if VaultNeedsPin then begin @@ -3130,6 +3170,7 @@ begin Log('Unlock code removed'); WebCall('PM.toast("Unlock code removed")'); WebCall('PM.pinChanged()'); + PushUnlockMode; ShowUnlock; except on E: Exception do