Skip to content

Commit 1b8f844

Browse files
committed
feat: Enhance error handling and logging in key rotation and sensitive info management
1 parent eaf8ff2 commit 1b8f844

File tree

6 files changed

+42
-26
lines changed

6 files changed

+42
-26
lines changed

android/src/main/java/com/sensitiveinfo/HybridSensitiveInfo.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.sensitiveinfo
33
import android.content.Context
44
import android.os.Handler
55
import android.os.Looper
6+
import android.util.Log
67
import com.margelo.nitro.core.Promise
78
import com.margelo.nitro.sensitiveinfo.*
89
import com.sensitiveinfo.internal.auth.AndroidAuthenticationManager
@@ -188,7 +189,7 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
188189
deps.storage.save(service, request.key, entry)
189190

190191
// Step 8: Build response using response builder
191-
deps.responseBuilder.buildMutationResult(metadata)
192+
return@async deps.responseBuilder.buildMutationResult(metadata)
192193
}
193194
}
194195

@@ -221,14 +222,14 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
221222
// Step 3: Read entry from storage
222223
val entry = deps.storage.read(service, request.key)
223224
if (entry == null) {
224-
return@async null
225+
// Item not found - throw exception that JS can catch and handle as null
226+
throw Exception("Item not found")
225227
}
226228

227229
// Step 4: Decode metadata
228230
val metadata = try {
229231
entry.metadata.toStorageMetadata()
230232
} catch (e: Exception) {
231-
RuntimeError.log("Failed to decode metadata for key: $e")
232233
null
233234
}
234235

@@ -256,7 +257,6 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
256257
null
257258
}
258259
} catch (e: Exception) {
259-
RuntimeError.log("Failed to decrypt value for key: $e")
260260
null
261261
}
262262

@@ -266,15 +266,17 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
266266
backend = StorageBackend.ANDROIDKEYSTORE,
267267
accessControl = AccessControl.NONE,
268268
timestamp = System.currentTimeMillis() / 1000.0,
269-
alias = entry.alias
269+
alias = entry.alias.takeIf { it.isNotEmpty() } ?: "unknown"
270270
)
271271

272-
deps.responseBuilder.buildItem(
272+
val item = deps.responseBuilder.buildItem(
273273
key = request.key,
274274
value = value,
275275
metadata = finalMetadata,
276276
service = service
277277
)
278+
279+
return@async item
278280
}
279281
}
280282

@@ -413,7 +415,7 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
413415
}
414416

415417
// Step 5: Build item using response builder
416-
deps.responseBuilder.buildItem(
418+
return@mapNotNull deps.responseBuilder.buildItem(
417419
key = key,
418420
value = value,
419421
metadata = metadata,
@@ -822,7 +824,7 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
822824
// Result is Promise, but we don't wait for it in background
823825
} catch (e: Exception) {
824826
// Log error but don't crash
825-
android.util.Log.e("KeyRotation", "Automatic rotation failed: ${e.message}")
827+
// Rotation failed silently
826828
}
827829
}
828830
}

android/src/main/java/com/sensitiveinfo/KeyRotation.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class AndroidKeyRotationManager(private val context: Context) {
134134

135135
true
136136
} catch (exception: Exception) {
137-
android.util.Log.e("KeyRotation", "Failed to generate key: ${exception.message}")
137+
// Key generation failed
138138
false
139139
}
140140
}
@@ -157,7 +157,7 @@ class AndroidKeyRotationManager(private val context: Context) {
157157
setCurrentKeyVersion(newKeyVersionId)
158158
true
159159
} catch (exception: Exception) {
160-
android.util.Log.e("KeyRotation", "Failed to rotate key: ${exception.message}")
160+
// Key rotation failed
161161
false
162162
}
163163
}
@@ -280,15 +280,15 @@ class AndroidKeyRotationManager(private val context: Context) {
280280
fun handleInvalidatedKey(keyVersionId: String) {
281281
try {
282282
// Log the invalidation for audit purposes
283-
android.util.Log.w("KeyRotation", "Key invalidated: $keyVersionId")
283+
// Key invalidated
284284

285285
// Attempt to delete the invalidated key
286286
deleteKey(keyVersionId)
287287

288288
// Notify JavaScript side about biometric change
289289
notifyBiometricChangeToJavaScript()
290290
} catch (exception: Exception) {
291-
android.util.Log.e("KeyRotation", "Error handling invalidated key: ${exception.message}")
291+
// Error handling invalidated key
292292
}
293293
}
294294

@@ -361,7 +361,7 @@ class AndroidKeyRotationManager(private val context: Context) {
361361
apply()
362362
}
363363
} catch (exception: Exception) {
364-
android.util.Log.e("KeyRotation", "Failed to store key metadata: ${exception.message}")
364+
// Failed to store key metadata
365365
}
366366
}
367367

android/src/main/java/com/sensitiveinfo/core/Result.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ package com.sensitiveinfo.core
88
* Usage:
99
* ```kotlin
1010
* when (val result = storage.setItem("key", "value")) {
11-
* is Result.Success -> println("Stored: ${result.value}")
12-
* is Result.Failure -> println("Error: ${result.error.message}")
11+
* is Result.Success -> {}
12+
* is Result.Failure -> {}
1313
* }
1414
* ```
1515
*/

android/src/main/java/com/sensitiveinfo/internal/auth/BiometricAuthenticator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ internal class BiometricAuthenticator {
4444
return withContext(Dispatchers.Main) {
4545
if (cipher == null && allowLegacyDeviceCredential && !canUseBiometric()) {
4646
DeviceCredentialPromptFragment.authenticate(activity, effectivePrompt)
47-
cipher
47+
null
4848
} else {
4949
try {
5050
authenticateWithBiometricPrompt(

android/src/main/java/com/sensitiveinfo/internal/crypto/AccessControlResolver.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ internal class AccessControlResolver(
113113
useStrongBox = false,
114114
invalidateOnEnrollment = false
115115
)
116+
else -> {
117+
// For any unknown cases (e.g., iOS-only SECUREENCLAVE from cross-platform API),
118+
// map to the strongest available Android security: StrongBox biometry if available,
119+
// otherwise fall back through the preference list
120+
val biometryResolution = tryResolve(AccessControl.BIOMETRYCURRENTSET, availability)
121+
if (biometryResolution != null) {
122+
return biometryResolution
123+
}
124+
tryResolve(AccessControl.BIOMETRYANY, availability)
125+
}
116126
}
117127
}
118128

android/src/main/java/com/sensitiveinfo/internal/crypto/CryptoManager.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,17 +118,21 @@ internal class CryptoManager(
118118
val authenticated = authenticator.authenticate(prompt, resolution.allowedAuthenticators, cipher)
119119
(authenticated ?: cipher)
120120
} else {
121-
authenticator.authenticate(prompt, resolution.allowedAuthenticators, null)
122-
try {
123-
cipher.init(Cipher.DECRYPT_MODE, key, spec)
124-
} catch (invalidated: KeyPermanentlyInvalidatedException) {
125-
deleteKey(alias)
126-
throw IllegalStateException("Decryption key invalidated. Item must be recreated.", invalidated)
127-
} catch (unrecoverable: UnrecoverableKeyException) {
128-
deleteKey(alias)
129-
throw IllegalStateException("Decryption key unavailable. Item must be recreated.", unrecoverable)
121+
val authenticated = authenticator.authenticate(prompt, resolution.allowedAuthenticators, null)
122+
if (authenticated != null) {
123+
authenticated
124+
} else {
125+
try {
126+
cipher.init(Cipher.DECRYPT_MODE, key, spec)
127+
} catch (invalidated: KeyPermanentlyInvalidatedException) {
128+
deleteKey(alias)
129+
throw IllegalStateException("Decryption key invalidated. Item must be recreated.", invalidated)
130+
} catch (unrecoverable: UnrecoverableKeyException) {
131+
deleteKey(alias)
132+
throw IllegalStateException("Decryption key unavailable. Item must be recreated.", unrecoverable)
133+
}
134+
cipher
130135
}
131-
cipher
132136
}
133137
} catch (error: CancellationException) {
134138
throw error

0 commit comments

Comments
 (0)