@@ -10,9 +10,9 @@ import uk.gov.android.securestore.authentication.AuthenticatorPromptConfiguratio
1010import uk.gov.android.securestore.authentication.UserAuthenticator
1111import uk.gov.android.securestore.crypto.HybridCryptoManagerAsync
1212import uk.gov.android.securestore.crypto.HybridCryptoManagerAsyncImpl
13- import uk.gov.android.securestore.error.ErrorTypeHandlerV2
1413import uk.gov.android.securestore.error.SecureStorageErrorV2
15- import uk.gov.android.securestore.error.SecureStoreErrorTypeV2
14+ import uk.gov.android.securestore.error.SecureStorageErrorV2.Companion.mapToSecureStorageError
15+ import kotlin.coroutines.resumeWithException
1616import kotlin.coroutines.suspendCoroutine
1717
1818@Suppress(" TooGenericExceptionCaught" , " TooManyFunctions" )
@@ -22,7 +22,6 @@ class SharedPrefsStoreAsyncV2(
2222) : SecureStoreAsyncV2 {
2323 private var configurationAsync: SecureStorageConfigurationAsync ? = null
2424 private var sharedPrefs: SharedPreferences ? = null
25- private val errorHandler = ErrorTypeHandlerV2
2625
2726 override fun init (
2827 context : Context ,
@@ -46,7 +45,7 @@ class SharedPrefsStoreAsyncV2(
4645 }
4746 result.data
4847 } catch (e: Exception ) {
49- throw SecureStorageErrorV2 (e )
48+ throw e.mapToSecureStorageError( )
5049 }
5150 }
5251
@@ -63,96 +62,53 @@ class SharedPrefsStoreAsyncV2(
6362 try {
6463 hybridCryptoManagerAsync.deleteKey()
6564 } catch (e: Exception ) {
66- throw SecureStorageErrorV2 (e )
65+ throw e.mapToSecureStorageError( )
6766 }
6867 }
6968
7069 override suspend fun retrieve (
7170 vararg key : String ,
72- ): RetrievalEventV2 {
71+ ): Map < String , String ?> {
7372 return configurationAsync?.let { configuration ->
7473 if (configuration.accessControlLevel != AccessControlLevel .OPEN ) {
75- RetrievalEventV2 .Failed (
76- SecureStoreErrorTypeV2 .RECOVERABLE ,
77- " Access control level must be OPEN to use this retrieve method" ,
78- )
74+ throw SecureStorageErrorV2 (Exception (REQUIRE_OPEN_ACCESS_LEVEL ))
7975 } else {
8076 try {
81- val results = handleResults(* key)
82- RetrievalEventV2 .Success (results)
83- } catch (e: SecureStorageErrorV2 ) {
84- RetrievalEventV2 .Failed (
85- errorHandler.getErrorType(e),
86- e.message,
87- )
77+ handleResults(* key)
78+ } catch (e: Exception ) {
79+ throw e.mapToSecureStorageError()
8880 }
8981 }
90- } ? : RetrievalEventV2 .Failed (
91- SecureStoreErrorTypeV2 .RECOVERABLE ,
92- " Must call init on SecureStore first!" ,
93- )
82+ } ? : throw SecureStorageErrorV2 (INIT_ERROR )
9483 }
9584
96- @Suppress(" NestedBlockDepth" , " LongMethod " )
85+ @Suppress(" NestedBlockDepth" )
9786 override suspend fun retrieveWithAuthentication (
9887 vararg key : String ,
9988 authPromptConfig : AuthenticatorPromptConfiguration ,
10089 context : FragmentActivity ,
101- ): RetrievalEventV2 {
102- var result: RetrievalEventV2 = RetrievalEventV2 .Failed (
103- SecureStoreErrorTypeV2 .RECOVERABLE ,
104- " Must call init on SecureStore first!" ,
105- )
106- configurationAsync?.let { configuration ->
90+ ): Map <String , String ?> {
91+ return configurationAsync?.let { configuration ->
92+ // When access control is set to open on the secureStore instance, then redirect consumer to use the retrieve method
10793 if (configuration.accessControlLevel == AccessControlLevel .OPEN ) {
108- result = RetrievalEventV2 .Failed (
109- SecureStoreErrorTypeV2 .RECOVERABLE ,
110- " Use retrieve method, access control is set to OPEN, " +
111- " no need for auth" ,
112- )
94+ throw SecureStorageErrorV2 (Exception (AUTH_ON_OPEN_STORE_ERROR_MSG ))
11395 } else {
96+ // Attempt to surface the Biometrics prompt and handle the result of that
11497 try {
11598 authenticator.init (context)
116- val authenticateResultSuccess: Boolean = suspendCoroutine { continuation ->
117- authenticator.authenticate(
118- configuration.accessControlLevel,
119- authPromptConfig,
120- AuthenticatorCallbackHandler (
121- onSuccess = {
122- continuation.resumeWith(Result .success(true ))
123- },
124- onError = { errorCode, errorString ->
125- result = RetrievalEventV2 .Failed (
126- errorHandler.getErrorType(errorCode),
127- " $BIOMETRIC_PREFIX$errorCode $errorString " ,
128- )
129- continuation.resumeWith(Result .success(false ))
130- },
131- onFailure = {
132- // Do nothing to allow user to try again
133- },
134- ),
135- )
136- }
137- if (authenticateResultSuccess) {
138- result = processSafeHandleResultsOnAuthenticateSuccess(* key)
139- }
140- } catch (e: SecureStorageErrorV2 ) {
141- result = RetrievalEventV2 .Failed (
142- errorHandler.getErrorType(e),
143- " authenticate call throws SecureStorageError ${e.message} " ,
144- )
145- } catch (e: Exception ) {
146- result = RetrievalEventV2 .Failed (
147- SecureStoreErrorTypeV2 .RECOVERABLE ,
148- " authenticate call throws Exception ${e.message} " ,
149- )
99+ handleBiometricPrompt(configuration, authPromptConfig)
100+ handleResults(* key)
101+ // Catches errors thrown from the BiometricPrompt onError(...)
102+ } catch (sse: SecureStorageErrorV2 ) {
103+ throw sse
104+ // Catches any other errors (mainly the java.security and java.crypto fron the KeyStore)
105+ } catch (e: Throwable ) {
106+ throw e.mapToSecureStorageError()
150107 } finally {
151108 authenticator.close()
152109 }
153110 }
154- }
155- return result
111+ } ? : throw SecureStorageErrorV2 (INIT_ERROR )
156112 }
157113
158114 override fun exists (key : String ): Boolean {
@@ -164,26 +120,22 @@ class SharedPrefsStoreAsyncV2(
164120 it.edit {
165121 putString(key, value)
166122 }
167- } ? : throw SecureStorageErrorV2 ( Exception ( " You must call init first! " ))
123+ } ? : throw INIT_ERROR
168124 }
169125
170126 private suspend fun cryptoDecryptText (
171127 alias : String ,
172128 onTextReady : (String? ) -> Unit ,
173129 ) {
174130 sharedPrefs?.let {
175- try {
176- val encryptedData = it.getString(alias, null )
177- val encryptedKey = it.getString(alias + KEY_SUFFIX , null )
178- if (encryptedData.isNullOrEmpty() || encryptedKey.isNullOrEmpty()) {
179- onTextReady(null )
180- } else {
181- onTextReady(hybridCryptoManagerAsync.decrypt(encryptedData, encryptedKey))
182- }
183- } catch (e: Exception ) {
184- throw SecureStorageErrorV2 (e)
131+ val encryptedData = it.getString(alias, null )
132+ val encryptedKey = it.getString(alias + KEY_SUFFIX , null )
133+ if (encryptedData.isNullOrEmpty() || encryptedKey.isNullOrEmpty()) {
134+ onTextReady(null )
135+ } else {
136+ onTextReady(hybridCryptoManagerAsync.decrypt(encryptedData, encryptedKey))
185137 }
186- } ? : throw SecureStorageErrorV2 ( Exception ( " You must call init first! " ))
138+ } ? : throw INIT_ERROR
187139 }
188140
189141 private suspend fun handleResults (vararg key : String ): MutableMap <String , String ?> {
@@ -196,23 +148,42 @@ class SharedPrefsStoreAsyncV2(
196148 return results
197149 }
198150
199- private suspend fun processSafeHandleResultsOnAuthenticateSuccess (
200- vararg key : String ,
201- ): RetrievalEventV2 =
202- try {
203- RetrievalEventV2 .Success (handleResults(* key))
204- } catch (e: SecureStorageErrorV2 ) {
205- RetrievalEventV2 .Failed (
206- errorHandler.getErrorType(e),
207- " authenticate call onSuccess callback throws " +
208- " SecureStorageError ${e.message} " ,
151+ private suspend fun handleBiometricPrompt (
152+ config : SecureStorageConfigurationAsync ,
153+ authPromptConfig : AuthenticatorPromptConfiguration ,
154+ ) {
155+ suspendCoroutine { continuation ->
156+ authenticator.authenticate(
157+ config.accessControlLevel,
158+ authPromptConfig,
159+ AuthenticatorCallbackHandler (
160+ onSuccess = {
161+ // This just allows for continuation
162+ continuation.resumeWith(Result .success(true ))
163+ },
164+ onError = { errorCode, errorString ->
165+ // Continues the coroutine with the error
166+ continuation.resumeWithException(
167+ SecureStorageErrorV2
168+ .getErrorFromBiometricsError(errorCode, errorString),
169+ )
170+ },
171+ onFailure = {
172+ // Do nothing to allow user to try again
173+ },
174+ ),
209175 )
210176 }
177+ }
211178
212179 companion object {
213180 // DO NOT CHANGE THIS
214- private const val KEY_SUFFIX = " Key"
181+ internal const val KEY_SUFFIX = " Key"
215182
216- private const val BIOMETRIC_PREFIX = " biometric error code "
183+ private val INIT_ERROR = Exception (" Must call init on SecureStore first!" )
184+ private const val AUTH_ON_OPEN_STORE_ERROR_MSG = " Use retrieve method, access control is" +
185+ " set to OPEN, no need for auth"
186+ private const val REQUIRE_OPEN_ACCESS_LEVEL = " Access control level must be OPEN to use" +
187+ " this retrieve method"
217188 }
218189}
0 commit comments