@@ -72,52 +72,49 @@ object ApkSignatureVerifier {
7272 archive.isNotEmpty() && installed.isNotEmpty() && archive == installed
7373
7474 /* *
75- * Vrai si [apk] est signé exactement par la même clé que l'app installée.
76- * Toute erreur (archive corrompue, API indisponible) renvoie false.
75+ * Vrai si [apk] est signé exactement par la même clé que l'app installée — et journalise le
76+ * verdict dans les deux cas. Toute erreur (archive illisible, API indisponible) renvoie false.
77+ *
78+ * Une seule fonction fait la mesure ET la décision : les avoir séparées ferait analyser
79+ * l'archive et calculer son sha256 deux fois par installation, pour un résultat identique.
80+ *
81+ * ⚠️ Le verdict ne repose QUE sur les empreintes de certificats. Ni la taille ni le sha256
82+ * n'y participent — ils ne servent qu'au log, pour se confronter aux lignes publiées par la
83+ * CI. Un APK légitime change forcément de poids d'une version à l'autre.
7784 */
7885 fun matchesRunningApp (context : Context , apk : File ): Boolean {
86+ val taille = runCatching { apk.length() }.getOrDefault(- 1L )
87+ val empreinte = sha256(apk)
7988 val archive = fingerprintsOfArchive(context, apk)
8089 val installed = fingerprintsOfInstalled(context)
8190 val ok = certsMatch(archive, installed)
82- if (! ok) {
83- AppLogger .w(TAG , " Signature de l'APK non conforme — installation refusée " +
84- " (archive=${archive.size} cert(s), installée=${installed.size} cert(s))" )
85- // Les compteurs seuls ne permettent pas de trancher entre les trois causes réelles :
86- // archive illisible (0 cert — typiquement une signature v2 sans v1, que
87- // getPackageArchiveInfo ne remonte pas sur AAOS 9), clé différente, ou lecture de
88- // notre propre signature en échec. Les empreintes le disent en une ligne.
91+ if (ok) {
92+ AppLogger .i(TAG , " Signature CONFORME — ${apk.name} , $taille octets, sha256=$empreinte " )
93+ } else {
94+ AppLogger .w(TAG , " Signature NON CONFORME — installation refusée : ${apk.name} , " +
95+ " $taille octets, sha256=$empreinte , " +
96+ " archive=${archive.size} cert(s), installee=${installed.size} cert(s)" )
97+ // Les compteurs seuls ne distinguent pas les trois causes réelles : archive illisible,
98+ // clé réellement différente, ou lecture de notre propre signature en échec.
8999 AppLogger .w(TAG , " archive : ${describe(archive)} " )
90- AppLogger .w(TAG , " installée : ${describe(installed)} " )
100+ AppLogger .w(TAG , " installee : ${describe(installed)} " )
101+ if (archive.isEmpty()) AppLogger .w(TAG , " archive=0 cert : archive illisible " +
102+ " (APK non signé, ou v1 absente) — ce n'est PAS la preuve d'une clé différente" )
91103 }
92104 return ok
93105 }
94106
95- /* *
96- * Journalise le verdict de signature SANS rien bloquer.
97- *
98- * Le contrôle bloquant a été désactivé à la demande du propriétaire (voir [ApkInstaller]),
99- * mais garder la MESURE permet de trancher, log en main, entre « signature réellement
100- * différente » et « archive illisible parce que le téléchargement a été coupé ». Les deux
101- * produisaient le même message auparavant, et c'est précisément ce qui a fait chercher une
102- * panne de clé là où le réseau était en cause.
103- *
104- * Ne renvoie volontairement RIEN : le résultat ne doit pas pouvoir être réutilisé comme
105- * condition, sans quoi le contrôle redeviendrait bloquant par inadvertance.
106- */
107- fun logVerdict (context : Context , apk : File ) {
108- val taille = runCatching { apk.length() }.getOrDefault(- 1L )
109- val archive = fingerprintsOfArchive(context, apk)
110- val installed = fingerprintsOfInstalled(context)
111- if (certsMatch(archive, installed)) {
112- AppLogger .i(TAG , " [AUDIT] Signature CONFORME (non bloquant) — ${apk.name} , $taille octets" )
113- } else {
114- AppLogger .w(TAG , " [AUDIT] Signature NON CONFORME (non bloquant, installation poursuivie) — " +
115- " ${apk.name} , $taille octets, archive=${archive.size} cert(s), installee=${installed.size} cert(s)" )
116- AppLogger .w(TAG , " [AUDIT] archive : ${describe(archive)} " )
117- AppLogger .w(TAG , " [AUDIT] installee : ${describe(installed)} " )
118- if (archive.isEmpty()) AppLogger .w(TAG , " [AUDIT] archive=0 cert : archive illisible " +
119- " (telechargement tronque, ou v1 absente) — ce n'est PAS la preuve d'une cle differente" )
107+ /* * Empreinte du fichier reçu, à confronter à celle publiée par la CI. */
108+ private fun sha256 (f : File ): String = try {
109+ val md = MessageDigest .getInstance(" SHA-256" )
110+ f.inputStream().use { input ->
111+ val buf = ByteArray (8_192 )
112+ var read: Int
113+ while (input.read(buf).also { read = it } != - 1 ) md.update(buf, 0 , read)
120114 }
115+ md.digest().joinToString(" " ) { " %02x" .format(it) }
116+ } catch (e: Exception ) {
117+ " (illisible: ${e.message} )"
121118 }
122119
123120 /* * Empreintes tronquées, suffisantes pour identifier une clé dans un log. */
0 commit comments