11package com.margelo.nitro.sensitiveinfo
2-
3- import android.content.Context
4- import androidx.biometric.BiometricManager
5- import androidx.biometric.BiometricPrompt
6- import androidx.core.content.ContextCompat
2+
73import com.facebook.proguard.annotations.DoNotStrip
8- import kotlinx.coroutines.suspendCancellableCoroutine
9- import java.util.concurrent.Executor
10- import kotlin.coroutines.resume
114
125@DoNotStrip
136class SensitiveInfo : HybridSensitiveInfoSpec () {
14-
15- private fun getMasterKey (context : Context ): androidx.security.crypto.MasterKey {
16- val builder = androidx.security.crypto.MasterKey .Builder (context)
17- .setKeyScheme(androidx.security.crypto.MasterKey .KeyScheme .AES256_GCM )
18- // Use StrongBox if available
19- if (android.os.Build .VERSION .SDK_INT >= android.os.Build .VERSION_CODES .P ) {
20- try {
21- builder.setUserAuthenticationRequired(false , 0 )
22- builder.setRequestStrongBoxBacked(true )
23- } catch (_: Exception ) {}
24- }
25- return builder.build()
26- }
27-
28- suspend fun setItem (
29- key : String ,
30- value : String ,
31- requireBiometric : Boolean ,
32- promptTitle : String? ,
33- promptSubtitle : String? ,
34- promptDescription : String? ,
35- promptNegativeButton : String? ,
36- promptReason : String?
37- ) {
38- val context = getReactApplicationContext()
39- val masterKey = getMasterKey(context)
40- val prefs = androidx.security.crypto.EncryptedSharedPreferences .create(
41- " sensitive_info_prefs" ,
42- " master_key_alias" ,
43- masterKey,
44- context,
45- androidx.security.crypto.EncryptedSharedPreferences .PrefKeyEncryptionScheme .AES256_SIV ,
46- androidx.security.crypto.EncryptedSharedPreferences .PrefValueEncryptionScheme .AES256_GCM
47- )
48- try {
49- if (requireBiometric) {
50- prefs.edit().putString(" ${key} _biometric" , " true" ).apply ()
51- }
52- prefs.edit().putString(key, value).apply ()
53- } catch (e: Exception ) {
54- throw Exception (" Keystore error" , e)
55- } finally {
56- value.toByteArray().fill(0 )
57- }
58- }
59-
60- suspend fun getItem (
61- key : String ,
62- requireBiometric : Boolean ,
63- promptTitle : String? ,
64- promptSubtitle : String? ,
65- promptDescription : String? ,
66- promptNegativeButton : String? ,
67- promptReason : String?
68- ): String? {
69- val context = getReactApplicationContext()
70- val masterKey = getMasterKey(context)
71- val prefs = androidx.security.crypto.EncryptedSharedPreferences .create(
72- " sensitive_info_prefs" ,
73- " master_key_alias" ,
74- masterKey,
75- context,
76- androidx.security.crypto.EncryptedSharedPreferences .PrefKeyEncryptionScheme .AES256_SIV ,
77- androidx.security.crypto.EncryptedSharedPreferences .PrefValueEncryptionScheme .AES256_GCM
78- )
79- try {
80- if (requireBiometric) {
81- val title = promptTitle ? : " Authenticate"
82- val subtitle = promptSubtitle
83- val description = promptDescription
84- val negativeButtonText = promptNegativeButton ? : " Cancel"
85-
86- val executor: Executor = ContextCompat .getMainExecutor(context)
87- val activity = getCurrentActivity() ? : throw Exception (" No current activity" )
88- return suspendCancellableCoroutine { continuation ->
89- val biometricPrompt = BiometricPrompt (
90- activity,
91- executor,
92- object : BiometricPrompt .AuthenticationCallback () {
93- override fun onAuthenticationSucceeded (result : BiometricPrompt .AuthenticationResult ) {
94- val value = prefs.getString(key, null )
95- value?.toByteArray()?.fill(0 )
96- continuation.resume(value)
97- }
98- override fun onAuthenticationError (errorCode : Int , errString : CharSequence ) {
99- continuation.resume(null )
100- }
101- override fun onAuthenticationFailed () {
102- // Do nothing, user can retry
103- }
104- }
105- )
106- val promptInfo = BiometricPrompt .PromptInfo .Builder ()
107- .setTitle(title)
108- .setNegativeButtonText(negativeButtonText)
109- .apply {
110- subtitle?.let { setSubtitle(it) }
111- description?.let { setDescription(it) }
112- }
113- .build()
114- biometricPrompt.authenticate(promptInfo)
115- }
116- } else {
117- val value = prefs.getString(key, null )
118- value?.toByteArray()?.fill(0 )
119- return value
120- }
121- } catch (e: Exception ) {
122- throw Exception (" Keystore error" , e)
123- }
124- }
125-
126- suspend fun deleteItem (key : String ) {
127- val context = getReactApplicationContext()
128- val masterKey = getMasterKey(context)
129- val prefs = androidx.security.crypto.EncryptedSharedPreferences .create(
130- " sensitive_info_prefs" ,
131- " master_key_alias" ,
132- masterKey,
133- context,
134- androidx.security.crypto.EncryptedSharedPreferences .PrefKeyEncryptionScheme .AES256_SIV ,
135- androidx.security.crypto.EncryptedSharedPreferences .PrefValueEncryptionScheme .AES256_GCM
136- )
137- prefs.edit().remove(key).apply ()
138- prefs.edit().remove(" ${key} _biometric" ).apply ()
139- }
140-
141- suspend fun isBiometricAvailable (): Boolean {
142- val context = getReactApplicationContext()
143- val biometricManager = BiometricManager .from(context)
144- return biometricManager.canAuthenticate(BiometricManager .Authenticators .BIOMETRIC_STRONG ) == BiometricManager .BIOMETRIC_SUCCESS
145- }
146-
147- suspend fun authenticate (
148- promptTitle : String? ,
149- promptSubtitle : String? ,
150- promptDescription : String? ,
151- promptNegativeButton : String? ,
152- promptReason : String?
153- ): Boolean {
154- val context = getReactApplicationContext()
155- val title = promptTitle ? : " Authenticate"
156- val subtitle = promptSubtitle
157- val description = promptDescription
158- val negativeButtonText = promptNegativeButton ? : " Cancel"
159-
160- val executor: Executor = ContextCompat .getMainExecutor(context)
161- val activity = getCurrentActivity() ? : throw Exception (" No current activity" )
162- return suspendCancellableCoroutine { continuation ->
163- val biometricPrompt = BiometricPrompt (
164- activity,
165- executor,
166- object : BiometricPrompt .AuthenticationCallback () {
167- override fun onAuthenticationSucceeded (result : BiometricPrompt .AuthenticationResult ) {
168- continuation.resume(true )
169- }
170- override fun onAuthenticationError (errorCode : Int , errString : CharSequence ) {
171- continuation.resume(false )
172- }
173- override fun onAuthenticationFailed () {
174- // Do nothing, user can retry
175- }
176- }
177- )
178- val promptInfo = BiometricPrompt .PromptInfo .Builder ()
179- .setTitle(title)
180- .setNegativeButtonText(negativeButtonText)
181- .apply {
182- subtitle?.let { setSubtitle(it) }
183- description?.let { setDescription(it) }
184- }
185- .build()
186- biometricPrompt.authenticate(promptInfo)
187- }
188- }
189- }
7+ override fun multiply (a : Double , b : Double ): Double {
8+ return a * b
9+ }
10+ }
0 commit comments