From dd44a164f665ff058537d497c90e7e63e45364fc Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:04:04 +0100 Subject: [PATCH] fix(android): le cache retient le secret qui ouvre CE coffre La separation laissait FVaultPwd vide apres une ouverture depuis le cache - seules la creation et la connexion le posaient - et la reconciliation refusait donc en boucle, "open vault first" alors que le coffre etait ouvert. Le cache porte un secret et un seul : celui du conteneur local pour un coffre ne ici, celui du depot pour un coffre publie. Les deux ne coexistent jamais, puisque la reconciliation detruit le conteneur local en passant l'autorite au depot. CachePwd choisit a l'ecriture, et la relecture le range dans la meme case. Co-Authored-By: Claude Opus 5 --- mainApk.pas | 53 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/mainApk.pas b/mainApk.pas index 3c17307..6b05d76 100644 --- a/mainApk.pas +++ b/mainApk.pas @@ -322,6 +322,14 @@ type procedure PublishPending; /// Demande au depot s'il a bouge, sans rien adopter. procedure ProbeRemote; + /// LE SECRET QUE LE CACHE DOIT RETENIR, et il n'y en a qu'un a la fois. + /// + /// Un coffre ne ici s'ouvre par le mot de passe de son conteneur ; un + /// coffre publie s'ouvre par le secret du depot. Les deux ne coexistent + /// jamais - la reconciliation detruit le conteneur local en passant + /// l'autorite au depot - donc une seule case suffit, a condition de + /// toujours y mettre le bon. + function CachePwd: string; /// Retire le code de deverrouillage : le cache se rescelle sous la seule /// cle de l'appareil. Exige un coffre ouvert. procedure ClearUnlockCode; @@ -965,7 +973,7 @@ begin if LLocalOnly then SaveLocalContainer(LBody); try - SaveVault(LNew, FETag, FSyncPwd, BiometricAvailable, False, + SaveVault(LNew, FETag, CachePwd, BiometricAvailable, False, FPinSealed, FKey, FKeySalt, FKeyIters); except on E: Exception do Log(' cache not written: ' + E.Message); @@ -1024,7 +1032,7 @@ begin // Le cache suit la fusion : sans cela il resterait sur notre // version d'avant, et le prochain essai repartirait de la. try - SaveVault(LNew, FETag, FSyncPwd, BiometricAvailable, False, + SaveVault(LNew, FETag, CachePwd, BiometricAvailable, False, FPinSealed, FKey, FKeySalt, FKeyIters); except on E: Exception do Log(' cache not written: ' + E.Message); @@ -1193,7 +1201,7 @@ begin begin FETag := LPut.ETag; try - SaveVault(LMerged, FETag, FSyncPwd, BiometricAvailable, False, + SaveVault(LMerged, FETag, CachePwd, BiometricAvailable, False, FPinSealed, FKey, FKeySalt, FKeyIters); except on E: Exception do Log(' cache not written: ' + E.Message); @@ -1354,7 +1362,7 @@ begin DropLocalContainer; FVaultPwd := ''; try - SaveVault(LMerged, FETag, FSyncPwd, BiometricAvailable, False, + SaveVault(LMerged, FETag, CachePwd, BiometricAvailable, False, FPinSealed, FKey, FKeySalt, FKeyIters); except on E: Exception do Log(' cache not written: ' + E.Message); @@ -2012,15 +2020,25 @@ begin Log(' WARNING: cache has no fingerprint - the refresh will re-derive'); if LPwd <> '' then begin - FSyncPwd := LPwd; - // ET LES REGLAGES AVEC. C'est ICI que tout passe - a chaque lancement - - // et non par la creation ni par la connexion, qui n'arrivent qu'une fois. - // Corriger celles-la seules ne changeait donc rien : PushConfig reposait - // la valeur de premier lancement au demarrage, l'ouverture ne la - // remplacait pas, et DoSync la PREFERE a celle du coffre. - FPwd := LPwd; - WebCall(Format('PM.setConfig(%s, %s, %s, false)', - [JsArg(FUrl), JsArg(FToken), JsArg(FPwd)])); + // DANS LA CASE QUI CONVIENT. Le cache d'un coffre ne ici porte le mot de + // passe de son conteneur, pas un secret de depot : le ranger dans + // FSyncPwd le ferait servir a sceller l'instantane, ce qu'on vient + // justement de defaire. Et sans cette relecture, FVaultPwd restait vide + // apres une ouverture depuis le cache - la reconciliation refusait alors + // en boucle, "open vault first" alors que le coffre etait ouvert. + if HasLocalContainer then + FVaultPwd := LPwd + else + begin + FSyncPwd := LPwd; + // ET LES REGLAGES AVEC. C'est ICI que tout passe - a chaque lancement - + // et non par la creation ni par la connexion, qui n'arrivent qu'une + // fois : PushConfig reposait la valeur de premier lancement au + // demarrage, l'ouverture ne la remplacait pas, et DoSync la PREFERE. + FPwd := LPwd; + WebCall(Format('PM.setConfig(%s, %s, %s, false)', + [JsArg(FUrl), JsArg(FToken), JsArg(FPwd)])); + end; end; // LA PROTECTION RATTRAPE LE CACHE. Elle se decidait a l'ecriture seule, @@ -2866,7 +2884,7 @@ begin try // ARequireAuth a FAUX : les deux protections s'excluent, un cache scelle // par un code porte VER_PIN et non VER_WRAP. - SaveVault(FPlain, FETag, FSyncPwd, False, False, ANew, + SaveVault(FPlain, FETag, CachePwd, False, False, ANew, FKey, FKeySalt, FKeyIters); if not VaultNeedsPin then begin @@ -2890,6 +2908,11 @@ begin end; end; +function TForm8.CachePwd: string; +begin + if HasLocalContainer then Result := FVaultPwd else Result := FSyncPwd; +end; + procedure TForm8.ClearUnlockCode; begin if FBusy then Exit; @@ -2906,7 +2929,7 @@ begin Exit; end; try - SaveVault(FPlain, FETag, FSyncPwd, BiometricAvailable, False, '', + SaveVault(FPlain, FETag, CachePwd, BiometricAvailable, False, '', FKey, FKeySalt, FKeyIters, True); if VaultNeedsPin then begin