feat: the last three passwords travel, and the phone can show them
The desktop keeps twenty previous versions per entry and never sent any. It now sends three. Three, not twenty, because the trade is not symmetrical. On the desktop they sit encrypted column by column and never move. In the snapshot everything travels in the clear inside the container, and comes down into a phone cache sealed only by the device key - not by the master password. An old password stays sensitive: it is often still valid somewhere else. The real use of the list is short - "I changed it recently, what was it?" - and three cover it. The other seventeen would be copies with no need facing them. The phone reads them ON DEMAND from the raw snapshot, never into TVault: loading them with the vault would put every entry's old passwords in the clear in memory from the moment it opens, for something one looks at now and then. Copying goes by RANK, not by value. The page displays the text but never sends it back - that is the bridge's rule everywhere else, and an old password has no reason to be the exception just because it happens to be on screen. They leave with the plaintext at lock, on both sides.
This commit is contained in:
+75
@@ -154,6 +154,10 @@ type
|
||||
/// Elle part au verrouillage, avec lui.
|
||||
FKey, FKeySalt: TBytes;
|
||||
FKeyIters: Integer;
|
||||
/// LES ANCIENNES VERSIONS QUI VIENNENT D'ETRE MONTREES, pour que la copie
|
||||
/// se demande par un rang. La page en affiche le texte ; elle ne le renvoie
|
||||
/// jamais - c'est la regle du pont partout ailleurs.
|
||||
FHistory: TArray<string>;
|
||||
/// Des modifications sont rangees ici sans avoir atteint le depot.
|
||||
FPending: Boolean;
|
||||
/// Le depot porte quelque chose que nous n'avons pas.
|
||||
@@ -308,6 +312,8 @@ type
|
||||
procedure ClearUnlockCode;
|
||||
/// 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.
|
||||
procedure ShowPasswordHistory;
|
||||
procedure ReopenEntryByUuid(const AUuid: string);
|
||||
procedure OnClipTimer(Sender: TObject);
|
||||
procedure OnDeadline(Sender: TObject);
|
||||
@@ -536,6 +542,15 @@ begin
|
||||
end;
|
||||
// C'est la PAGE qui sait ce qu'elle affiche - Delphi ne le devine pas. Elle
|
||||
// le declare a chaque changement de vue (cmd://state).
|
||||
// L'HISTORIQUE SE REFERME D'ABORD, comme les reglages : un voile pose
|
||||
// par-dessus, et la touche retour le retire sans toucher a ce qu'il couvre.
|
||||
if FWebView = 'history' then
|
||||
begin
|
||||
WebCall('PM.closeHistory()');
|
||||
Key := 0;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
// LES REGLAGES SE REFERMENT D'ABORD. Ils sont une parenthese posee par-dessus
|
||||
// le reste : la touche retour la ferme, elle ne saute pas ce qu'il y a
|
||||
// derriere.
|
||||
@@ -1621,6 +1636,21 @@ begin
|
||||
TNetEncoding.URL.Decode(Param('k')),
|
||||
TNetEncoding.URL.Decode(Param('d')))
|
||||
|
||||
else if LAction = 'history' then
|
||||
ShowPasswordHistory
|
||||
|
||||
else if LAction = 'copyhist' then
|
||||
begin
|
||||
LIdx := StrToIntDef(Param('k'), -1);
|
||||
if (LIdx >= 0) and (LIdx <= High(FHistory)) then
|
||||
begin
|
||||
LValue := FHistory[LIdx];
|
||||
LIdx := Pos(#9, LValue);
|
||||
if LIdx > 0 then
|
||||
CopyToClipboard(Copy(LValue, LIdx + 1, MaxInt), 'Previous password');
|
||||
end;
|
||||
end
|
||||
|
||||
else if LAction = 'icon' then
|
||||
SetEntryIcon(Param('a') = 'fetch')
|
||||
|
||||
@@ -1706,6 +1736,7 @@ begin
|
||||
FKeySalt := nil;
|
||||
FKeyIters := 0;
|
||||
FPlain := '';
|
||||
FHistory := nil;
|
||||
// Le code aussi : le retenir apres un verrouillage reviendrait a ne pas
|
||||
// verrouiller. Le garde de SaveVault protege le cache d'ici la.
|
||||
FPinSealed := '';
|
||||
@@ -2560,6 +2591,50 @@ end;
|
||||
/// La recherche est hors du thread principal : cinq secondes d'attente
|
||||
/// figeraient l'ecran. L'ecriture, elle, repasse par CommitChange - donc par
|
||||
/// le meme chemin que tout le reste, avec sa publication et son hors-ligne.
|
||||
/// LES ANCIENS MOTS DE PASSE DE L'ENTREE OUVERTE.
|
||||
///
|
||||
/// Ils ne descendent vers la page qu'ici, sur demande, et pour une seule
|
||||
/// entree. La page les jette au verrouillage comme le reste du clair.
|
||||
procedure TForm8.ShowPasswordHistory;
|
||||
var
|
||||
LRows: TArray<string>;
|
||||
LArr: TJSONArray;
|
||||
LObj: TJSONObject;
|
||||
LTab: Integer;
|
||||
LLine: string;
|
||||
begin
|
||||
if FCurrent.Uuid = '' then Exit;
|
||||
if FPlain = '' then
|
||||
begin
|
||||
WebCall('PM.toast("Open the vault again to read the history")');
|
||||
Exit;
|
||||
end;
|
||||
LRows := PasswordHistory(FPlain, FCurrent.Uuid);
|
||||
FHistory := LRows;
|
||||
if Length(LRows) = 0 then
|
||||
begin
|
||||
// DIRE LAQUELLE DES DEUX. Une entree jamais modifiee n'a pas d'historique ;
|
||||
// un instantane publie par un poste plus ancien n'en porte pour aucune. Les
|
||||
// confondre ferait chercher une panne la ou il n'y a rien a trouver.
|
||||
WebCall('PM.toast("No previous password recorded for this entry")');
|
||||
Exit;
|
||||
end;
|
||||
LArr := TJSONArray.Create;
|
||||
try
|
||||
for LLine in LRows do
|
||||
begin
|
||||
LTab := Pos(#9, LLine);
|
||||
LObj := TJSONObject.Create;
|
||||
LObj.AddPair('at', Copy(LLine, 1, LTab - 1));
|
||||
LObj.AddPair('pwd', Copy(LLine, LTab + 1, MaxInt));
|
||||
LArr.AddElement(LObj);
|
||||
end;
|
||||
WebCall('PM.showHistory(' + JsArg(LArr.ToJSON) + ')');
|
||||
finally
|
||||
LArr.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TForm8.SetEntryIcon(AFetch: Boolean);
|
||||
var
|
||||
LUuid, LSite, LTitle: string;
|
||||
|
||||
Reference in New Issue
Block a user