feat(apk): copie de secours au mot de passe, le coffre survit au retrait du schema

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-17 15:13:13 +01:00
parent d6bf06b6a7
commit 8269d99b5b
2 changed files with 144 additions and 11 deletions
+72 -4
View File
@@ -130,6 +130,9 @@ type
/// ouverture qui ne peut pas aboutir, et nomme le telechargement pour ce
/// qu'il est : un remplacement.
FVaultUnreadable: Boolean;
// La cle du verrouillage d'ecran est morte (schema retire ou reinitialise) :
// on ouvre la copie de secours, scellee sous le mot de passe.
FRecover: Boolean;
// Mot de passe de sync tel qu'il a ete range avec le coffre. Il rend le
// rafraichissement de fond silencieux : sans lui, chaque modification
// faite sur le poste ferait apparaitre une invite au lancement.
@@ -508,7 +511,8 @@ begin
// coffre scelle sous un code s'ouvrait donc sans code, echouait, et l'echec
// etait pris pour une corruption - l'appareil proposait de retelecharger un
// coffre qu'il avait parfaitement en main.
WebCall(Format('PM.setPinNeeded(%s)', [LowerCase(BoolToStr(VaultNeedsPin, True))]));
WebCall(Format('PM.setPinNeeded(%s)',
[LowerCase(BoolToStr(VaultNeedsPin or FRecover, True))]));
// LES TEMOINS SE REPOSENT ICI, A CHAQUE RETOUR SUR L'ACCUEIL.
//
@@ -540,7 +544,9 @@ begin
LCanOpen := HasVault and not FVaultUnreadable;
if LCanOpen then
begin
if VaultNeedsPin then
if FRecover then
Status('Screen lock changed - type your vault password')
else if VaultNeedsPin then
Status('Vault on this device - unlock code required')
else if VaultNeedsAuth then
Status('Vault on this device - protected')
@@ -1787,6 +1793,7 @@ begin
FPending := False;
FETag := '';
FVaultUnreadable := False;
FRecover := False;
Log('DEBUG: local vault dropped');
ShowUnlock;
WebCall('PM.toast("Local vault deleted")');
@@ -1904,6 +1911,7 @@ begin
Log(' ' + FVault.LiveCount.ToString + ' entries, ' +
FVault.TrashCount.ToString + ' in trash');
FVaultUnreadable := False;
FRecover := False;
Touch;
FIdleTimer.Enabled := True;
// Deja dans l'application : reconstruire la liste SANS y ramener
@@ -2125,7 +2133,7 @@ procedure TForm8.OpenLocalNow;
var
LPlain, LETag, LPwd: string;
LSw: TStopwatch;
LChainSync: Boolean;
LChainSync, LOk: Boolean;
begin
// LU ET CONSOMME ICI, une fois pour toutes : un drapeau qui survivrait a un
// code faux ferait synchroniser la prochaine ouverture, sans rapport.
@@ -2140,8 +2148,28 @@ begin
// LA CLE REVIENT AVEC LE CACHE, et c'est tout l'objet de l'y avoir mise :
// ouvrir depuis le cache ne derive rien, donc sans cela le PREMIER
// enregistrement de chaque session repayait les quatre secondes et demie.
if LoadVault(LPlain, LETag, LPwd, FKey, FKeySalt, FKeyIters, FPin) then
if FRecover then
LOk := LoadRecoveryVault(LPlain, LETag, LPwd, FKey, FKeySalt, FKeyIters, FPin)
else
LOk := LoadVault(LPlain, LETag, LPwd, FKey, FKeySalt, FKeyIters, FPin);
if LOk then
begin
if FRecover then
begin
FRecover := False;
// RESCELLER TOUT DE SUITE, sous ce qui existe maintenant : un nouveau
// verrouillage d'ecran s'il y en a un (cle neuve, l'ancienne est morte),
// sinon le mot de passe qu'on vient de taper - jamais en clair.
try
if BiometricAvailable then
SaveVault(LPlain, LETag, LPwd, True, True, '', FKey, FKeySalt, FKeyIters)
else
SaveVault(LPlain, LETag, LPwd, False, False, LPwd, FKey, FKeySalt, FKeyIters);
Log('Opened with the vault password after a screen lock change; cache re-sealed');
except
on E: Exception do Log('Re-seal after recovery failed: ' + E.Message);
end;
end;
Log('Vault opened from cache in ' + LSw.ElapsedMilliseconds.ToString + ' ms');
FETag := LETag;
if LETag = '' then
@@ -2272,6 +2300,25 @@ begin
// l'appareil detient, et ecraserait donc l'explication.
// Un code faux se retape ; le cache, lui, n'a rien perdu. Le marquer
// illisible retirerait le bouton d'ouverture pour le reste de la session.
if FRecover then
begin
Log('Recovery copy did not open under this password');
ShowUnlock;
StatusErr('Wrong vault password');
WebCall('PM.focusPin()');
Exit;
end;
// LA CLE VIENT DE MOURIR SOUS NOS YEUX, mais la copie de secours est la :
// on demande le mot de passe au lieu de declarer le coffre illisible.
if LastStoreKeyInvalidated and HasRecoveryVault then
begin
FRecover := True;
Log('Screen lock key destroyed - recovery copy available');
ShowUnlock;
StatusErr('Screen lock changed - type your vault password');
WebCall('PM.focusPin()');
Exit;
end;
if VaultNeedsPin then
begin
Log('Vault did not open under this unlock code');
@@ -2319,6 +2366,27 @@ begin
WebCall('PM.focusPin()');
Exit;
end;
// PLUS DE VERROUILLAGE D'ECRAN : la cle qui scelle le cache est morte, et
// demander l'empreinte ne pourrait que repondre "indisponible". La copie de
// secours s'ouvre au mot de passe.
if (not FRecover) and VaultNeedsAuth and HasRecoveryVault and
(not BiometricAvailable) then
begin
FRecover := True;
Log('No screen lock any more - recovery copy available');
ShowUnlock;
end;
if FRecover then
begin
if FPin = '' then
begin
StatusErr('Screen lock changed - type your vault password');
WebCall('PM.focusPin()');
Exit;
end;
OpenLocalNow;
Exit;
end;
// L'authentification affiche un dialogue systeme : elle doit rester sur le
// thread principal, et PRECEDER toute lecture du coffre.
if not VaultNeedsAuth then