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:
@@ -186,6 +186,19 @@ procedure SetPendingFlag(AOn: Boolean);
|
||||
/// Chemin du fichier, pour le journal et les tests.
|
||||
function VaultPath: string;
|
||||
|
||||
/// COPIE DE SECOURS du cache quand il est scelle par le verrouillage d'ecran.
|
||||
///
|
||||
/// Retirer le schema (ou le reinitialiser) fait DETRUIRE par Android la cle qui
|
||||
/// scelle vault.bin : le coffre devenait illisible et il fallait le
|
||||
/// retelecharger. SaveVault ecrit donc a cote une seconde copie scellee sous le
|
||||
/// mot de passe (meme format que le code d'app, VER_PIN : cle de l'appareil
|
||||
/// sans contrainte + PBKDF2 du mot de passe), que rien dans Android n'invalide.
|
||||
/// Elle n'existe QUE pour un cache VER_WRAP ; tout autre scellement l'efface.
|
||||
function HasRecoveryVault: Boolean;
|
||||
function LoadRecoveryVault(out APlainJson, AETag, ASyncPwd: string;
|
||||
out AKey, ASalt: TBytes; out AIterations: Integer;
|
||||
const APwd: string): Boolean;
|
||||
|
||||
/// Reglages du serveur : URL et jeton Bearer.
|
||||
///
|
||||
/// FICHIER SEPARE, ET PROTECTION PLUS FAIBLE, DELIBEREMENT. Ils sont chiffres
|
||||
@@ -218,6 +231,7 @@ const
|
||||
LOCAL_FILE = 'vault-local.json';
|
||||
PENDING_FILE = 'pending.flag';
|
||||
SETTINGS_FILE = 'settings.bin';
|
||||
RECOVER_FILE = 'vault-recover.bin';
|
||||
// Un alias par schema : la contrainte d'authentification est figee a la
|
||||
// creation de la cle et ne peut pas etre ajoutee apres coup.
|
||||
KEY_ALIAS = 'pm_vault_key'; // AES, sans authentification
|
||||
@@ -298,12 +312,18 @@ begin
|
||||
TFile.Delete(LocalContainerPath);
|
||||
end;
|
||||
|
||||
function RecoverPath: string;
|
||||
begin
|
||||
Result := TPath.Combine(TPath.GetHomePath, RECOVER_FILE);
|
||||
end;
|
||||
|
||||
procedure DropLocalVault;
|
||||
begin
|
||||
// Chacun sous son propre try : un fichier absent ou verrouille ne doit pas
|
||||
// empecher les deux autres de partir - un effacement a moitie fait laisse
|
||||
// l'appareil dans un etat que personne n'a voulu.
|
||||
try if TFile.Exists(VaultPath) then TFile.Delete(VaultPath); except end;
|
||||
try if TFile.Exists(RecoverPath) then TFile.Delete(RecoverPath); except end;
|
||||
try DropLocalContainer; except end;
|
||||
try SetPendingFlag(False); except end;
|
||||
end;
|
||||
@@ -335,6 +355,11 @@ begin
|
||||
Result := TPath.Combine(TPath.GetHomePath, VAULT_FILE);
|
||||
end;
|
||||
|
||||
function HasRecoveryVault: Boolean;
|
||||
begin
|
||||
Result := TFile.Exists(RecoverPath);
|
||||
end;
|
||||
|
||||
function HasVault: Boolean;
|
||||
begin
|
||||
Result := TFile.Exists(VaultPath);
|
||||
@@ -344,6 +369,8 @@ procedure ClearVault;
|
||||
begin
|
||||
if TFile.Exists(VaultPath) then
|
||||
TFile.Delete(VaultPath);
|
||||
if TFile.Exists(RecoverPath) then
|
||||
TFile.Delete(RecoverPath);
|
||||
end;
|
||||
|
||||
{$IF DEFINED(ANDROID)}
|
||||
@@ -919,7 +946,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure SaveVault(const APlainJson, AETag, ASyncPwd: string;
|
||||
procedure SaveVaultAt(const APath, APlainJson, AETag, ASyncPwd: string;
|
||||
ARequireAuth, AFreshKey: Boolean; const APin: string;
|
||||
const AKey, ASalt: TBytes; AIterations: Integer; AClearPin: Boolean);
|
||||
{$IF DEFINED(ANDROID)}
|
||||
@@ -1010,7 +1037,7 @@ begin
|
||||
LOut[0] := VER_PIN;
|
||||
Move(LIv[0], LOut[1], Length(LIv));
|
||||
Move(LCt[0], LOut[1 + Length(LIv)], Length(LCt));
|
||||
TFile.WriteAllBytes(VaultPath, LOut);
|
||||
TFile.WriteAllBytes(APath, LOut);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
@@ -1087,7 +1114,7 @@ begin
|
||||
Move(LCt[0], LOut[1 + Length(LIv)], Length(LCt));
|
||||
end;
|
||||
|
||||
TFile.WriteAllBytes(VaultPath, LOut);
|
||||
TFile.WriteAllBytes(APath, LOut);
|
||||
end;
|
||||
{$ELSE}
|
||||
begin
|
||||
@@ -1095,9 +1122,30 @@ begin
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
function LoadVault(out APlainJson, AETag, ASyncPwd: string;
|
||||
procedure SaveVault(const APlainJson, AETag, ASyncPwd: string;
|
||||
ARequireAuth, AFreshKey: Boolean; const APin: string;
|
||||
const AKey, ASalt: TBytes; AIterations: Integer; AClearPin: Boolean);
|
||||
begin
|
||||
SaveVaultAt(VaultPath, APlainJson, AETag, ASyncPwd, ARequireAuth, AFreshKey,
|
||||
APin, AKey, ASalt, AIterations, AClearPin);
|
||||
// Suit le scellement REEL (lu dans l'en-tete), pas celui demande : un
|
||||
// appareil sans verrouillage retombe sur VER_PLAIN, et une ecriture refusee
|
||||
// laisse l'ancien en place.
|
||||
try
|
||||
if VaultNeedsAuth and (ASyncPwd <> '') then
|
||||
SaveVaultAt(RecoverPath, APlainJson, AETag, ASyncPwd, False, False,
|
||||
ASyncPwd, AKey, ASalt, AIterations, False)
|
||||
else if TFile.Exists(RecoverPath) then
|
||||
TFile.Delete(RecoverPath);
|
||||
except
|
||||
on E: Exception do
|
||||
FLastStoreError := 'recovery copy not written: ' + E.Message;
|
||||
end;
|
||||
end;
|
||||
|
||||
function LoadVaultAt(const APath: string; out APlainJson, AETag, ASyncPwd: string;
|
||||
out AKey, ASalt: TBytes; out AIterations: Integer;
|
||||
const APin: string = ''): Boolean;
|
||||
const APin: string): Boolean;
|
||||
{$IF DEFINED(ANDROID)}
|
||||
var
|
||||
LRaw, LIv, LCt, LPlain, LWrapped, LAesRaw, LInner, LSalt, LPinKey: TBytes;
|
||||
@@ -1114,10 +1162,10 @@ begin
|
||||
ASalt := nil;
|
||||
AIterations := 0;
|
||||
FLastStoreError := '';
|
||||
if not TFile.Exists(VaultPath) then Exit;
|
||||
if not TFile.Exists(APath) then Exit;
|
||||
|
||||
try
|
||||
LRaw := TFile.ReadAllBytes(VaultPath);
|
||||
LRaw := TFile.ReadAllBytes(APath);
|
||||
if Length(LRaw) < 1 then Exit;
|
||||
|
||||
if LRaw[0] = VER_PIN then
|
||||
@@ -1268,4 +1316,21 @@ begin
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
function LoadVault(out APlainJson, AETag, ASyncPwd: string;
|
||||
out AKey, ASalt: TBytes; out AIterations: Integer;
|
||||
const APin: string = ''): Boolean;
|
||||
begin
|
||||
Result := LoadVaultAt(VaultPath, APlainJson, AETag, ASyncPwd, AKey, ASalt,
|
||||
AIterations, APin);
|
||||
end;
|
||||
|
||||
function LoadRecoveryVault(out APlainJson, AETag, ASyncPwd: string;
|
||||
out AKey, ASalt: TBytes; out AIterations: Integer;
|
||||
const APwd: string): Boolean;
|
||||
begin
|
||||
Result := (APwd <> '') and
|
||||
LoadVaultAt(RecoverPath, APlainJson, AETag, ASyncPwd, AKey, ASalt,
|
||||
AIterations, APwd);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user