feat(android): encryption, verified by a third implementation
Section 4 step 2. EncryptContainer produces the exact container the desktop writes: PBKDF2-SHA256, AES-256-GCM with the tag appended as WebCrypto does, a 32-byte salt and a 12-byte IV drawn per call from SecureRandom on Android and BCryptGenRandom on Windows - never Random, since a predictable salt or IV ruins the encryption that uses it and nothing in the output would show it. The check matters as much as the code. A round trip with itself only proves internal consistency, and it passed while the container carried two defects. Node - the same primitives as WebCrypto - found both by reading a container the phone had produced: - Delphi's base64 wraps at 76 characters; WebCrypto's never does. Lenient decoders accept it, but depending on their leniency is not a guarantee. - the accented literal in the check came out of the COMPILER already broken: this source has no byte-order mark, so Delphi reads it as ANSI. The defect was in the test rather than the encryption, and only the cross-check could tell those apart. The check runs at launch in DEBUG and leaves selfcheck.json in the private directory so it can be re-read from the PC. Also fixed on the way: TPath resolves to FMX.Objects.TPath - a drawing shape - because FMX.Objects comes later in the uses clause, and the error then talks about an unknown Combine rather than a name collision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+61
-1
@@ -32,7 +32,7 @@ interface
|
||||
uses
|
||||
System.SysUtils, System.Types, System.UITypes, System.Classes,
|
||||
System.Diagnostics, System.Rtti, System.NetEncoding, System.DateUtils,
|
||||
System.Generics.Collections,
|
||||
System.Generics.Collections, System.IOUtils,
|
||||
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Platform,
|
||||
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Memo, FMX.Memo.Types,
|
||||
FMX.ScrollBox, FMX.Layouts, FMX.Edit, FMX.ListBox, FMX.Objects,
|
||||
@@ -185,6 +185,9 @@ type
|
||||
procedure WebCall(const AJavaScript: string);
|
||||
procedure PushVault;
|
||||
procedure PushConfig;
|
||||
{$IFDEF DEBUG}
|
||||
procedure CryptoSelfCheck;
|
||||
{$ENDIF}
|
||||
procedure OpenEntry(AVaultIndex: Integer);
|
||||
procedure OnClipTimer(Sender: TObject);
|
||||
procedure OnDeadline(Sender: TObject);
|
||||
@@ -286,6 +289,15 @@ begin
|
||||
PushConfig;
|
||||
ShowUnlock;
|
||||
|
||||
{$IFDEF DEBUG}
|
||||
// VERIFICATION DU CHIFFREMENT AVANT TOUTE ECRITURE REELLE (§4). Elle depose
|
||||
// aussi un conteneur sur le disque prive, pour qu'il soit relu par l'AUTRE
|
||||
// implementation : un aller-retour avec soi-meme ne prouve que la coherence
|
||||
// interne, et c'est la compatibilite avec le poste qui decide si une
|
||||
// publication depuis le telephone est lisible ou perdue.
|
||||
TThread.ForceQueue(nil, procedure begin CryptoSelfCheck; end);
|
||||
{$ENDIF}
|
||||
|
||||
// Ouverture automatique du coffre local. Differee apres la construction :
|
||||
// l'invite biometrique s'afficherait sinon avant la premiere peinture de la
|
||||
// fenetre, sur un fond vide.
|
||||
@@ -404,6 +416,54 @@ begin
|
||||
SetLength(FWebPending, 0);
|
||||
end;
|
||||
|
||||
/// Chiffre un petit conteneur, le relit, et le laisse sur le disque prive pour
|
||||
/// qu'une autre implementation le verifie. Iterations volontairement basses :
|
||||
/// ce qu'on eprouve ici est le CABLAGE - cle, IV, place du tag - pas le cout
|
||||
/// de la derivation, qui a son propre vecteur de test.
|
||||
procedure TForm8.CryptoSelfCheck;
|
||||
const
|
||||
// Points de code : ce fichier n'a pas de BOM, un accent ecrit tel quel y
|
||||
// devient deux glyphes et la cle derivee ne serait plus celle qu'on croit.
|
||||
PWD = 'selfcheck-password-'#$00E9#$00E0#$00FC;
|
||||
ITERS = 1000;
|
||||
var
|
||||
LPlain, LContainer, LBack, LPath: string;
|
||||
LSw: TStopwatch;
|
||||
begin
|
||||
try
|
||||
// Accents EN POINTS DE CODE, comme le mot de passe : le controle croise a
|
||||
// montre que ce litteral-ci sortait deja abime du compilateur, et un
|
||||
// aller-retour avec soi-meme ne pouvait pas le voir.
|
||||
LPlain := '{"version":1,"entries":[{"uuid":"u1","title":"accent '#$00E9#$00E0'"}]}';
|
||||
LSw := TStopwatch.StartNew;
|
||||
LContainer := EncryptContainer(LPlain, PWD, ITERS);
|
||||
LBack := DecryptContainer(LContainer, PWD);
|
||||
LSw.Stop;
|
||||
if LBack = LPlain then
|
||||
Log(Format('selfcheck: encrypt/decrypt round-trip OK (%d ms)',
|
||||
[LSw.ElapsedMilliseconds]))
|
||||
else
|
||||
Log('selfcheck: ROUND-TRIP MISMATCH - encryption is not usable');
|
||||
|
||||
// Depose pour relecture externe. Le mot de passe et les iterations sont
|
||||
// dans le commentaire de tete du fichier de test cote PC, pas ici.
|
||||
// GetHomePath, comme PMA.Store : sur Android c'est /data/data/<paquet>/
|
||||
// files, prive a l'application. Documents n'existe pas ici.
|
||||
//
|
||||
// QUALIFIE : FMX.Objects declare aussi un TPath - une forme graphique - et
|
||||
// il vient apres System.IOUtils dans la clause uses, donc c'est lui que le
|
||||
// compilateur choisit. Le message parle alors d'un « Combine » inconnu, ce
|
||||
// qui n'oriente pas vers un conflit de noms.
|
||||
LPath := System.IOUtils.TPath.Combine(
|
||||
System.IOUtils.TPath.GetHomePath, 'selfcheck.json');
|
||||
TFile.WriteAllText(LPath, LContainer, TEncoding.UTF8);
|
||||
Log('selfcheck: container written for cross-check');
|
||||
except
|
||||
on E: Exception do
|
||||
Log('selfcheck FAILED: ' + E.ClassName + ' - ' + E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
/// Descend les coordonnees du serveur dans les champs de la page.
|
||||
procedure TForm8.PushConfig;
|
||||
var
|
||||
|
||||
Reference in New Issue
Block a user