fix(android): poser, changer et retirer le code exigent un coffre ouvert

Deux branches de l'ouverture posaient le code a partir de ce qui trainait
dans les champs.

La premiere appliquait un "nouveau code" depose par les reglages. Elle
n'exigeait rien de celui qui le deposait : n'importe qui tenant le
telephone deverrouille pouvait en poser un, et c'est le proprietaire qui,
en ouvrant son coffre, l'appliquait contre lui-meme.

La seconde rescellait sous le code tape des que le cache ne l'etait plus,
ce qui DEFAISAIT LE RETRAIT : on retirait le code, le champ le portait
encore, et l'ouverture suivante le reposait sans qu'un mot le dise. Le
premier octet du cache le disait - VER_PIN apres un retrait annonce
reussi.

Poser et changer passent maintenant par SetUnlockCode, jumeau de
ClearUnlockCode : geste explicite, coffre deja ouvert - donc code courant
connu - et effet immediat. Le champ de l'accueil ne veut plus dire qu'une
chose, "ouvre avec ceci", et ne parait que lorsqu'un code est pose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-28 14:17:24 +01:00
parent 45564e7266
commit 900f5ba740
2 changed files with 95 additions and 69 deletions
+70 -36
View File
@@ -201,8 +201,7 @@ type
/// Seule l'ouverture du coffre y touche desormais - c'est le seul moment ou
/// l'on sait que l'utilisateur connait le code courant.
FPinSealed: string;
/// Le NOUVEAU code demande, quand on veut en changer. Vide sinon.
FPinNew: string;
// --- l'interface entiere, rendue par le moteur du systeme
FLayWeb: TLayout;
@@ -315,6 +314,10 @@ type
/// Retire le code de deverrouillage : le cache se rescelle sous la seule
/// cle de l'appareil. Exige un coffre ouvert.
procedure ClearUnlockCode;
/// Pose ou change le code de deverrouillage. Exige un coffre ouvert, donc
/// 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);
/// Va chercher l'icone du site de l'entree ouverte, ou l'efface.
procedure SetEntryIcon(AFetch: Boolean);
/// Envoie a la page les anciens mots de passe de l'entree ouverte.
@@ -1605,7 +1608,6 @@ begin
FToken := TNetEncoding.URL.Decode(Param('t')).Trim;
FPwd := TNetEncoding.URL.Decode(Param('p'));
FPin := TNetEncoding.URL.Decode(Param('c'));
FPinNew := TNetEncoding.URL.Decode(Param('c2'));
// L'empreinte du mot de passe AUSSI : si une saisie destinee au jeton
// atterrit dans le champ voisin - les deux sont masques - c'est celle-ci
// qui bouge, et le jeton reste inchange sans que rien ne l'explique.
@@ -1659,6 +1661,9 @@ begin
else if LAction = 'icon' then
SetEntryIcon(Param('a') = 'fetch')
else if LAction = 'pinset' then
SetUnlockCode(TNetEncoding.URL.Decode(Param('c')))
else if LAction = 'pinclear' then
ClearUnlockCode
@@ -1963,38 +1968,21 @@ begin
// avait pas. C'est la seule certitude de ce genre dans toute l'application.
if VaultNeedsPin then FPinSealed := FPin;
// CHANGER LE CODE - ici, et ici seulement. Il faut avoir ouvert le coffre,
// donc connaitre le code courant : sans cela une frappe distraite suffirait
// a rendre le cache inouvrable pour son proprietaire.
if (FPinNew <> '') and (FPinNew <> FPinSealed) then
begin
try
SaveVault(LPlain, LETag, LPwd, False, False, FPinNew,
FKey, FKeySalt, FKeyIters);
FPinSealed := FPinNew;
FPinNew := '';
Log('Unlock code changed');
WebCall('PM.toast("Unlock code changed")');
WebCall('PM.pinChanged()');
except
on E: Exception do Log('Unlock code not changed: ' + E.Message);
end;
end
else if (FPin <> '') and (not VaultNeedsPin) then
begin
try
SaveVault(LPlain, LETag, LPwd, False, False, FPin,
FKey, FKeySalt, FKeyIters);
FPinSealed := FPin;
if VaultNeedsPin then
Log('Cache re-sealed under the unlock code')
else
Log('Re-seal under code refused: ' + LastStoreError);
except
on E: Exception do Log('Re-seal under code failed: ' + E.Message);
end;
end
else if (not VaultNeedsAuth) and (not VaultNeedsPin) and BiometricAvailable then
// PLUS RIEN NE POSE NI NE CHANGE LE CODE ICI.
//
// Deux branches le faisaient, a partir de ce qui trainait dans les champs :
// l'une appliquait un "nouveau code" depose par les reglages, l'autre
// rescellait sous le code tape des que le cache ne l'etait plus. La seconde
// DEFAISAIT LE RETRAIT : on retirait le code, le champ le portait encore,
// et l'ouverture suivante le reposait sans qu'un mot le dise. La premiere
// ouvrait pire - n'importe qui pouvait deposer un nouveau code depuis les
// reglages sans connaitre l'actuel, et c'est le proprietaire qui, en
// ouvrant son coffre, l'appliquait contre lui-meme.
//
// Poser, changer et retirer passent desormais par SetUnlockCode et
// ClearUnlockCode : un geste explicite, coffre deja ouvert, effet immediat.
// Le champ de l'accueil ne veut plus dire qu'une chose : "ouvre avec ceci".
if (not VaultNeedsAuth) and (not VaultNeedsPin) and BiometricAvailable then
begin
try
// Cle NEUVE, obligatoirement : le changement qui vient de ramener la
@@ -2751,6 +2739,53 @@ begin
end).Start;
end;
/// POSER OU CHANGER LE CODE, IMMEDIATEMENT ET SUR DEMANDE.
///
/// Le coffre doit etre ouvert : c'est la seule preuve que le code courant est
/// connu, et la seule facon de tenir le clair qu'il faut resceller. Sans cette
/// exigence, quiconque tenait le telephone deverrouille pouvait deposer un
/// code depuis les reglages - le proprietaire l'appliquait ensuite lui-meme en
/// ouvrant son coffre, et se retrouvait dehors.
procedure TForm8.SetUnlockCode(const ANew: string);
begin
if FBusy then Exit;
if ANew = '' then
begin
ClearUnlockCode;
Exit;
end;
if (FPlain = '') or (FSyncPwd = '') then
begin
WebCall('PM.toast("Open the vault first")');
Exit;
end;
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,
FKey, FKeySalt, FKeyIters);
if not VaultNeedsPin then
begin
Log('Unlock code not set: ' + LastStoreError);
WebCall('PM.toast("Could not set the unlock code")');
Exit;
end;
FPinSealed := ANew;
// Le champ de l'accueil est vide : il sert a OUVRIR, et le coffre l'est
// deja. Le laisser rempli rescellerait sous lui a la prochaine ecriture.
FPin := '';
Log('Unlock code set');
WebCall('PM.toast("Unlock code set")');
WebCall('PM.pinChanged()');
except
on E: Exception do
begin
Log('Unlock code not set: ' + E.ClassName + ' - ' + E.Message);
WebCall('PM.toast("Could not set the unlock code")');
end;
end;
end;
procedure TForm8.ClearUnlockCode;
begin
if FBusy then Exit;
@@ -2775,7 +2810,6 @@ begin
end;
FPinSealed := '';
FPin := '';
FPinNew := '';
Log('Unlock code removed');
WebCall('PM.toast("Unlock code removed")');
WebCall('PM.pinChanged()');