feat(android): a local account, so the app serves without a repository

Until now the phone could only read a vault that came from elsewhere:
without a URL, a token and a sync password it showed nothing. It now
signs in like the desktop does - username and master password - and can
create its own vault, with no network at all.

The local container carries exactly what the repository would carry: same
format, same encryption under the password. Two reasons, not one. It is
the desktop's model, where the master password protects the vault at rest
rather than the hardware alone; and the format staying the repository's
means sync keeps working with nothing to convert. No Keystore on that
file - it is already encrypted under the password, and binding it to the
device would make it unrecoverable after a reinstall. vault.bin keeps
that role: it is the fast-open cache, and only it needs sealing.

The password opens the container - the decryption itself is the check, no
secret is ever compared - and the username, read from inside once open,
says whose vault it is.

The home screen now knows which of three situations it is in: nothing
here yet, a container without a cache, or a cache ready to open.

Verified on the device: with the cache set aside, the screen offers
username, master password and Create vault, alongside the download path.
Creating an account could not be exercised to the end - the device locked
its screen mid-run - and the vault that was set aside is back in place,
same size, still sealed under its code.
This commit is contained in:
r-zakarya
2026-08-21 14:07:39 +01:00
parent b1be802e22
commit f32cde81fc
4 changed files with 288 additions and 1 deletions
+40
View File
@@ -117,6 +117,23 @@ function LastStoreError: string;
/// certitude, devrait l'appeler : il se retelecharge, mais pas gratuitement.
procedure ClearVault;
/// LE CONTENEUR LOCAL - l'autorite quand l'appareil se sert seul.
///
/// Il porte EXACTEMENT ce que porterait le depot : meme format, meme
/// chiffrement sous mot de passe (`pm-encrypted-export-v1`, PBKDF2 600k). Deux
/// raisons, pas une. C'est le modele du poste - le mot de passe maitre protege
/// le coffre au repos, et pas seulement le materiel. Et le format restant celui
/// du depot, la synchronisation continue de marcher sans rien convertir.
///
/// PAS DE KEYSTORE ICI, volontairement : ce fichier est deja chiffre sous le
/// mot de passe, comme sa copie distante. Le lier en plus a l'appareil le
/// rendrait irrecuperable apres une reinstallation, alors que le mot de passe,
/// lui, se retient. `vault.bin` garde ce role - c'est le cache d'ouverture
/// rapide, et lui seul a besoin d'etre scelle.
function HasLocalContainer: Boolean;
function LoadLocalContainer: string;
procedure SaveLocalContainer(const AContainer: string);
/// Chemin du fichier, pour le journal et les tests.
function VaultPath: string;
@@ -147,6 +164,7 @@ uses
const
VAULT_FILE = 'vault.bin';
LOCAL_FILE = 'vault-local.json';
SETTINGS_FILE = 'settings.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.
@@ -194,6 +212,28 @@ begin
Result := FLastStoreError;
end;
function LocalContainerPath: string;
begin
Result := TPath.Combine(TPath.GetHomePath, LOCAL_FILE);
end;
function HasLocalContainer: Boolean;
begin
Result := TFile.Exists(LocalContainerPath);
end;
function LoadLocalContainer: string;
begin
Result := '';
if TFile.Exists(LocalContainerPath) then
Result := TFile.ReadAllText(LocalContainerPath, TEncoding.UTF8);
end;
procedure SaveLocalContainer(const AContainer: string);
begin
TFile.WriteAllText(LocalContainerPath, AContainer, TEncoding.UTF8);
end;
function VaultPath: string;
begin
// GetHomePath sur Android = /data/data/<paquet>/files, prive a l'app.