Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@
-keep,allowobfuscation,allowshrinking class com.google.gson.** { *; }
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.** { *; }

# Keep ConfigurationProvider's List<String> cert fields (tslCerts / certBundle),
-keepclassmembers,allowobfuscation class ee.ria.DigiDoc.** {
@com.google.gson.annotations.SerializedName <fields>;
@com.google.gson.annotations.JsonAdapter <fields>;
}

# BouncyCastle
-keep class org.bouncycastle.** { *; }

Expand Down
52 changes: 30 additions & 22 deletions app/src/main/kotlin/ee/ria/DigiDoc/init/LibrarySetup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,6 +53,7 @@ class LibrarySetup
private val libdigidocLibraryLoader: LibdigidocLibraryLoader,
) {
private val logTag = "LibrarySetup"
private var isConfigured = false

suspend fun setupLibraries(
context: Context,
Expand All @@ -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,
)
}
}
}
}
}
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -63,28 +66,53 @@ class ConfigurationLoaderImpl
private val configurationProperties: ConfigurationProperties,
private val configurationSignatureVerifier: ConfigurationSignatureVerifier,
) : ConfigurationLoader {
private val logTag = "ConfigurationLoader"
private val configurationFlow = MutableStateFlow<ConfigurationProvider?>(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
}
}

Expand All @@ -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
}
}

Expand All @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ interface ConfigurationRepository {
manualProxy: ManualProxy,
): ConfigurationProvider?

suspend fun observeConfigurationUpdates(onUpdate: (ConfigurationProvider) -> Unit)
suspend fun observeConfigurationUpdates(onUpdate: suspend (ConfigurationProvider) -> Unit)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading
Loading