11package com.sensitiveinfo
22
33import android.content.Context
4+ import android.os.Handler
5+ import android.os.Looper
46import com.margelo.nitro.core.Promise
57import com.margelo.nitro.sensitiveinfo.*
8+ import com.sensitiveinfo.internal.auth.AndroidAuthenticationManager
9+ import com.sensitiveinfo.internal.auth.AuthenticationManager
610import com.sensitiveinfo.internal.auth.BiometricAuthenticator
11+ import com.sensitiveinfo.internal.crypto.AccessControlManager
712import com.sensitiveinfo.internal.crypto.AccessControlResolver
13+ import com.sensitiveinfo.internal.crypto.AndroidAccessControlManager
814import com.sensitiveinfo.internal.crypto.CryptoManager
915import com.sensitiveinfo.internal.crypto.SecurityAvailabilityResolver
16+ import com.sensitiveinfo.internal.metadata.AndroidMetadataManagerImpl
17+ import com.sensitiveinfo.internal.metadata.MetadataManager
1018import com.sensitiveinfo.internal.response.ResponseBuilder
1119import com.sensitiveinfo.internal.response.StandardResponseBuilder
1220import com.sensitiveinfo.internal.storage.PersistedEntry
@@ -41,10 +49,14 @@ import kotlin.jvm.Volatile
4149 * @since 6.0.0
4250 */
4351final class HybridSensitiveInfo : HybridSensitiveInfoSpec () {
52+
4453 private data class Dependencies (
4554 val context : Context ,
4655 val storage : SecureStorage ,
4756 val cryptoManager : CryptoManager ,
57+ val metadataManager : MetadataManager ,
58+ val authenticationManager : AuthenticationManager ,
59+ val accessControlManager : AccessControlManager ,
4860 val accessControlResolver : AccessControlResolver ,
4961 val securityAvailabilityResolver : SecurityAvailabilityResolver ,
5062 val serviceNameResolver : ServiceNameResolver ,
@@ -58,9 +70,11 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
5870 private val initializationLock = Any ()
5971 private val coroutineScope = CoroutineScope (Dispatchers .Default + SupervisorJob ())
6072 private var rotationEventCallback: ((RotationEvent ) -> Unit )? = null
61- private val mainHandler = android.os. Handler (android.os. Looper .getMainLooper())
73+ private val mainHandler = Handler (Looper .getMainLooper())
6274 private var rotationCheckRunnable: Runnable ? = null
6375
76+ // MARK: - Initialization
77+
6478 private fun initialize (ctx : Context ): Dependencies {
6579 dependencies?.let { return it }
6680
@@ -72,10 +86,20 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
7286 val authenticator = BiometricAuthenticator ()
7387 val cryptoManager = CryptoManager (authenticator)
7488
89+ // Initialize specialized managers
90+ val metadataManager: MetadataManager = AndroidMetadataManagerImpl ()
91+ val authenticationManager: AuthenticationManager = AndroidAuthenticationManager (authenticator)
92+ val accessControlManager: AccessControlManager = AndroidAccessControlManager (
93+ securityAvailabilityResolver
94+ )
95+
7596 Dependencies (
7697 context = ctx,
7798 storage = SecureStorage (ctx),
7899 cryptoManager = cryptoManager,
100+ metadataManager = metadataManager,
101+ authenticationManager = authenticationManager,
102+ accessControlManager = accessControlManager,
79103 accessControlResolver = accessControlResolver,
80104 securityAvailabilityResolver = securityAvailabilityResolver,
81105 serviceNameResolver = serviceNameResolver,
@@ -655,15 +679,21 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
655679 var reEncryptedCount = 0
656680 val errors = mutableListOf<ReEncryptError >()
657681
658- // Step 4: Re-encrypt items that use old keys
682+ // Step 4: Re-encrypt items that use old keys or have empty metadata alias
659683 for ((key, entry) in entries) {
660684 try {
661- if (entry.alias != currentKeyVersion && entry.ciphertext != null && entry.iv != null ) {
685+ // Re-encrypt if: (1) using old key, (2) metadata alias is empty, or (3) has ciphertext and iv
686+ val shouldReEncrypt = (entry.metadata.alias != currentKeyVersion || entry.metadata.alias.isEmpty()) &&
687+ entry.ciphertext != null && entry.iv != null
688+
689+ if (shouldReEncrypt) {
662690 // Get access control from persisted
663691 val accessControl = accessControlFromPersisted(entry.metadata.accessControl) ? : AccessControl .NONE
664692 val securityLevel = securityLevelFromPersisted(entry.metadata.securityLevel) ? : SecurityLevel .SOFTWARE
665693
666- // Decrypt with old key
694+ // Decrypt with old key (use metadata.alias or entry.alias as fallback)
695+ val oldKeyAlias = entry.metadata.alias.takeIf { it.isNotEmpty() } ? : entry.alias
696+
667697 val resolution = deps.cryptoManager.buildResolutionForPersisted(
668698 accessControl = accessControl,
669699 securityLevel = securityLevel,
@@ -674,7 +704,7 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
674704 )
675705
676706 val plaintext = deps.cryptoManager.decrypt(
677- entry.alias ,
707+ oldKeyAlias ,
678708 entry.ciphertext,
679709 entry.iv,
680710 resolution,
@@ -698,7 +728,7 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
698728 null
699729 )
700730
701- // Update storage
731+ // Update storage with new key alias
702732 val updatedEntry = entry.copy(
703733 ciphertext = encryption.ciphertext,
704734 iv = encryption.iv,
@@ -797,12 +827,18 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
797827
798828 for ((key, entry) in entries) {
799829 try {
800- if (entry.metadata.alias != newKeyVersion && entry.ciphertext != null && entry.iv != null ) {
830+ // Re-encrypt if: (1) using old key, (2) metadata alias is empty, or (3) has ciphertext and iv
831+ val shouldReEncrypt = (entry.metadata.alias != newKeyVersion || entry.metadata.alias.isEmpty()) &&
832+ entry.ciphertext != null && entry.iv != null
833+
834+ if (shouldReEncrypt) {
801835 // Get access control from persisted
802836 val accessControl = accessControlFromPersisted(entry.metadata.accessControl) ? : AccessControl .NONE
803837 val securityLevel = securityLevelFromPersisted(entry.metadata.securityLevel) ? : SecurityLevel .SOFTWARE
804838
805- // Decrypt with old key
839+ // Decrypt with old key (use metadata.alias or entry.alias as fallback)
840+ val oldKeyAlias = entry.metadata.alias.takeIf { it.isNotEmpty() } ? : entry.alias
841+
806842 val resolution = deps.cryptoManager.buildResolutionForPersisted(
807843 accessControl = accessControl,
808844 securityLevel = securityLevel,
@@ -813,7 +849,7 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
813849 )
814850
815851 val plaintext = deps.cryptoManager.decrypt(
816- entry.metadata.alias ,
852+ oldKeyAlias ,
817853 entry.ciphertext,
818854 entry.iv,
819855 resolution,
@@ -837,7 +873,7 @@ final class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
837873 null
838874 )
839875
840- // Update storage
876+ // Update storage with new key alias
841877 val updatedEntry = entry.copy(
842878 ciphertext = encryption.ciphertext,
843879 iv = encryption.iv,
0 commit comments