1+ package com.aheaditec.freerasp
2+
3+ import android.content.Context
4+ import android.graphics.Bitmap
5+ import android.graphics.Canvas
6+ import android.graphics.drawable.BitmapDrawable
7+ import android.graphics.drawable.Drawable
8+ import android.os.Build
9+ import android.util.Base64
10+ import com.aheaditec.talsec_security.security.api.TalsecConfig
11+ import org.json.JSONArray
12+ import org.json.JSONException
13+ import org.json.JSONObject
14+ import java.io.ByteArrayOutputStream
15+
16+ internal object Utils {
17+ @Suppress(" ArrayInDataClass" )
18+ data class MalwareConfig (
19+ val blacklistedPackageNames : Array <String >,
20+ val blacklistedHashes : Array <String >,
21+ val suspiciousPermissions : Array <Array <String >>,
22+ val whitelistedInstallationSources : Array <String >
23+ )
24+
25+ fun toTalsecConfigThrowing (configJson : String? ): TalsecConfig {
26+ if (configJson == null ) {
27+ throw JSONException (" Configuration is null" )
28+ }
29+
30+ val json = JSONObject (configJson)
31+
32+ val watcherMail = json.getString(" watcherMail" )
33+ val isProd = json.getBoolean(" isProd" )
34+ val androidConfig = json.getJSONObject(" androidConfig" )
35+ val packageName = androidConfig.getString(" packageName" )
36+ val certificateHashes = androidConfig.extractArray<String >(" signingCertHashes" )
37+ val alternativeStores = androidConfig.extractArray<String >(" supportedStores" )
38+ val malwareConfig = parseMalwareConfig(androidConfig)
39+
40+ return TalsecConfig .Builder (packageName, certificateHashes)
41+ .watcherMail(watcherMail)
42+ .supportedAlternativeStores(alternativeStores)
43+ .prod(isProd)
44+ .blacklistedPackageNames(malwareConfig.blacklistedPackageNames)
45+ .blacklistedHashes(malwareConfig.blacklistedHashes)
46+ .suspiciousPermissions(malwareConfig.suspiciousPermissions)
47+ .whitelistedInstallationSources(malwareConfig.whitelistedInstallationSources)
48+ .build()
49+ }
50+
51+ private fun parseMalwareConfig (androidConfig : JSONObject ): MalwareConfig {
52+ if (! androidConfig.has(" malwareConfig" )) {
53+ return MalwareConfig (emptyArray(), emptyArray(), emptyArray(), emptyArray())
54+ }
55+
56+ val malwareConfig = androidConfig.getJSONObject(" malwareConfig" )
57+
58+ return MalwareConfig (
59+ malwareConfig.extractArray(" blacklistedPackageNames" ),
60+ malwareConfig.extractArray(" blacklistedHashes" ),
61+ malwareConfig.extractArray<Array <String >>(" suspiciousPermissions" ),
62+ malwareConfig.extractArray(" whitelistedInstallationSources" )
63+ )
64+ }
65+
66+
67+ /* *
68+ * Retrieves the package name of the installer for a given app package.
69+ *
70+ * @param context The context of the application.
71+ * @param packageName The package name of the app whose installer package name is to be retrieved.
72+ * @return The package name of the installer if available, or `null` if not.
73+ */
74+ fun getInstallerPackageName (context : Context , packageName : String ): String? {
75+ runCatching {
76+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R )
77+ return context.packageManager.getInstallSourceInfo(packageName).installingPackageName
78+ @Suppress(" DEPRECATION" )
79+ return context.packageManager.getInstallerPackageName(packageName)
80+ }
81+ return null
82+ }
83+
84+ /* *
85+ * Converts the application icon of the specified package into a Base64 encoded string.
86+ *
87+ * @param context The context of the application.
88+ * @param packageName The package name of the app whose icon is to be converted.
89+ * @return A Base64 encoded string representing the app icon.
90+ */
91+ fun parseIconBase64 (context : Context , packageName : String ): String? {
92+ val result = runCatching {
93+ val drawable = context.packageManager.getApplicationIcon(packageName)
94+ val bitmap = drawable.toBitmap()
95+ bitmap.toBase64()
96+ }
97+
98+ return result.getOrNull()
99+ }
100+
101+ /* *
102+ * Creates a Bitmap from a Drawable object.
103+ *
104+ * @param drawable The Drawable to be converted.
105+ * @return A Bitmap representing the drawable.
106+ */
107+ private fun createBitmapFromDrawable (drawable : Drawable ): Bitmap {
108+ val width = if (drawable.intrinsicWidth > 0 ) drawable.intrinsicWidth else 1
109+ val height = if (drawable.intrinsicHeight > 0 ) drawable.intrinsicHeight else 1
110+ val bitmap = Bitmap .createBitmap(width, height, Bitmap .Config .ARGB_8888 )
111+ val canvas = Canvas (bitmap)
112+
113+ drawable.setBounds(0 , 0 , canvas.width, canvas.height)
114+ drawable.draw(canvas)
115+
116+ return bitmap
117+ }
118+
119+ /* *
120+ * Converts a Drawable into a Bitmap.
121+ *
122+ * @receiver The Drawable to be converted.
123+ * @return A Bitmap representing the drawable.
124+ */
125+ private fun Drawable.toBitmap (): Bitmap {
126+ return when (this ) {
127+ is BitmapDrawable -> bitmap
128+ else -> createBitmapFromDrawable(this )
129+ }
130+ }
131+
132+ /* *
133+ * Converts a Bitmap into a Base64 encoded string.
134+ *
135+ * @receiver The Bitmap to be converted.
136+ * @return A Base64 encoded string representing the bitmap.
137+ */
138+ private fun Bitmap.toBase64 (): String {
139+ val byteArrayOutputStream = ByteArrayOutputStream ()
140+ compress(Bitmap .CompressFormat .PNG , 10 , byteArrayOutputStream)
141+ val byteArray = byteArrayOutputStream.toByteArray()
142+ return Base64 .encodeToString(byteArray, Base64 .NO_WRAP )
143+ }
144+ }
145+
146+ private inline fun <reified T > JSONObject.extractArray (key : String ): Array <T > {
147+ return this .optJSONArray(key)?.let { processArray(it) } ? : emptyArray()
148+ }
149+
150+ private inline fun <reified T > processArray (jsonArray : JSONArray ): Array <T > {
151+ val list = mutableListOf<T >()
152+
153+ for (i in 0 until jsonArray.length()) {
154+ val element: T = when (T ::class ) {
155+ String ::class -> jsonArray.getString(i) as T
156+ Int ::class -> jsonArray.getInt(i) as T
157+ Double ::class -> jsonArray.getDouble(i) as T
158+ Boolean ::class -> jsonArray.getBoolean(i) as T
159+ Long ::class -> jsonArray.getLong(i) as T
160+ Array <String >::class -> {
161+ // Not universal or ideal solution, but should work for our use case
162+ val nestedArray = jsonArray.getJSONArray(i)
163+ val nestedList = mutableListOf<String >()
164+ for (j in 0 until nestedArray.length()) {
165+ nestedList.add(nestedArray.getString(j))
166+ }
167+ nestedList.toTypedArray() as T
168+ }
169+
170+ else -> throw JSONException (" Unsupported type" )
171+ }
172+ list.add(element)
173+ }
174+
175+ return list.toTypedArray()
176+ }
0 commit comments