diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 8d689678e..c728cd337 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -105,6 +105,12 @@ -keep,allowobfuscation,allowshrinking class com.google.gson.** { *; } -keep,allowobfuscation,allowshrinking class * extends com.google.gson.** { *; } +# Keep ConfigurationProvider's List cert fields (tslCerts / certBundle), +-keepclassmembers,allowobfuscation class ee.ria.DigiDoc.** { + @com.google.gson.annotations.SerializedName ; + @com.google.gson.annotations.JsonAdapter ; +} + # BouncyCastle -keep class org.bouncycastle.** { *; } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/init/LibrarySetup.kt b/app/src/main/kotlin/ee/ria/DigiDoc/init/LibrarySetup.kt index 04ed833f9..ce964e73f 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/init/LibrarySetup.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/init/LibrarySetup.kt @@ -33,7 +33,8 @@ import ee.ria.DigiDoc.libdigidoclib.init.Initialization import ee.ria.DigiDoc.libdigidoclib.init.LibdigidocLibraryLoader import ee.ria.DigiDoc.utils.snackbar.SnackBarManager import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.withContext import java.io.InterruptedIOException import java.net.SocketTimeoutException @@ -52,6 +53,7 @@ class LibrarySetup private val libdigidocLibraryLoader: LibdigidocLibraryLoader, ) { private val logTag = "LibrarySetup" + private var isConfigured = false suspend fun setupLibraries( context: Context, @@ -60,28 +62,34 @@ class LibrarySetup libdigidocLibraryLoader.init(context) cryptoInitialization.init(isLoggingEnabled) - try { - TSLUtil.setupTSLFiles(context) - configurationLoader.initConfiguration( - context, - dataStore.getProxySetting(), - dataStore.getManualProxySettings(), - ) - } catch (ex: Exception) { - if (ex !is UnknownHostException && - ex !is SocketTimeoutException && - ex !is InterruptedIOException - ) { - errorLog( - logTag, - "Unable to initialize configuration: ", - ex, - ) - withContext(Dispatchers.Main) { - SnackBarManager.showMessage( + + if (!isConfigured) { + withContext(IO) { + try { + TSLUtil.setupTSLFiles(context) + configurationLoader.initConfiguration( context, - R.string.configuration_initialization_failed, + dataStore.getProxySetting(), + dataStore.getManualProxySettings(), ) + isConfigured = true + } catch (ex: Exception) { + if (ex !is UnknownHostException && + ex !is SocketTimeoutException && + ex !is InterruptedIOException + ) { + errorLog( + logTag, + "Unable to initialize configuration: ", + ex, + ) + withContext(Main) { + SnackBarManager.showMessage( + context, + R.string.configuration_initialization_failed, + ) + } + } } } } @@ -91,7 +99,7 @@ class LibrarySetup } catch (e: Exception) { if (e !is AlreadyInitializedException) { errorLog(logTag, "Unable to initialize libdigidocpp", e) - withContext(Dispatchers.Main) { + withContext(Main) { Toast .makeText( context, diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModel.kt b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModel.kt index 66fabb6f4..7ca017a4e 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModel.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModel.kt @@ -28,6 +28,7 @@ import androidx.activity.result.ActivityResult import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.google.common.io.ByteStreams import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext @@ -47,7 +48,6 @@ import ee.ria.DigiDoc.utilsLib.file.FileUtil import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import org.xmlpull.v1.XmlPullParserException @@ -88,26 +88,24 @@ class DiagnosticsViewModel } fun observeConfigurationUpdates(context: Context) { - CoroutineScope(Main).launch { + viewModelScope.launch(Main) { configurationRepository.observeConfigurationUpdates { newConfig -> - CoroutineScope(Main).launch { - if (updatedConfiguration.value != null) { - val messageResId = - if (newConfig.configurationUpdateDate !== - updatedConfiguration.value?.configurationUpdateDate - ) { - R.string.configuration_updated - } else { - R.string.configuration_is_already_up_to_date - } - sendAccessibilityEvent( - context, - getAccessibilityEventType(), - context.getString(messageResId), - ) - } - _updatedConfiguration.value = newConfig + if (updatedConfiguration.value != null) { + val messageResId = + if (newConfig.configurationUpdateDate != + updatedConfiguration.value?.configurationUpdateDate + ) { + R.string.configuration_updated + } else { + R.string.configuration_is_already_up_to_date + } + sendAccessibilityEvent( + context, + getAccessibilityEventType(), + context.getString(messageResId), + ) } + _updatedConfiguration.value = newConfig } } } diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderImpl.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderImpl.kt index d0713d66a..625ec2c0b 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderImpl.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderImpl.kt @@ -42,8 +42,11 @@ import ee.ria.DigiDoc.network.proxy.ManualProxy import ee.ria.DigiDoc.network.proxy.ProxySetting import ee.ria.DigiDoc.utilsLib.date.DateUtil import ee.ria.DigiDoc.utilsLib.extensions.removeWhitespaces +import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import java.io.File import java.time.LocalDateTime import java.time.ZoneId @@ -63,28 +66,53 @@ class ConfigurationLoaderImpl private val configurationProperties: ConfigurationProperties, private val configurationSignatureVerifier: ConfigurationSignatureVerifier, ) : ConfigurationLoader { + private val logTag = "ConfigurationLoader" private val configurationFlow = MutableStateFlow(null) + private val loadMutex = Mutex() + @Throws(Exception::class) override suspend fun initConfiguration( context: Context, proxySetting: ProxySetting?, manualProxy: ManualProxy, - ) { - val cacheDir = File(context.cacheDir, CACHE_CONFIG_FOLDER) + ) = loadMutex.withLock { + val cacheDir = getConfigCacheDir(context) if (!cacheDir.exists()) { cacheDir.mkdir() } - loadCachedConfiguration(context, false) + loadLocalConfiguration(context) loadConfigurationProperty(context) - if (shouldCheckForUpdates( - context, - ) - ) { - loadCentralConfiguration(context, proxySetting, manualProxy) + if (shouldCheckForUpdates(context)) { + try { + loadCentralConfigurationCore(context, proxySetting, manualProxy) + } catch (e: Exception) { + errorLog(logTag, "Unable to check Central configuration. Using current configuration", e) + } + } + } + + // Load default configuration if cached configuration does not succeed + private suspend fun loadLocalConfiguration(context: Context) { + try { + loadCachedConfiguration(context, false) + } catch (e: Exception) { + errorLog(logTag, "Unable to load cached configuration. Using default configuration", e) + loadDefaultConfiguration(context) + } + } + + private fun getConfigCacheDir(context: Context): File = File(context.cacheDir, CACHE_CONFIG_FOLDER) + + private fun decodeSignature(signatureBytes: ByteArray): ByteArray { + val signatureText = String(signatureBytes, Charsets.UTF_8) + return if (ConfigurationUtil.isBase64(signatureText)) { + Base64.getDecoder().decode(signatureText) + } else { + signatureBytes } } @@ -103,55 +131,44 @@ class ConfigurationLoaderImpl context: Context, afterCentralCheck: Boolean, ) { - val cacheDir = File(context.cacheDir, CACHE_CONFIG_FOLDER) - + val cacheDir = getConfigCacheDir(context) val confFile = File(cacheDir, CACHED_CONFIG_JSON) val publicKeyFile = File(cacheDir, CACHED_CONFIG_PUB) val signatureFile = File(cacheDir, CACHED_CONFIG_RSA) - if (confFile.exists() && publicKeyFile.exists() && signatureFile.exists()) { - val signatureBytes = signatureFile.readBytes() - val signatureText = String(signatureBytes, Charsets.UTF_8) - - val signature = - if (ConfigurationUtil.isBase64(signatureText)) { - Base64.getDecoder().decode(signatureText) - } else { - signatureBytes - } - - configurationSignatureVerifier - .verifyConfigurationSignature(confFile.readText(), publicKeyFile.readText(), signature) - val configText = confFile.readText() - val configurationProvider = gson.fromJson(configText, ConfigurationProvider::class.java) - ConfigurationCache.cacheConfigurationFiles( + val isCacheComplete = confFile.exists() && publicKeyFile.exists() && signatureFile.exists() + if (!isCacheComplete) { + loadDefaultConfiguration(context) + return + } + + val configText = confFile.readText() + val publicKey = publicKeyFile.readText() + val signature = decodeSignature(signatureFile.readBytes()) + + configurationSignatureVerifier.verifyConfigurationSignature(configText, publicKey, signature) + val configurationProvider = gson.fromJson(configText, ConfigurationProvider::class.java) + ConfigurationCache.cacheConfigurationFiles(context, configText, publicKey, signature) + + if (!afterCentralCheck) { + configurationProperties.updateProperties( context, - configText, - publicKeyFile.readText(), - signature, + configurationProvider.configurationLastUpdateCheckDate, + configurationProvider.configurationUpdateDate, + configurationProvider.metaInf.serial, ) - if (!afterCentralCheck) { - configurationProperties.updateProperties( - context, - configurationProvider.configurationLastUpdateCheckDate, - configurationProvider.configurationUpdateDate, - configurationProvider.metaInf.serial, - ) - configurationProvider.configurationLastUpdateCheckDate = - configurationProperties.getConfigurationLastCheckDate(context) - configurationProvider.configurationUpdateDate = - configurationProperties.getConfigurationUpdatedDate(context) - configurationFlow.value = configurationProvider - } else { - val currentDate = Date() - configurationProvider?.configurationUpdateDate = configurationProvider.configurationUpdateDate - ?: configurationFlow.value?.configurationUpdateDate - configurationProvider?.configurationLastUpdateCheckDate = currentDate - configurationProperties.setConfigurationLastCheckDate(context, currentDate) - configurationFlow.value = configurationProvider - } + configurationProvider.configurationLastUpdateCheckDate = + configurationProperties.getConfigurationLastCheckDate(context) + configurationProvider.configurationUpdateDate = + configurationProperties.getConfigurationUpdatedDate(context) + configurationFlow.value = configurationProvider } else { - loadDefaultConfiguration(context) + val currentDate = Date() + configurationProvider.configurationUpdateDate = configurationProvider.configurationUpdateDate + ?: configurationFlow.value?.configurationUpdateDate + configurationProvider.configurationLastUpdateCheckDate = currentDate + configurationProperties.setConfigurationLastCheckDate(context, currentDate) + configurationFlow.value = configurationProvider } } @@ -160,16 +177,7 @@ class ConfigurationLoaderImpl val confData = assets.open("config/${DEFAULT_CONFIG_JSON}").bufferedReader().use { it.readText() } val publicKey = assets.open("config/${DEFAULT_CONFIG_PUB}").bufferedReader().use { it.readText() } - val signatureBytes = assets.open("config/${DEFAULT_CONFIG_RSA}").readBytes() - - val signatureText = String(signatureBytes, Charsets.UTF_8) - - val signature = - if (ConfigurationUtil.isBase64(signatureText)) { - Base64.getDecoder().decode(signatureText) - } else { - signatureBytes - } + val signature = decodeSignature(assets.open("config/${DEFAULT_CONFIG_RSA}").readBytes()) configurationSignatureVerifier.verifyConfigurationSignature(confData, publicKey, signature) @@ -217,6 +225,15 @@ class ConfigurationLoaderImpl context: Context, proxySetting: ProxySetting?, proxy: ManualProxy, + ) = loadMutex.withLock { + loadCentralConfigurationCore(context, proxySetting, proxy) + } + + @Throws(Exception::class) + private suspend fun loadCentralConfigurationCore( + context: Context, + proxySetting: ProxySetting?, + proxy: ManualProxy, ) { val cachedSignature = ConfigurationCache.getCachedFile(context, CACHED_CONFIG_RSA) diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepository.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepository.kt index 7ddc1120f..10ba075f3 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepository.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepository.kt @@ -34,5 +34,5 @@ interface ConfigurationRepository { manualProxy: ManualProxy, ): ConfigurationProvider? - suspend fun observeConfigurationUpdates(onUpdate: (ConfigurationProvider) -> Unit) + suspend fun observeConfigurationUpdates(onUpdate: suspend (ConfigurationProvider) -> Unit) } diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryImpl.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryImpl.kt index 06a2345f4..0efcf12e9 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryImpl.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryImpl.kt @@ -48,7 +48,7 @@ class ConfigurationRepositoryImpl return getConfiguration() } - override suspend fun observeConfigurationUpdates(onUpdate: (ConfigurationProvider) -> Unit) { + override suspend fun observeConfigurationUpdates(onUpdate: suspend (ConfigurationProvider) -> Unit) { configurationLoader .getConfigurationFlow() .filterNotNull() diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/TSLUtil.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/TSLUtil.kt index 0556d9aa2..4e393acd0 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/TSLUtil.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/TSLUtil.kt @@ -61,6 +61,8 @@ object TSLUtil { } createDirectoryIfNotExist(destination) + + debugLog(logTag, "Setting up TSL files in cache; bundled in assets: ${tslFiles?.joinToString() ?: "none"}") if (!tslFiles.isNullOrEmpty()) { for (fileName in tslFiles) { if (isXMLFile(fileName) && shouldCopyTSL(context, assetsPath, fileName, destination)) { @@ -68,6 +70,7 @@ object TSLUtil { val tslFile = File(destination, fileName) setFileDateAttributes(tslFile) removeExistingETag(tslFile.path) + debugLog(logTag, "Copied TSL '$fileName' from assets into cache (${tslFile.length()} bytes)") } } } @@ -82,20 +85,28 @@ object TSLUtil { fileName: String, destinationDir: String, ): Boolean { - if (!FileUtil.fileExists(File(destinationDir, fileName).path)) { + val cachedFile = File(destinationDir, fileName) + if (!FileUtil.fileExists(cachedFile.path)) { + debugLog(logTag, "TSL '$fileName' is not in the cache yet; copying it from assets") return true } else { try { context.assets .open(File(sourcePath, fileName).path) .use { assetsTSLInputStream -> - FileInputStream(File(destinationDir, fileName)) + FileInputStream(cachedFile) .use { cachedTSLInputStream -> val assetsTslVersion: Int = readSequenceNumber(assetsTSLInputStream) val cachedTslVersion: Int = readSequenceNumber(cachedTSLInputStream) - return assetsTslVersion > cachedTslVersion + val isAssetNewer = assetsTslVersion > cachedTslVersion + debugLog( + logTag, + "TSL '$fileName': assets version $assetsTslVersion, cached version " + + "$cachedTslVersion — ${if (isAssetNewer) "updating cache" else "cache is up to date"}", + ) + return isAssetNewer } } } catch (e: Exception) { diff --git a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/ContainerWrapper.kt b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/ContainerWrapper.kt index e4bc5c450..9d2a63104 100644 --- a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/ContainerWrapper.kt +++ b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/domain/model/ContainerWrapper.kt @@ -22,6 +22,8 @@ package ee.ria.DigiDoc.libdigidoclib.domain.model import ee.ria.DigiDoc.libdigidoclib.SignedContainer +import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog +import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog import ee.ria.DigiDoc.utilsLib.text.TextUtil.removeEmptyStrings import ee.ria.libdigidocpp.ExternalSigner import ee.ria.libdigidocpp.Signature @@ -46,6 +48,7 @@ interface ContainerWrapper { class ContainerWrapperImpl : ContainerWrapper { private lateinit var signature: Signature + private val logTag = "Libdigidoc-ContainerWrapper" @Throws(CertificateException::class) override fun prepareSignature( @@ -54,6 +57,7 @@ class ContainerWrapperImpl : ContainerWrapper { cert: ByteArray?, roleData: RoleData?, ): ByteArray { + debugLog(logTag, "Preparing signature (with role data: ${roleData != null})") signature = when { roleData != null && signedContainer != null -> { @@ -75,7 +79,9 @@ class ContainerWrapperImpl : ContainerWrapper { } else -> throw IllegalStateException("Unable to get container") } - return signature.dataToSign() + val dataToSign = signature.dataToSign() + debugLog(logTag, "Signature prepared (${dataToSign.size} bytes to sign)") + return dataToSign } override fun finalizeSignature( @@ -84,7 +90,14 @@ class ContainerWrapperImpl : ContainerWrapper { signatureArray: ByteArray, ) { signature.setSignatureValue(signatureArray) - signature.extendSignatureProfile(signer) + debugLog(logTag, "Extending signature profile (fetches OCSP confirmation and timestamp)") + try { + signature.extendSignatureProfile(signer) + } catch (e: Exception) { + errorLog(logTag, "Unable to extend signature profile: ${e.message}", e) + throw e + } signedContainer?.rawContainer()?.save() + debugLog(logTag, "Signature finalized and container saved") } } diff --git a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt index 3556d51eb..b2b7ed284 100644 --- a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt +++ b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt @@ -56,8 +56,10 @@ import ee.ria.libdigidocpp.Conf import ee.ria.libdigidocpp.DigiDocConf import ee.ria.libdigidocpp.digidoc import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers.Default import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File @@ -82,6 +84,10 @@ class Initialization private var sivaUrlChangeListener: OnSharedPreferenceChangeListener? = null private var sivaCertChangeListener: OnSharedPreferenceChangeListener? = null + private val configApplyDispatcher = Default.limitedParallelism(1) + private val configScope = CoroutineScope(SupervisorJob() + configApplyDispatcher) + private var lastAppliedConfiguration: ConfigurationProvider? = null + /** * Initialize libdigidoc-lib. * @@ -93,6 +99,7 @@ class Initialization isLoggingEnabled: Boolean = false, ) { if (isInitialized) { + debugLog(libdigidocInitLogTag, "libdigidocpp is already initialized; only refreshing the log level") setLibdigidocppLogLevel(isLoggingEnabled) throw AlreadyInitializedException("Libdigidocpp is already initialized") } @@ -111,6 +118,10 @@ class Initialization throw erre } + debugLog( + libdigidocInitLogTag, + "TSL cache directory contains: ${getSchemaDir(context).list()?.joinToString() ?: "(empty)"}", + ) initLibDigiDocpp( context, getSchemaPath(context), @@ -119,7 +130,7 @@ class Initialization } } - private fun initLibDigiDocpp( + private suspend fun initLibDigiDocpp( context: Context, path: String, isLoggingEnabled: Boolean, @@ -130,10 +141,11 @@ class Initialization ) digidoc.initializeLib(UserAgentUtil.getAppInfo(context), path) UserAgentUtil.setLibdigidocppVersion(digidoc.version()) + debugLog(libdigidocInitLogTag, "Initialized libdigidocpp ${digidoc.version()} (TSL cache: $path)") isInitialized = true } - private fun initLibDigiDocConfiguration( + private suspend fun initLibDigiDocConfiguration( context: Context, isLoggingEnabled: Boolean, ) { @@ -185,10 +197,41 @@ class Initialization loadConfiguration(context) } - private fun overrideConfiguration( + private suspend fun overrideConfiguration( context: Context, configurationProvider: ConfigurationProvider, - ) { + ) = withContext(configApplyDispatcher) { + val newSerial = configurationProvider.metaInf.serial + val normalizedConfiguration = + configurationProvider.copy( + configurationLastUpdateCheckDate = null, + configurationUpdateDate = null, + ) + val lastSerial = lastAppliedConfiguration?.metaInf?.serial + if (lastSerial != null && newSerial < lastSerial) { + debugLog( + libdigidocInitLogTag, + "Ignoring older configuration (serial $newSerial < applied $lastSerial)", + ) + return@withContext + } + if (normalizedConfiguration == lastAppliedConfiguration) { + debugLog( + libdigidocInitLogTag, + "Configuration unchanged (serial $newSerial); skipping re-apply", + ) + return@withContext + } + debugLog( + libdigidocInitLogTag, + "Applying configuration to libdigidocpp — " + + "TSL URL: ${configurationProvider.tslUrl}, TSA URL: ${configurationProvider.tsaUrl}, " + + "SiVa URL: ${configurationProvider.sivaUrl}, " + + "TSL signer certs: ${configurationProvider.tslCerts.size}, " + + "trust bundle certs: ${configurationProvider.certBundle.size}, " + + "config serial: $newSerial", + ) + overrideTSLUrl(configurationProvider.tslUrl) overrideTSLCert(configurationProvider.tslCerts) overrideSivaUrl(configurationProvider.sivaUrl) @@ -228,6 +271,8 @@ class Initialization sivaCertPreferenceKey, configurationProvider.certBundle, ) + + lastAppliedConfiguration = normalizedConfiguration } private fun forcePKCS12Certificate() { @@ -417,10 +462,20 @@ class Initialization } } - private fun loadConfiguration(context: Context) { - configurationRepository.getConfiguration()?.let { overrideConfiguration(context, it) } - CoroutineScope(Main).launch { + private suspend fun loadConfiguration(context: Context) { + val current = configurationRepository.getConfiguration() + debugLog( + libdigidocInitLogTag, + if (current == null) { + "No cached configuration yet; will apply it once it is loaded" + } else { + "Applying cached configuration" + }, + ) + current?.let { overrideConfiguration(context, it) } + configScope.launch { configurationRepository.observeConfigurationUpdates { newConfig -> + debugLog(libdigidocInitLogTag, "Configuration updated; reapplying it to libdigidocpp") overrideConfiguration(context, newConfig) } } diff --git a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/utils/FileUtils.kt b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/utils/FileUtils.kt index 3560fb155..5234bf987 100644 --- a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/utils/FileUtils.kt +++ b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/utils/FileUtils.kt @@ -68,6 +68,8 @@ object FileUtils { errorLog(LIBDIGIDOC_FILEUTILS_LOG_TAG, "Unable to get 'schema' resource", nfe) throw nfe } + debugLog(LIBDIGIDOC_FILEUTILS_LOG_TAG, "Extracting XML schema into ${schemaDir.absolutePath}") + val extractedFiles = mutableListOf() schemaResourceInputStream.use { inputStream -> ZipInputStream(inputStream).use { zipInputStream -> var entry: ZipEntry? @@ -78,9 +80,11 @@ object FileUtils { throw ZipException("Bad zip entry: $entryName") } Files.copy(zipInputStream, Paths.get(entryFile.toURI()), StandardCopyOption.REPLACE_EXISTING) + extractedFiles.add(entryName) } } } + debugLog(LIBDIGIDOC_FILEUTILS_LOG_TAG, "Extracted schema files: ${extractedFiles.joinToString()}") } private fun isChild(