diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5a4cc48..16cf27c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ = Build.VERSION_CODES.P) + PackageManager.GET_SIGNING_CERTIFICATES else @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES + val downloaded = pm.getPackageArchiveInfo(apkFile.absolutePath, flags) + if (downloaded == null) { + Log.e(TAG, "Could not parse downloaded APK") + return false + } + if (downloaded.packageName != context.packageName) { + Log.e(TAG, "APK package mismatch: ${downloaded.packageName} != ${context.packageName}") + return false + } + val installed = pm.getPackageInfo(context.packageName, flags) + val downloadedSigs = signingCertHashes(downloaded) + val installedSigs = signingCertHashes(installed) + if (downloadedSigs.isEmpty() || installedSigs.isEmpty()) { + Log.e(TAG, "Missing signing certificates (downloaded=${downloadedSigs.size}, installed=${installedSigs.size})") + return false + } + // Share at least one current signing certificate. + val match = downloadedSigs.any { it in installedSigs } + if (!match) Log.e(TAG, "APK signing certificate does not match installed app") + match + } catch (e: Exception) { + Log.e(TAG, "Signature verification error: ${e.message}", e) + false + } + } + + private fun signingCertHashes(info: PackageInfo): Set { + val sigs: Array? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + info.signingInfo?.apkContentsSigners + } else { + @Suppress("DEPRECATION") info.signatures + } + return sigs?.mapNotNull { sha256(it.toByteArray()) }?.toSet() ?: emptySet() + } + + private fun sha256(bytes: ByteArray): String? { + return try { + MessageDigest.getInstance("SHA-256").digest(bytes).joinToString("") { "%02x".format(it) } + } catch (e: Exception) { + null + } + } + private fun getAppVersion(): String { return try { context.packageManager.getPackageInfo(context.packageName, 0).versionName ?: "1.0.0"