diff --git a/android-client/PMA.Store.pas b/android-client/PMA.Store.pas index 7948158..20154c5 100644 --- a/android-client/PMA.Store.pas +++ b/android-client/PMA.Store.pas @@ -408,6 +408,7 @@ type function getKey(alias: JString; password: TJavaArray): JKeyX; cdecl; function getCertificate(alias: JString): JCertificateX; cdecl; function containsAlias(alias: JString): Boolean; cdecl; + procedure deleteEntry(alias: JString); cdecl; end; TJKeyStoreX = class(TJavaGenericImport) end; @@ -552,7 +553,7 @@ end; /// Cree la paire RSA si elle manque. Leve si l'appareil n'a NI empreinte NI /// code : une cle privee protegee par authentification n'aurait alors rien /// pour la deverrouiller. L'appelant retombe sur la cle AES. -procedure EnsureWrapKey; +procedure EnsureWrapKey(AForceNew: Boolean = False); var LStore: JKeyStoreX; LGen: JKeyPairGeneratorX; @@ -560,7 +561,15 @@ var LPaddings, LDigests: TJavaObjectArray; begin LStore := OpenKeyStore; - if LStore.containsAlias(StringToJString(KEY_ALIAS_WRAP)) then Exit; + // AForceNew : la cle existe mais ne fonctionne plus. Retirer le + // verrouillage d'ecran, ou enroler une nouvelle empreinte, invalide + // DEFINITIVEMENT une cle protegee - or l'alias, lui, subsiste. Sans cette + // suppression on la reutiliserait indefiniment, chaque chiffrement + // echouerait, et on retomberait pour toujours sur la cle simple. + if AForceNew and LStore.containsAlias(StringToJString(KEY_ALIAS_WRAP)) then + LStore.deleteEntry(StringToJString(KEY_ALIAS_WRAP)) + else if LStore.containsAlias(StringToJString(KEY_ALIAS_WRAP)) then + Exit; LPaddings := TJavaObjectArray.Create(1); LPaddings.Items[0] := StringToJString('OAEPPadding'); @@ -730,7 +739,7 @@ var LPlain, LIv, LCt, LOut, LAesRaw, LWrapped: TBytes; LCipher: JCipherX; LGotAuth: Boolean; - LOfs: Integer; + LOfs, LAttempt: Integer; begin // ETag, mot de passe et coffre chiffres ENSEMBLE : ils doivent rester // coherents. Un ETag qui survivrait a un coffre illisible ferait repondre @@ -748,21 +757,30 @@ begin LGotAuth := False; LWrapped := nil; if ARequireAuth then - try - EnsureWrapKey; - // Cle AES jetable, tiree du generateur cryptographique de la plateforme - // - jamais de Random ici. - LAesRaw := RandomBytes(AES_LEN); - LCipher := TJCipherX.JavaClass.getInstance(StringToJString(RSA_TRANSFORM)); - // AUCUNE authentification requise : la cle publique reconstruite ne - // porte plus les contraintes de la privee. C'est tout l'interet. - LCipher.init(TJCipherX.JavaClass.ENCRYPT_MODE, UnrestrictedPublicKey, - OaepParams); - LWrapped := FromJava(LCipher.doFinal(ToJava(LAesRaw))); - LGotAuth := True; - except - LWrapped := nil; // appareil sans protection, ou refus du Keystore - end; + begin + // Deux essais : le second apres avoir jete une cle devenue inutilisable. + for LAttempt := 0 to 1 do + try + EnsureWrapKey(LAttempt = 1); + // Cle AES jetable, tiree du generateur cryptographique de la + // plateforme - jamais de Random ici. + LAesRaw := RandomBytes(AES_LEN); + LCipher := TJCipherX.JavaClass.getInstance(StringToJString(RSA_TRANSFORM)); + // AUCUNE authentification requise : la cle publique reconstruite ne + // porte plus les contraintes de la privee. C'est tout l'interet. + LCipher.init(TJCipherX.JavaClass.ENCRYPT_MODE, UnrestrictedPublicKey, + OaepParams); + LWrapped := FromJava(LCipher.doFinal(ToJava(LAesRaw))); + LGotAuth := True; + Break; + except + on E: Exception do + begin + LWrapped := nil; // appareil sans protection, ou cle morte + FLastStoreError := E.ClassName + ': ' + E.Message; + end; + end; + end; if LGotAuth then begin diff --git a/mainApk.pas b/mainApk.pas index c1fc5e2..1fbb169 100644 --- a/mainApk.pas +++ b/mainApk.pas @@ -834,6 +834,25 @@ begin begin FETag := LETag; if LPwd <> '' then FSyncPwd := LPwd; + + // LA PROTECTION RATTRAPE LE CACHE. Elle se decidait a l'ecriture seule, + // et une ecriture n'arrive qu'avec un instantane nouveau : un cache pose + // pendant que l'appareil n'avait pas de verrouillage restait en clair + // indefiniment, meme apres en avoir remis un. On le rescelle ici, ou l'on + // tient justement le clair. + if (not VaultNeedsAuth) and BiometricAvailable then + begin + try + SaveVault(LPlain, LETag, LPwd, True); + if VaultNeedsAuth then + Log('Cache re-sealed under authentication') + else + Log('Re-seal refused: ' + LastStoreError); + except + on E: Exception do Log('Re-seal failed: ' + E.Message); + end; + end; + try AdoptVault(LPlain); // Afficher d'abord, synchroniser ensuite : le coffre est a l'ecran, le