diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt index 66bd01d71..8019bf2f4 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt @@ -95,7 +95,7 @@ class FileOpeningRepositoryImplTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt index 756dba119..503f6f046 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt @@ -104,7 +104,7 @@ class SivaServiceTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt index 7e3791e8b..5cc058c34 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt @@ -149,7 +149,7 @@ class DiagnosticsViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt index 0e6f004d7..742abd24a 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt @@ -150,7 +150,7 @@ class FileOpeningViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) CryptoInitialization().init() diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt index 551b5b33c..aa01fb740 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt @@ -168,7 +168,7 @@ class MobileIdViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt index 991d1cf77..acd64f517 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt @@ -109,7 +109,7 @@ class RecentDocumentsViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt index 03400e2d7..ac9f95123 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt @@ -121,7 +121,7 @@ class SigningViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt index fe3e0a8b1..e239d8206 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt @@ -175,7 +175,7 @@ class SmartIdViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt index cf0ee584e..16b689ebd 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt @@ -110,7 +110,7 @@ class SharedContainerViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt index 693e4ec92..2dfcd65db 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt @@ -103,7 +103,7 @@ class SharedSettingsViewModelTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt index 0108d23f3..d93b3d4d8 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt @@ -56,6 +56,7 @@ import ee.ria.DigiDoc.utilsLib.file.FileUtil.getExternalFileUris import ee.ria.DigiDoc.utilsLib.file.FileUtil.getLogsDirectory import ee.ria.DigiDoc.utilsLib.locale.LocaleUtil.getLocale import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.launch import java.util.logging.Logger import javax.inject.Inject @@ -88,6 +89,8 @@ class MainActivity : @Inject lateinit var secureUtil: SecureUtil + private val logTag = "MainActivity" + private var isAppReady = false override fun onCreate(savedInstanceState: Bundle?) { @@ -160,15 +163,21 @@ class MainActivity : ) val isLoggingEnabled = isDebug || (isDiagnosticsLoggingEnabled && isDiagnosticsLoggingRunning) lifecycleScope.launch { - LoggingUtil.initialize( - applicationContext, - Logger.getLogger(MainActivity::class.java.name), - isLoggingEnabled, - ) - fileTypeSetup.initializeApplicationFileTypesAssociation(componentClassName) - librarySetup.setupLibraries(applicationContext, isLoggingEnabled) - - isAppReady = true + try { + LoggingUtil.initialize( + applicationContext, + Logger.getLogger(MainActivity::class.java.name), + isLoggingEnabled, + ) + fileTypeSetup.initializeApplicationFileTypesAssociation(componentClassName) + librarySetup.setupLibraries(applicationContext, isLoggingEnabled) + } catch (ce: CancellationException) { + throw ce + } catch (t: Throwable) { + LoggingUtil.errorLog(logTag, "Unable to complete application setup", t) + } finally { + isAppReady = true + } setContent { RIADigiDocTheme(darkTheme = useDarkMode) { diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt index 0cd698e23..bcd4572df 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt @@ -68,6 +68,8 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import ee.ria.DigiDoc.BuildConfig import ee.ria.DigiDoc.R +import ee.ria.DigiDoc.configuration.exception.ConfigurationSignatureValidationException +import ee.ria.DigiDoc.configuration.exception.PublicKeyNotFoundException import ee.ria.DigiDoc.ui.component.menu.SettingsMenuBottomSheet import ee.ria.DigiDoc.ui.component.settings.SettingsSwitchItem import ee.ria.DigiDoc.ui.component.shared.CancelAndOkButtonRow @@ -91,6 +93,7 @@ import ee.ria.DigiDoc.utilsLib.file.FileUtil.sanitizeString import ee.ria.DigiDoc.viewmodel.DiagnosticsViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedSettingsViewModel +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -219,6 +222,12 @@ fun DiagnosticsScreen( diagnosticsViewModel.updateConfiguration(context) } showMessage(context, R.string.configuration_update_success, SnackbarType.SUCCESS) + } catch (_: ConfigurationSignatureValidationException) { + showMessage(context, R.string.configuration_update_validation_failed) + } catch (_: PublicKeyNotFoundException) { + showMessage(context, R.string.configuration_initialization_failed) + } catch (ce: CancellationException) { + throw ce } catch (_: Exception) { showMessage(context, R.string.configuration_update_failed) } 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 ce964e73f..d8cbea270 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/init/LibrarySetup.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/init/LibrarySetup.kt @@ -24,6 +24,8 @@ package ee.ria.DigiDoc.init import android.content.Context import android.widget.Toast import ee.ria.DigiDoc.R +import ee.ria.DigiDoc.configuration.exception.ConfigurationSignatureValidationException +import ee.ria.DigiDoc.configuration.exception.PublicKeyNotFoundException import ee.ria.DigiDoc.configuration.loader.ConfigurationLoader import ee.ria.DigiDoc.configuration.utils.TSLUtil import ee.ria.DigiDoc.cryptolib.init.CryptoInitialization @@ -31,10 +33,15 @@ import ee.ria.DigiDoc.domain.preferences.DataStore import ee.ria.DigiDoc.libdigidoclib.exceptions.AlreadyInitializedException import ee.ria.DigiDoc.libdigidoclib.init.Initialization import ee.ria.DigiDoc.libdigidoclib.init.LibdigidocLibraryLoader +import ee.ria.DigiDoc.utils.locale.LocaleUtil import ee.ria.DigiDoc.utils.snackbar.SnackBarManager +import ee.ria.DigiDoc.utilsLib.locale.LocaleUtil.getLocale import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.io.InterruptedIOException import java.net.SocketTimeoutException @@ -51,61 +58,107 @@ class LibrarySetup private val configurationLoader: ConfigurationLoader, private val dataStore: DataStore, private val libdigidocLibraryLoader: LibdigidocLibraryLoader, + private val localeUtil: LocaleUtil, ) { private val logTag = "LibrarySetup" + + private val setupMutex = Mutex() private var isConfigured = false suspend fun setupLibraries( context: Context, isLoggingEnabled: Boolean, ) { + val localizedContext = localeUtil.updateLocale(context, dataStore.getLocale() ?: getLocale("en")) + libdigidocLibraryLoader.init(context) cryptoInitialization.init(isLoggingEnabled) - if (!isConfigured) { - withContext(IO) { - try { - TSLUtil.setupTSLFiles(context) - configurationLoader.initConfiguration( - context, - 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, + setupMutex.withLock { + if (!isConfigured) { + withContext(IO) { + try { + TSLUtil.setupTSLFiles(context) + configurationLoader.initConfiguration( + context, + dataStore.getProxySetting(), + dataStore.getManualProxySettings(), ) + isConfigured = true + } catch (ex: ConfigurationSignatureValidationException) { + errorLog(logTag, "Configuration signature validation failed", ex) + + val hasConfiguration = configurationLoader.getConfigurationFlow().value != null + isConfigured = hasConfiguration + + withContext(Main) { + SnackBarManager.showMessage( + localizedContext, + if (hasConfiguration) { + R.string.configuration_update_validation_failed + } else { + R.string.configuration_initialization_failed + }, + ) + } + } catch (ex: PublicKeyNotFoundException) { + errorLog(logTag, "Bundled signing key unavailable", ex) + withContext(Main) { SnackBarManager.showMessage( - context, + localizedContext, R.string.configuration_initialization_failed, ) } + } catch (ce: CancellationException) { + throw ce + } catch (ex: Exception) { + if (ex !is UnknownHostException && + ex !is SocketTimeoutException && + ex !is InterruptedIOException + ) { + errorLog( + logTag, + "Unable to initialize configuration: ", + ex, + ) + withContext(Main) { + SnackBarManager.showMessage( + localizedContext, + R.string.configuration_initialization_failed, + ) + } + } + } + + if (configurationLoader.getConfigurationFlow().value == null) { + try { + configurationLoader.loadLocalConfiguration(context) + } catch (ce: CancellationException) { + throw ce + } catch (fallbackFailure: Exception) { + errorLog(logTag, "Unable to load local configuration", fallbackFailure) + } } } } - } - try { - initialization.init(context, isLoggingEnabled) - } catch (e: Exception) { - if (e !is AlreadyInitializedException) { - errorLog(logTag, "Unable to initialize libdigidocpp", e) - withContext(Main) { - Toast - .makeText( - context, - R.string.libdigidocpp_initialization_failed, - Toast.LENGTH_LONG, - ).show() + try { + initialization.init(context, isLoggingEnabled) + } catch (ce: CancellationException) { + throw ce + } catch (e: Exception) { + if (e !is AlreadyInitializedException) { + errorLog(logTag, "Unable to initialize libdigidocpp", e) + withContext(Main) { + Toast + .makeText( + localizedContext, + R.string.libdigidocpp_initialization_failed, + Toast.LENGTH_LONG, + ).show() + } } } } diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index e069e2a03..d9e13eaf4 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -485,6 +485,7 @@ Konfiguratsioon uuendatud RIA DigiDoci konfiguratsiooni värskendamine õnnestus Palun kontrolli internetiühendust ja võrgu sätteid + Serveris olev konfiguratsioonifail ei valideeru Keel muudetud Teema muudetud Seade väärtus muudetud diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c5c37fcc9..4ba1443fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -485,6 +485,7 @@ Configuration updated RIA DigiDoc configuration update was successful Please check your internet connection and network settings + The configuration file located on the server cannot be validated Language changed Theme changed Setting value changed diff --git a/app/src/test/kotlin/ee/ria/DigiDoc/init/LibrarySetupTest.kt b/app/src/test/kotlin/ee/ria/DigiDoc/init/LibrarySetupTest.kt new file mode 100644 index 000000000..4186c7f87 --- /dev/null +++ b/app/src/test/kotlin/ee/ria/DigiDoc/init/LibrarySetupTest.kt @@ -0,0 +1,206 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.init + +import android.content.Context +import android.content.res.AssetManager +import ee.ria.DigiDoc.R +import ee.ria.DigiDoc.configuration.exception.ConfigurationSignatureValidationException +import ee.ria.DigiDoc.configuration.loader.ConfigurationLoader +import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider +import ee.ria.DigiDoc.cryptolib.init.CryptoInitialization +import ee.ria.DigiDoc.domain.preferences.DataStore +import ee.ria.DigiDoc.libdigidoclib.init.Initialization +import ee.ria.DigiDoc.libdigidoclib.init.LibdigidocLibraryLoader +import ee.ria.DigiDoc.network.proxy.ManualProxy +import ee.ria.DigiDoc.network.proxy.ProxySetting +import ee.ria.DigiDoc.utils.locale.LocaleUtil +import ee.ria.DigiDoc.utilsLib.MainDispatcherRule +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import java.net.UnknownHostException +import java.nio.file.Files + +@OptIn(ExperimentalCoroutinesApi::class) +class LibrarySetupTest { + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + private lateinit var context: Context + private lateinit var localizedContext: Context + private lateinit var configurationLoader: ConfigurationLoader + private lateinit var initialization: Initialization + private lateinit var librarySetup: LibrarySetup + + @Before + fun setUp() { + val assets = mock() + whenever(assets.list("tslFiles")).thenReturn(emptyArray()) + + context = mock() + whenever(context.cacheDir).thenReturn(Files.createTempDirectory("librarySetupTest").toFile()) + whenever(context.assets).thenReturn(assets) + + localizedContext = mock() + whenever(localizedContext.getString(any())).thenReturn("message") + + val dataStore = mock() + whenever(dataStore.getProxySetting()).thenReturn(ProxySetting.NO_PROXY) + whenever(dataStore.getManualProxySettings()).thenReturn(ManualProxy("", 80, "", "")) + + val localeUtil = mock() + whenever(localeUtil.updateLocale(any(), any())).thenReturn(localizedContext) + + configurationLoader = mock() + + initialization = mock() + + librarySetup = + LibrarySetup( + initialization, + mock(), + configurationLoader, + dataStore, + mock(), + localeUtil, + ) + } + + @Test + fun librarySetup_setupLibraries_showsValidationMessageWhenCentralConfigurationCannotBeValidated() = + runTest { + givenLoadedConfiguration(true) + givenInitConfigurationFails(ConfigurationSignatureValidationException()) + + librarySetup.setupLibraries(context, false) + + verify(localizedContext).getString(R.string.configuration_update_validation_failed) + verify(localizedContext, never()).getString(R.string.configuration_initialization_failed) + verify(context, never()).getString(any()) + } + + @Test + fun librarySetup_setupLibraries_showsInitializationFailedMessageWhenNoConfigurationLoaded() = + runTest { + givenLoadedConfiguration(false) + givenInitConfigurationFails(ConfigurationSignatureValidationException()) + + librarySetup.setupLibraries(context, false) + + verify(localizedContext).getString(R.string.configuration_initialization_failed) + verify(localizedContext, never()).getString(R.string.configuration_update_validation_failed) + } + + @Test + fun librarySetup_setupLibraries_showsNoMessageWhenNetworkIsUnavailable() = + runTest { + givenLoadedConfiguration(true) + givenInitConfigurationFails(UnknownHostException()) + + librarySetup.setupLibraries(context, false) + + verify(localizedContext, never()).getString(any()) + } + + @Test + fun librarySetup_setupLibraries_showsInitializationFailedMessageWhenConfigurationFailsForOtherReason() = + runTest { + givenLoadedConfiguration(true) + givenInitConfigurationFails(IllegalStateException("broken")) + + librarySetup.setupLibraries(context, false) + + verify(localizedContext).getString(R.string.configuration_initialization_failed) + verify(localizedContext, never()).getString(R.string.configuration_update_validation_failed) + } + + @Test + fun librarySetup_setupLibraries_doesNotRetryConfigurationWhenValidationFailedButConfigurationLoaded() = + runTest { + givenLoadedConfiguration(true) + givenInitConfigurationFails(ConfigurationSignatureValidationException()) + + librarySetup.setupLibraries(context, false) + librarySetup.setupLibraries(context, false) + + verify(configurationLoader, times(1)).initConfiguration(any(), anyOrNull(), any()) + verify(initialization, times(2)).init(any(), any()) + } + + @Test + fun librarySetup_setupLibraries_configuresOnceWhenEnteredConcurrently() = + runTest { + givenLoadedConfiguration(true) + + coroutineScope { + launch { librarySetup.setupLibraries(context, false) } + launch { librarySetup.setupLibraries(context, false) } + } + + verify(configurationLoader, times(1)).initConfiguration(any(), anyOrNull(), any()) + verify(initialization, times(2)).init(any(), any()) + } + + @Test + fun librarySetup_setupLibraries_loadsFallbackConfigurationWhenNoneLoaded() = + runTest { + givenLoadedConfiguration(false) + givenInitConfigurationFails(IllegalStateException("broken")) + + librarySetup.setupLibraries(context, false) + + verify(configurationLoader).loadLocalConfiguration(context) + } + + @Test + fun librarySetup_setupLibraries_skipsFallbackWhenConfigurationAlreadyLoaded() = + runTest { + givenLoadedConfiguration(true) + givenInitConfigurationFails(IllegalStateException("broken")) + + librarySetup.setupLibraries(context, false) + + verify(configurationLoader, never()).loadLocalConfiguration(any()) + } + + private fun givenLoadedConfiguration(loaded: Boolean) { + val configuration = if (loaded) mock() else null + whenever(configurationLoader.getConfigurationFlow()).thenReturn(MutableStateFlow(configuration)) + } + + private suspend fun givenInitConfigurationFails(throwable: Throwable) { + whenever(configurationLoader.initConfiguration(any(), anyOrNull(), any())).thenThrow(throwable) + } +} diff --git a/codemagic.yaml b/codemagic.yaml index 9852d7346..cafdfe4d4 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -103,14 +103,24 @@ workflows: # Get repo.ria values from environmental variables, as CodeMagic doesn't have access to repo.ria domain if [[ "${{ inputs.defaultCentralConfigurationTslUrl_input }}" == *"ria.test"* ]]; then echo "Using repo.ria configuration" - echo $REPO_RIA_DEFAULT_PROPERTIES | base64 --decode > $CONFIG_DIR/configuration.properties - echo $REPO_RIA_CONFIG | base64 --decode > $CONFIG_DIR/default-config.json - echo $REPO_RIA_PUB_KEY | base64 --decode > $CONFIG_DIR/default-config.pub - echo $REPO_RIA_RSA | base64 --decode > $CONFIG_DIR/default-config.rsa + echo $REPO_RIA_EC_DEFAULT_PROPERTIES | base64 --decode > $CONFIG_DIR/configuration.properties + echo $REPO_RIA_EC_CONFIG | base64 --decode > $CONFIG_DIR/default-config.json + echo $REPO_RIA_EC_PUB_KEY | base64 --decode > $CONFIG_DIR/default-config.ecpub + echo $REPO_RIA_EC_SIGNATURE | base64 --decode > $CONFIG_DIR/default-config.ecc echo $REPO_RIA_TRUSTED_TEST_MP | base64 --decode > $TSL_FILES_DIR/trusted-test-mp.xml echo $REPO_RIA_TL_MP_TEST_EE | base64 --decode > $TSL_FILES_DIR/tl-mp-test-EE.xml # EE_T.xml is too large to use in environmental variable. Using compressed variant + set -o pipefail echo $REPO_RIA_EE_TSL | base64 --decode | gunzip > $TSL_FILES_DIR/EE_T.xml + for configFile in "$CONFIG_DIR/configuration.properties" "$CONFIG_DIR/default-config.json" \ + "$CONFIG_DIR/default-config.ecpub" "$CONFIG_DIR/default-config.ecc" \ + "$TSL_FILES_DIR/trusted-test-mp.xml" "$TSL_FILES_DIR/tl-mp-test-EE.xml" \ + "$TSL_FILES_DIR/EE_T.xml"; do + if [ ! -s "$configFile" ]; then + echo "ERROR: $configFile is missing or empty" + exit 1 + fi + done else echo "Not using repo.ria configuration" fi diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCacheTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCacheTest.kt index 081d92a89..27ab23111 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCacheTest.kt +++ b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCacheTest.kt @@ -58,12 +58,11 @@ class ConfigurationCacheTest { @Test fun configurationCache_cacheConfigurationFiles_success() { - ConfigurationCache.cacheConfigurationFiles(context, testStringData, testStringData, testByteArrayData) + ConfigurationCache.cacheConfigurationFiles(context, testStringData, testByteArrayData) - assertEquals(3, File(context.cacheDir, CACHE_CONFIG_FOLDER).listFiles()?.size ?: 0) + assertEquals(2, File(context.cacheDir, CACHE_CONFIG_FOLDER).listFiles()?.size ?: 0) assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.json").exists()) - assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.pub").exists()) - assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.rsa").exists()) + assertTrue(File(File(context.cacheDir, CACHE_CONFIG_FOLDER), "active-config.ecc").exists()) } @Test diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModelTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModelTest.kt deleted file mode 100644 index 2ef956dd2..000000000 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModelTest.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName") - -package ee.ria.DigiDoc.configuration.domain.model - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.lifecycle.Observer -import ee.ria.DigiDoc.common.Constant.Defaults.DEFAULT_UUID_VALUE -import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider -import ee.ria.DigiDoc.configuration.repository.ConfigurationRepository -import ee.ria.DigiDoc.network.proxy.ManualProxy -import ee.ria.DigiDoc.network.proxy.ProxySetting -import kotlinx.coroutines.runBlocking -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations -import org.mockito.junit.MockitoJUnitRunner -import java.util.Date -import java.util.UUID - -@RunWith(MockitoJUnitRunner::class) -class ConfigurationViewModelTest { - @get:Rule - val instantTaskExecutorRule = InstantTaskExecutorRule() - - @Mock - private lateinit var repository: ConfigurationRepository - - @Mock - private lateinit var configurationProviderObserver: Observer - - private lateinit var proxySetting: ProxySetting - private lateinit var manualProxy: ManualProxy - - private lateinit var viewModel: ConfigurationViewModel - - @Before - fun setUp() { - MockitoAnnotations.openMocks(this) - viewModel = ConfigurationViewModel(repository) - - proxySetting = ProxySetting.NO_PROXY - manualProxy = ManualProxy("", 80, "", "") - } - - @Test - fun configurationViewModel_getConfiguration_success() = - runBlocking { - `when`(repository.getConfiguration()) - .thenReturn(createMockConfigurationProvider()) - viewModel.configuration.observeForever(configurationProviderObserver) - - val configuration = viewModel.getConfiguration() - - Assert.assertNotNull(configuration) - Assert.assertEquals(1, configuration?.metaInf?.serial) - } - - @Test - fun configurationViewModel_fetchConfiguration_configurationIsFetchedAndChanged() = - runBlocking { - val configurationProvider = createMockConfigurationProvider() - `when`(repository.getCentralConfiguration(proxySetting, manualProxy)) - .thenReturn(configurationProvider) - viewModel.configuration.observeForever(configurationProviderObserver) - - viewModel.fetchConfiguration(0L, proxySetting, manualProxy) - - verify(repository, times(1)).getCentralConfiguration(proxySetting, manualProxy) - verify(configurationProviderObserver, times(1)) - .onChanged(configurationProvider) - } - - @Test - fun configurationViewModel_fetchConfiguration_configurationFetchedButNotChanged() = - runBlocking { - val configurationProvider = createMockConfigurationProvider() - `when`(repository.getCentralConfiguration(proxySetting, manualProxy)) - .thenReturn(configurationProvider) - viewModel.configuration.observeForever(configurationProviderObserver) - - viewModel.fetchConfiguration(Date().time, proxySetting, manualProxy) - - verify(repository, times(1)).getCentralConfiguration(proxySetting, manualProxy) - verify(configurationProviderObserver, never()) - .onChanged(configurationProvider) - } - - private fun createMockConfigurationProvider(): ConfigurationProvider = - ConfigurationProvider( - ConfigurationProvider.MetaInf("url", "date", 1, 1), - "sivaUrl", - mapOf( - DEFAULT_UUID_VALUE to - ConfigurationProvider.CDOC2Conf( - uuid = UUID.randomUUID(), - name = "RIA", - post = "https://cdoc2.id.ee:8443", - fetch = "https://cdoc2.id.ee:8444", - ), - ), - false, - false, - DEFAULT_UUID_VALUE, - "tslUrl", - emptyList(), - "tsaUrl", - "ldapPersonUrl", - ldapPersonUrls = listOf("ldapPersonUrl"), - "ldapCorpUrl", - "midRestUrl", - "midSkRestUrl", - "sidV2RestUrl", - "sidV2SkRestUrl", - emptyList(), - Date(), - Date(), - ) -} diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt index ec0b295e2..ccc9daf51 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt +++ b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt @@ -34,9 +34,8 @@ import ee.ria.DigiDoc.configuration.properties.ConfigurationProperties import ee.ria.DigiDoc.configuration.properties.ConfigurationPropertiesImpl import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepository +import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECC import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_JSON -import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_PUB -import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_RSA import ee.ria.DigiDoc.configuration.utils.Constant.CACHE_CONFIG_FOLDER import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_LAST_UPDATE_CHECK_DATE_PROPERTY_NAME import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_PREFERENCES @@ -111,13 +110,12 @@ class ConfigurationLoaderTest { propertiesFile = AssetFile.getAssetFileAsFile(context, "config/configuration.properties") confFile = AssetFile.getAssetFileAsFile(context, "config/default-config.json") - publicKeyFile = AssetFile.getAssetFileAsFile(context, "config/default-config.pub") - signatureFile = AssetFile.getAssetFileAsFile(context, "config/default-config.rsa") + publicKeyFile = AssetFile.getAssetFileAsFile(context, "config/default-config.ecpub") + signatureFile = AssetFile.getAssetFileAsFile(context, "config/default-config.ecc") File(context.cacheDir, CACHE_CONFIG_FOLDER).mkdirs() Files.copy(confFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_JSON)) - Files.copy(publicKeyFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_PUB)) - Files.copy(signatureFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_RSA)) + Files.copy(signatureFile, File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_ECC)) proxySetting = ProxySetting.NO_PROXY manualProxy = ManualProxy("", 80, "", "") @@ -127,8 +125,7 @@ class ConfigurationLoaderTest { fun tearDown() { File(context.cacheDir, PROPERTIES_FILE_NAME).delete() File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_JSON).delete() - File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_PUB).delete() - File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_RSA).delete() + File(File(context.cacheDir, CACHE_CONFIG_FOLDER), CACHED_CONFIG_ECC).delete() File(context.cacheDir, CACHE_CONFIG_FOLDER).delete() } @@ -191,10 +188,9 @@ class ConfigurationLoaderTest { val confData = mockConfigurationProvider() val centralConfig = gson.toJson(confData) - val centralPublicKey = "Public Key" + val centralPublicKey = publicKeyFile.readText() val centralSignature = "dGVzdA==" `when`(centralConfigurationRepository.fetchConfiguration()).thenReturn(centralConfig) - `when`(centralConfigurationRepository.fetchPublicKey()).thenReturn(centralPublicKey) `when`(centralConfigurationRepository.fetchSignature()).thenReturn(centralSignature) doNothing() @@ -217,7 +213,7 @@ class ConfigurationLoaderTest { // Write random text to file, to make sure that current signature and new signature are different val configFolder = File(context.cacheDir, CACHE_CONFIG_FOLDER) configFolder.mkdirs() - val signatureFile = File(configFolder, CACHED_CONFIG_RSA) + val signatureFile = File(configFolder, CACHED_CONFIG_ECC) FileWriter(signatureFile).use { writer -> writer.write("dGVzdDI=") @@ -237,10 +233,9 @@ class ConfigurationLoaderTest { val confData = mockConfigurationProvider() val centralConfig = gson.toJson(confData) - val centralPublicKey = "Public Key" + val centralPublicKey = publicKeyFile.readText() val centralSignature = "dGVzdA==" `when`(centralConfigurationRepository.fetchConfiguration()).thenReturn(centralConfig) - `when`(centralConfigurationRepository.fetchPublicKey()).thenReturn(centralPublicKey) `when`(centralConfigurationRepository.fetchSignature()).thenReturn(centralSignature) doNothing() @@ -263,7 +258,7 @@ class ConfigurationLoaderTest { // Write same signature to file, to make sure that current signature and new signature match val configFolder = File(context.cacheDir, CACHE_CONFIG_FOLDER) configFolder.mkdirs() - val signatureFile = File(configFolder, CACHED_CONFIG_RSA) + val signatureFile = File(configFolder, CACHED_CONFIG_ECC) FileWriter(signatureFile).use { writer -> writer.write(String(Base64.getDecoder().decode(centralSignature))) } @@ -276,33 +271,6 @@ class ConfigurationLoaderTest { configFolder.delete() } - @Test - fun configurationLoader_loadCentralConfigurationData_success() = - runBlocking { - val confData = mockConfigurationProvider() - - val centralConfig = gson.toJson(confData) - val centralPublicKey = "Public Key" - val centralSignature = "dGVzdA==" - `when`(centralConfigurationRepository.fetchConfiguration()).thenReturn(centralConfig) - `when`(centralConfigurationRepository.fetchPublicKey()).thenReturn(centralPublicKey) - `when`(centralConfigurationRepository.fetchSignature()).thenReturn(centralSignature) - - doNothing() - .`when`(configurationSignatureVerifier) - .verifyConfigurationSignature( - centralConfig, - centralPublicKey, - Base64.getDecoder().decode(centralSignature), - ) - - val configurationData = - configurationLoader - .loadCentralConfigurationData("serviceUri", "Tests") - - assertNotNull(configurationData) - } - @Test fun configurationLoader_shouldCheckForUpdates_returnTrueWhenEnoughDaysPassed() = runBlocking { diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryTest.kt index 8e968b6f0..22db38f95 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryTest.kt +++ b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryTest.kt @@ -57,18 +57,6 @@ class CentralConfigurationRepositoryTest { verify(configurationService).fetchConfiguration() } - @Test - fun centralConfigurationRepository_fetchPublicKey_success(): Unit = - runBlocking { - val expectedPublicKey = "Example Public Key" - `when`(configurationService.fetchPublicKey()).thenReturn(expectedPublicKey) - - val result = centralConfigurationRepository.fetchPublicKey() - - assertEquals(expectedPublicKey, result) - verify(configurationService).fetchPublicKey() - } - @Test fun centralConfigurationRepository_fetchSignature_success(): Unit = runBlocking { diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt index 4be9358ad..71f0401d6 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt +++ b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt @@ -44,7 +44,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner import java.util.UUID @@ -70,7 +69,7 @@ class ConfigurationRepositoryTest { @Before fun setUp() { `when`(configurationLoader.getConfigurationFlow()).thenReturn(configurationFlow) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) Dispatchers.setMain(testDispatcher) proxySetting = ProxySetting.NO_PROXY @@ -103,18 +102,6 @@ class ConfigurationRepositoryTest { assertNull(result) } - @Test - fun configurationRepository_getCentralConfiguration_success() = - runBlocking { - val configurationProvider = mockConfigurationProvider() - configurationFlow.value = configurationProvider - - val result = configurationRepository.getCentralConfiguration(proxySetting, manualProxy) - assertNotNull(result) - assertEquals(configurationProvider, result) - verify(configurationLoader).loadCentralConfiguration(context, proxySetting, manualProxy) - } - @Test fun configurationRepository_observeConfigurationUpdates_success() = runBlocking { diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/ConfigurationSignatureVerifier.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/ConfigurationSignatureVerifier.kt index f69925938..8de9e77b0 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/ConfigurationSignatureVerifier.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/ConfigurationSignatureVerifier.kt @@ -21,9 +21,11 @@ package ee.ria.DigiDoc.configuration +import ee.ria.DigiDoc.configuration.exception.ConfigurationSignatureValidationException import ee.ria.DigiDoc.configuration.utils.SignatureVerifier interface ConfigurationSignatureVerifier { + @Throws(ConfigurationSignatureValidationException::class) fun verifyConfigurationSignature( config: String, publicKey: String, @@ -37,7 +39,15 @@ class ConfigurationSignatureVerifierImpl : ConfigurationSignatureVerifier { publicKey: String, signature: ByteArray, ) { - val signatureValid: Boolean = SignatureVerifier.verify(signature, publicKey, config) - check(signatureValid) { "Configuration signature validation failed!" } + val signatureValid = + try { + SignatureVerifier.verify(signature, publicKey, config) + } catch (e: IllegalStateException) { + throw ConfigurationSignatureValidationException(e) + } + + if (!signatureValid) { + throw ConfigurationSignatureValidationException() + } } } diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCache.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCache.kt index 033e3f848..9d2171b57 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCache.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/cache/ConfigurationCache.kt @@ -22,9 +22,8 @@ package ee.ria.DigiDoc.configuration.cache import android.content.Context +import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECC import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_JSON -import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_PUB -import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_RSA import ee.ria.DigiDoc.configuration.utils.Constant.CACHE_CONFIG_FOLDER import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil import java.io.File @@ -39,12 +38,10 @@ object ConfigurationCache { fun cacheConfigurationFiles( context: Context, confData: String, - publicKey: String, signature: ByteArray, ) { cacheFile(context, CACHED_CONFIG_JSON, confData) - cacheFile(context, CACHED_CONFIG_PUB, publicKey) - cacheFile(context, CACHED_CONFIG_RSA, signature) + cacheFile(context, CACHED_CONFIG_ECC, signature) } private fun cacheFile( @@ -71,6 +68,7 @@ object ConfigurationCache { } } + @Suppress("SameParameterValue") private fun cacheFile( context: Context, fileName: String, diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt index 2a399a0f2..4ad0035c5 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt @@ -21,12 +21,10 @@ package ee.ria.DigiDoc.configuration.di -import android.content.Context import com.google.gson.Gson import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import ee.ria.DigiDoc.configuration.ConfigurationProperty import ee.ria.DigiDoc.configuration.ConfigurationSignatureVerifier @@ -71,10 +69,8 @@ class ConfigModules { fun provideConfigurationSignatureVerifier(): ConfigurationSignatureVerifier = ConfigurationSignatureVerifierImpl() @Provides - fun provideConfigurationRepository( - @ApplicationContext context: Context, - configurationLoader: ConfigurationLoader, - ): ConfigurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + fun provideConfigurationRepository(configurationLoader: ConfigurationLoader): ConfigurationRepository = + ConfigurationRepositoryImpl(configurationLoader) @Provides fun provideCentralConfigurationRepository( diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModel.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModel.kt deleted file mode 100644 index 51f8c2a10..000000000 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModel.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName") - -package ee.ria.DigiDoc.configuration.domain.model - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider -import ee.ria.DigiDoc.configuration.repository.ConfigurationRepository -import ee.ria.DigiDoc.network.proxy.ManualProxy -import ee.ria.DigiDoc.network.proxy.ProxySetting -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.withContext -import java.util.Date -import javax.inject.Inject - -@HiltViewModel -class ConfigurationViewModel - @Inject - constructor( - private val repository: ConfigurationRepository, - ) : ViewModel() { - private val _configuration = MutableLiveData() - val configuration: MutableLiveData = _configuration - - suspend fun fetchConfiguration( - lastUpdate: Long, - proxySetting: ProxySetting?, - manualProxy: ManualProxy, - ) { - withContext(IO) { - try { - val configurationProvider = repository.getCentralConfiguration(proxySetting, manualProxy) - val confUpdateDate = configurationProvider?.configurationUpdateDate - if (lastUpdate == 0L || (confUpdateDate != null && confUpdateDate.after(Date(lastUpdate)))) { - _configuration.postValue(configurationProvider) - } - } catch (_: Exception) { - // Handle error - } - } - } - - fun getConfiguration(): ConfigurationProvider? = repository.getConfiguration() - } diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/exception/ConfigurationSignatureValidationException.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/exception/ConfigurationSignatureValidationException.kt new file mode 100644 index 000000000..23a44a54f --- /dev/null +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/exception/ConfigurationSignatureValidationException.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.configuration.exception + +class ConfigurationSignatureValidationException( + cause: Throwable? = null, +) : Exception("Configuration signature validation failed", cause) diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/exception/PublicKeyNotFoundException.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/exception/PublicKeyNotFoundException.kt new file mode 100644 index 000000000..f4ef05698 --- /dev/null +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/exception/PublicKeyNotFoundException.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.configuration.exception + +class PublicKeyNotFoundException( + message: String, + cause: Throwable? = null, +) : Exception(message, cause) diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoader.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoader.kt index 9a33a04e0..83663879e 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoader.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoader.kt @@ -23,13 +23,13 @@ package ee.ria.DigiDoc.configuration.loader import android.content.Context import ee.ria.DigiDoc.configuration.ConfigurationProperty -import ee.ria.DigiDoc.configuration.domain.model.ConfigurationData import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider import ee.ria.DigiDoc.network.proxy.ManualProxy import ee.ria.DigiDoc.network.proxy.ProxySetting import kotlinx.coroutines.flow.StateFlow interface ConfigurationLoader { + @Throws(Exception::class) suspend fun initConfiguration( context: Context, proxySetting: ProxySetting?, @@ -45,13 +45,9 @@ interface ConfigurationLoader { afterCentralCheck: Boolean, ) - suspend fun loadDefaultConfiguration(context: Context) + suspend fun loadLocalConfiguration(context: Context) - @Throws(Exception::class) - suspend fun loadCentralConfigurationData( - configurationServiceUrl: String, - userAgent: String, - ): ConfigurationData + suspend fun loadDefaultConfiguration(context: Context) @Throws(Exception::class) suspend fun loadCentralConfiguration( 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 625ec2c0b..dd3f7a084 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 @@ -26,28 +26,30 @@ import com.google.gson.Gson import ee.ria.DigiDoc.configuration.ConfigurationProperty import ee.ria.DigiDoc.configuration.ConfigurationSignatureVerifier import ee.ria.DigiDoc.configuration.cache.ConfigurationCache -import ee.ria.DigiDoc.configuration.domain.model.ConfigurationData +import ee.ria.DigiDoc.configuration.exception.ConfigurationSignatureValidationException +import ee.ria.DigiDoc.configuration.exception.PublicKeyNotFoundException import ee.ria.DigiDoc.configuration.properties.ConfigurationProperties import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepository import ee.ria.DigiDoc.configuration.utils.ConfigurationUtil +import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_ECC import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_JSON -import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_PUB -import ee.ria.DigiDoc.configuration.utils.Constant.CACHED_CONFIG_RSA import ee.ria.DigiDoc.configuration.utils.Constant.CACHE_CONFIG_FOLDER +import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECC +import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECPUB import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_JSON -import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_PUB -import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_RSA 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.CancellationException 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.io.IOException import java.time.LocalDateTime import java.time.ZoneId import java.time.temporal.ChronoUnit @@ -89,33 +91,19 @@ class ConfigurationLoaderImpl if (shouldCheckForUpdates(context)) { try { loadCentralConfigurationCore(context, proxySetting, manualProxy) + } catch (e: ConfigurationSignatureValidationException) { + errorLog(logTag, "Central configuration signature validation failed", e) + restoreLocalConfiguration(context) + throw e + } catch (ce: CancellationException) { + throw ce } catch (e: Exception) { errorLog(logTag, "Unable to check Central configuration. Using current configuration", e) + restoreLocalConfiguration(context) } } } - // 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 - } - } - override fun getConfigurationFlow(): StateFlow = configurationFlow override suspend fun loadConfigurationProperty(context: Context): ConfigurationProperty { @@ -133,22 +121,21 @@ class ConfigurationLoaderImpl ) { 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) + val signatureFile = File(cacheDir, CACHED_CONFIG_ECC) - val isCacheComplete = confFile.exists() && publicKeyFile.exists() && signatureFile.exists() + val isCacheComplete = confFile.exists() && signatureFile.exists() if (!isCacheComplete) { loadDefaultConfiguration(context) return } val configText = confFile.readText() - val publicKey = publicKeyFile.readText() + val publicKey = bundledPublicKey(context) val signature = decodeSignature(signatureFile.readBytes()) configurationSignatureVerifier.verifyConfigurationSignature(configText, publicKey, signature) val configurationProvider = gson.fromJson(configText, ConfigurationProvider::class.java) - ConfigurationCache.cacheConfigurationFiles(context, configText, publicKey, signature) + ConfigurationCache.cacheConfigurationFiles(context, configText, signature) if (!afterCentralCheck) { configurationProperties.updateProperties( @@ -172,25 +159,35 @@ class ConfigurationLoaderImpl } } + // Load default configuration if cached configuration does not succeed + override 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) + } + } + override suspend fun loadDefaultConfiguration(context: Context) { val assets = context.assets 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 signature = decodeSignature(assets.open("config/${DEFAULT_CONFIG_RSA}").readBytes()) + val publicKey = bundledPublicKey(context) + val signature = + decodeSignature(assets.open("config/${DEFAULT_CONFIG_ECC}").use { it.readBytes() }) configurationSignatureVerifier.verifyConfigurationSignature(confData, publicKey, signature) ConfigurationCache.cacheConfigurationFiles( context, confData, - publicKey, signature, ) val configurationProvider = gson.fromJson(confData, ConfigurationProvider::class.java) configurationProperties.updateProperties( context, - DateUtil.getConfigurationDate(configurationProvider.metaInf.date), + null, DateUtil.getConfigurationDate(configurationProvider.metaInf.date), configurationProvider.metaInf.serial, ) @@ -201,25 +198,6 @@ class ConfigurationLoaderImpl configurationFlow.value = configurationProvider } - @Throws(Exception::class) - override suspend fun loadCentralConfigurationData( - configurationServiceUrl: String, - userAgent: String, - ): ConfigurationData { - val centralSignature = - Base64.getDecoder().decode(centralConfigurationRepository.fetchSignature().trim()) - val centralConfig = centralConfigurationRepository.fetchConfiguration() - val centralPublicKey = centralConfigurationRepository.fetchPublicKey() - - configurationSignatureVerifier.verifyConfigurationSignature( - centralConfig, - centralPublicKey, - centralSignature, - ) - - return ConfigurationData(centralConfig, centralPublicKey, centralSignature) - } - @Throws(Exception::class) override suspend fun loadCentralConfiguration( context: Context, @@ -229,6 +207,64 @@ class ConfigurationLoaderImpl loadCentralConfigurationCore(context, proxySetting, proxy) } + override suspend fun shouldCheckForUpdates(context: Context): Boolean { + val lastExecutionDate = + configurationProperties + .getConfigurationLastCheckDate(context) + ?.toInstant() + ?.atZone(ZoneId.systemDefault()) + ?.toLocalDateTime() + + if (lastExecutionDate == null) { + return true + } + + val currentDate = LocalDateTime.now() + + val daysSinceLastUpdateCheck = ChronoUnit.DAYS.between(lastExecutionDate, currentDate) + + return daysSinceLastUpdateCheck >= 4 + } + + private suspend fun restoreLocalConfiguration(context: Context) { + try { + loadLocalConfiguration(context) + } catch (recoveryFailure: Exception) { + errorLog(logTag, "Unable to restore local configuration", recoveryFailure) + } + } + + private fun getConfigCacheDir(context: Context): File = File(context.cacheDir, CACHE_CONFIG_FOLDER) + + private fun bundledPublicKey(context: Context): String { + val publicKey = + try { + context.assets + .open("config/${DEFAULT_CONFIG_ECPUB}") + .bufferedReader() + .use { it.readText() } + } catch (e: IOException) { + throw PublicKeyNotFoundException("Bundled ${DEFAULT_CONFIG_ECPUB} is missing", e) + } + + return publicKey.ifBlank { + throw PublicKeyNotFoundException("Bundled ${DEFAULT_CONFIG_ECPUB} is empty") + } + } + + private fun decodeSignature(signatureBytes: ByteArray): ByteArray { + val signatureText = String(signatureBytes, Charsets.UTF_8).removeWhitespaces().trim() + if (signatureText.isEmpty()) { + return signatureBytes + } + + return if (ConfigurationUtil.isBase64(signatureText)) { + Base64.getDecoder().decode(signatureText) + } else { + signatureBytes + } + } + @Throws(Exception::class) private suspend fun loadCentralConfigurationCore( context: Context, @@ -236,7 +272,7 @@ class ConfigurationLoaderImpl proxy: ManualProxy, ) { val cachedSignature = - ConfigurationCache.getCachedFile(context, CACHED_CONFIG_RSA) + ConfigurationCache.getCachedFile(context, CACHED_CONFIG_ECC) val currentSignature = cachedSignature.readBytes() @@ -245,16 +281,17 @@ class ConfigurationLoaderImpl centralConfigurationRepository.setupProxy(proxySetting, proxy) val centralSignature = - Base64.getDecoder().decode( - centralConfigurationRepository.fetchSignature().removeWhitespaces().trim(), - ) + try { + Base64.getDecoder().decode( + centralConfigurationRepository.fetchSignature().removeWhitespaces().trim(), + ) + } catch (e: IllegalArgumentException) { + throw ConfigurationSignatureValidationException(e) + } if (!currentSignature.contentEquals(centralSignature)) { val centralConfig = centralConfigurationRepository.fetchConfiguration() - val centralPublicKey = centralConfigurationRepository.fetchPublicKey() - - val centralConfigurationProvider = - gson.fromJson(centralConfig, ConfigurationProvider::class.java) + val centralPublicKey = bundledPublicKey(context) configurationSignatureVerifier.verifyConfigurationSignature( centralConfig, @@ -262,6 +299,9 @@ class ConfigurationLoaderImpl centralSignature, ) + val centralConfigurationProvider = + gson.fromJson(centralConfig, ConfigurationProvider::class.java) + if (ConfigurationUtil.isSerialNewerThanCached( configurationFlow.value?.metaInf?.serial ?: 0, centralConfigurationProvider.metaInf.serial, @@ -270,7 +310,6 @@ class ConfigurationLoaderImpl ConfigurationCache.cacheConfigurationFiles( context, centralConfig, - centralPublicKey, centralSignature, ) configurationProperties.updateProperties( @@ -299,23 +338,4 @@ class ConfigurationLoaderImpl loadCachedConfiguration(context, true) } } - - override suspend fun shouldCheckForUpdates(context: Context): Boolean { - val lastExecutionDate = - configurationProperties - .getConfigurationLastCheckDate(context) - ?.toInstant() - ?.atZone(ZoneId.systemDefault()) - ?.toLocalDateTime() - - if (lastExecutionDate == null) { - return true - } - - val currentDate = LocalDateTime.now() - - val daysSinceLastUpdateCheck = ChronoUnit.DAYS.between(lastExecutionDate, currentDate) - - return daysSinceLastUpdateCheck >= 4 - } } diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepository.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepository.kt index ad86acd03..65de00cf0 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepository.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepository.kt @@ -31,11 +31,7 @@ interface CentralConfigurationRepository { suspend fun fetchConfiguration(): String @Throws(Exception::class) - @GET("config.pub") - suspend fun fetchPublicKey(): String - - @Throws(Exception::class) - @GET("config.rsa") + @GET("config.ecc") suspend fun fetchSignature(): String suspend fun setupProxy( diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryImpl.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryImpl.kt index cda0497d3..4132a222c 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryImpl.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/repository/CentralConfigurationRepositoryImpl.kt @@ -35,8 +35,6 @@ class CentralConfigurationRepositoryImpl ) : CentralConfigurationRepository { override suspend fun fetchConfiguration(): String = configurationService.fetchConfiguration() - override suspend fun fetchPublicKey(): String = configurationService.fetchPublicKey() - @Throws(Exception::class) override suspend fun fetchSignature(): String = configurationService.fetchSignature() 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 10ba075f3..95adfbcb1 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 @@ -22,17 +22,9 @@ package ee.ria.DigiDoc.configuration.repository import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider -import ee.ria.DigiDoc.network.proxy.ManualProxy -import ee.ria.DigiDoc.network.proxy.ProxySetting interface ConfigurationRepository { fun getConfiguration(): ConfigurationProvider? - @Throws(Exception::class) - suspend fun getCentralConfiguration( - proxySetting: ProxySetting?, - manualProxy: ManualProxy, - ): ConfigurationProvider? - 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 0efcf12e9..a5121e186 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 @@ -21,11 +21,8 @@ package ee.ria.DigiDoc.configuration.repository -import android.content.Context import ee.ria.DigiDoc.configuration.loader.ConfigurationLoader import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider -import ee.ria.DigiDoc.network.proxy.ManualProxy -import ee.ria.DigiDoc.network.proxy.ProxySetting import kotlinx.coroutines.flow.filterNotNull import javax.inject.Inject import javax.inject.Singleton @@ -34,20 +31,10 @@ import javax.inject.Singleton class ConfigurationRepositoryImpl @Inject constructor( - private val context: Context, private val configurationLoader: ConfigurationLoader, ) : ConfigurationRepository { override fun getConfiguration(): ConfigurationProvider? = configurationLoader.getConfigurationFlow().value - @Throws(Exception::class) - override suspend fun getCentralConfiguration( - proxySetting: ProxySetting?, - manualProxy: ManualProxy, - ): ConfigurationProvider? { - configurationLoader.loadCentralConfiguration(context, proxySetting, manualProxy) - return getConfiguration() - } - override suspend fun observeConfigurationUpdates(onUpdate: suspend (ConfigurationProvider) -> Unit) { configurationLoader .getConfigurationFlow() diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt index 2f965cb19..8e5958747 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt @@ -48,8 +48,6 @@ import javax.inject.Singleton interface CentralConfigurationService { suspend fun fetchConfiguration(): String - suspend fun fetchPublicKey(): String - suspend fun fetchSignature(): String suspend fun setupProxy( @@ -90,16 +88,6 @@ open class CentralConfigurationServiceImpl return retrofit.fetchConfiguration() } - override suspend fun fetchPublicKey(): String { - val retrofit = - constructRetrofit( - configurationProperty.centralConfigurationServiceUrl, - constructHttpClient(defaultTimeout, proxySetting, manualProxy), - ).create(CentralConfigurationRepository::class.java) - - return retrofit.fetchPublicKey() - } - @Throws(Exception::class) override suspend fun fetchSignature(): String { val retrofit = diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/task/FetchAndPackageDefaultConfigurationTask.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/task/FetchAndPackageDefaultConfigurationTask.kt index 2a0e66223..f013fa33f 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/task/FetchAndPackageDefaultConfigurationTask.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/task/FetchAndPackageDefaultConfigurationTask.kt @@ -30,9 +30,9 @@ import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_DOWNLOAD_DATE_P import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_UPDATE_INTERVAL_PROPERTY import ee.ria.DigiDoc.configuration.utils.Constant.CONFIGURATION_VERSION_SERIAL_PROPERTY import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIGURATION_PROPERTIES_FILE_NAME +import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECC +import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_ECPUB import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_JSON -import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_PUB -import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_CONFIG_RSA import ee.ria.DigiDoc.configuration.utils.Constant.DEFAULT_UPDATE_INTERVAL import ee.ria.DigiDoc.configuration.utils.Constant.PROPERTIES_FILE_NAME import ee.ria.DigiDoc.configuration.utils.Parser @@ -44,6 +44,7 @@ import ee.ria.DigiDoc.utilsLib.file.FileUtil import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog import kotlinx.coroutines.runBlocking import okhttp3.OkHttpClient +import okhttp3.Request import okhttp3.internal.tls.OkHostnameVerifier import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit @@ -80,7 +81,7 @@ object FetchAndPackageDefaultConfigurationTask { OkHttpClient .Builder() .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) - .addInterceptor(UserAgentInterceptor("Jenkins")) + .addInterceptor(UserAgentInterceptor("Codemagic")) .addInterceptor(NetworkInterceptor()) .hostnameVerifier(OkHostnameVerifier) .connectTimeout(defaultTimeout, TimeUnit.SECONDS) @@ -100,10 +101,17 @@ object FetchAndPackageDefaultConfigurationTask { val centralConfigurationRepository = retrofit.create(CentralConfigurationRepository::class.java) + val publicKeyRequest = Request.Builder().url("$configurationServiceUrl/config.ecpub").build() + val publicKey = + okHttpClient.newCall(publicKeyRequest).execute().use { response -> + check(response.isSuccessful) { "Unable to fetch config.ecpub: ${response.code}" } + response.body.string() + } + val configurationData = ConfigurationData( centralConfigurationRepository.fetchConfiguration(), - centralConfigurationRepository.fetchPublicKey(), + publicKey, Base64.getDecoder().decode(centralConfigurationRepository.fetchSignature().trim().removeWhitespaces()), ) @@ -115,8 +123,8 @@ object FetchAndPackageDefaultConfigurationTask { confServiceUrl: String, args: Array, ) { - verifyConfigurationSignature(confData) assertConfigurationLoaded(confData) + verifyConfigurationSignature(confData) storeAsDefaultConfiguration(confData) val configurationParser = Parser(confData.configurationJson) val confVersionSerial: Int = configurationParser.parseIntValue("META-INF", "SERIAL") @@ -168,9 +176,10 @@ object FetchAndPackageDefaultConfigurationTask { private fun assertConfigurationLoaded(confData: ConfigurationData) { check( !( - confData.configurationJson == null || + confData.configurationJson.isNullOrBlank() || confData.configurationSignature == null || - confData.configurationSignaturePublicKey == null + confData.configurationSignature.isEmpty() || + confData.configurationSignaturePublicKey.isNullOrBlank() ), ) { "Configuration loading has failed" @@ -179,8 +188,8 @@ object FetchAndPackageDefaultConfigurationTask { private fun storeAsDefaultConfiguration(confData: ConfigurationData) { confData.configurationJson?.let { storeFile(DEFAULT_CONFIG_JSON, it) } - confData.configurationSignature?.let { storeFile(DEFAULT_CONFIG_RSA, it) } - confData.configurationSignaturePublicKey?.let { storeFile(DEFAULT_CONFIG_PUB, it) } + confData.configurationSignature?.let { storeFile(DEFAULT_CONFIG_ECC, it) } + confData.configurationSignaturePublicKey?.let { storeFile(DEFAULT_CONFIG_ECPUB, it) } } private fun storeApplicationProperties( diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/Constant.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/Constant.kt index 4c31b6b2f..1526e8fcb 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/Constant.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/Constant.kt @@ -22,10 +22,6 @@ package ee.ria.DigiDoc.configuration.utils object Constant { - const val FORCE_LOAD_CENTRAL_CONFIGURATION = - "ee.ria.digidoc.configuration.FORCE_LOAD_CENTRAL_CONFIGURATION" - const val LAST_CONFIGURATION_UPDATE = "ee.ria.digidoc.configuration.LAST_CONFIGURATION_UPDATE" - const val CENTRAL_CONFIGURATION_SERVICE_URL_PROPERTY = "central-configuration-service.url" const val CONFIGURATION_UPDATE_INTERVAL_PROPERTY = "configuration.update-interval" const val CONFIGURATION_VERSION_SERIAL_PROPERTY = "configuration.version-serial" @@ -38,16 +34,14 @@ object Constant { "default-configuration.properties" const val DEFAULT_CONFIG_JSON = "default-config.json" - const val DEFAULT_CONFIG_RSA = "default-config.rsa" - const val DEFAULT_CONFIG_PUB = "default-config.pub" + const val DEFAULT_CONFIG_ECC = "default-config.ecc" + const val DEFAULT_CONFIG_ECPUB = "default-config.ecpub" const val CACHED_CONFIG_JSON = "active-config.json" - const val CACHED_CONFIG_RSA = "active-config.rsa" - const val CACHED_CONFIG_PUB = "active-config.pub" + const val CACHED_CONFIG_ECC = "active-config.ecc" const val CONFIGURATION_PREFERENCES = "ConfigurationPreferences" const val CACHE_CONFIG_FOLDER = "/config/" - const val CONFIGURATION_INFO_FILE_NAME = "configuration-info.properties" const val CONFIGURATION_LAST_UPDATE_CHECK_DATE_PROPERTY_NAME = "configuration.last-update-check-date" const val CONFIGURATION_UPDATE_DATE_PROPERTY_NAME = "configuration.update-date" diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifier.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifier.kt index 663a0a537..5129865b7 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifier.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifier.kt @@ -23,69 +23,59 @@ package ee.ria.DigiDoc.configuration.utils import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -import org.bouncycastle.crypto.params.RSAKeyParameters -import org.bouncycastle.crypto.util.PublicKeyFactory import org.bouncycastle.openssl.PEMParser import java.io.IOException import java.io.StringReader import java.nio.charset.StandardCharsets -import java.security.InvalidKeyException +import java.security.GeneralSecurityException import java.security.KeyFactory -import java.security.NoSuchAlgorithmException import java.security.PublicKey import java.security.Signature -import java.security.SignatureException -import java.security.spec.InvalidKeySpecException -import java.security.spec.RSAPublicKeySpec +import java.security.interfaces.ECPublicKey +import java.security.spec.X509EncodedKeySpec object SignatureVerifier { private val LOG_TAG = javaClass.simpleName + private const val CONVERSION_ERROR = "Failed to convert SubjectPublicKeyInfo to EC java.security.PublicKey" + private const val PARSE_ERROR = "Failed to parse PEM encoded public key" + private const val NOT_SPKI_ERROR = + "PEM did not contain SubjectPublicKeyInfo. Make sure it's 'BEGIN PUBLIC KEY' (SPKI)." + private const val VERIFY_ERROR = "Failed to verify signature" + fun verify( signature: ByteArray, publicKeyPEM: String, signedContent: String, ): Boolean { - val publicKeyInfo: SubjectPublicKeyInfo = - parsePublicKeyInfo(publicKeyPEM) + val publicKeyInfo = parsePublicKeyInfo(publicKeyPEM) val publicKey = convertPublicKeyInfoToPublicKey(publicKeyInfo) return verifySignature(signature, publicKey, signedContent) } private fun convertPublicKeyInfoToPublicKey(publicKeyInfo: SubjectPublicKeyInfo): PublicKey = try { - val keyParams: RSAKeyParameters = - PublicKeyFactory.createKey(publicKeyInfo) as RSAKeyParameters - val publicKeySpec = RSAPublicKeySpec(keyParams.modulus, keyParams.exponent) - val keyFactory = KeyFactory.getInstance("RSA") - keyFactory.generatePublic(publicKeySpec) - } catch (e: InvalidKeySpecException) { - errorLog(LOG_TAG, "PublicKey conversion failed", e) - throw IllegalStateException( - "Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey", - e, - ) - } catch (e: NoSuchAlgorithmException) { - errorLog(LOG_TAG, "PublicKey conversion failed", e) - throw IllegalStateException( - "Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey", - e, - ) + val keySpec = X509EncodedKeySpec(publicKeyInfo.encoded) + KeyFactory.getInstance("EC").generatePublic(keySpec) + } catch (e: GeneralSecurityException) { + fail(CONVERSION_ERROR, e) } catch (e: IOException) { - errorLog(LOG_TAG, "PublicKey conversion failed", e) - throw IllegalStateException( - "Failed to convert org.bouncycastle.asn1.x509.SubjectPublicKeyInfo to kotlin.security.PublicKey", - e, - ) + fail(CONVERSION_ERROR, e) + } catch (e: RuntimeException) { + fail(CONVERSION_ERROR, e) } - private fun parsePublicKeyInfo(PKCS1PublicKeyPEM: String): SubjectPublicKeyInfo { - try { - PEMParser(StringReader(PKCS1PublicKeyPEM)) - .use { pemParser -> return pemParser.readObject() as SubjectPublicKeyInfo } - } catch (e: IOException) { - throw IllegalStateException("Failed to parse PEM encoded PKCS#1 public key", e) - } + private fun parsePublicKeyInfo(publicKeyPem: String): SubjectPublicKeyInfo { + val pemObject = + try { + PEMParser(StringReader(publicKeyPem)).use { it.readObject() } + } catch (e: IOException) { + fail(PARSE_ERROR, e) + } catch (e: RuntimeException) { + fail(PARSE_ERROR, e) + } + + return pemObject as? SubjectPublicKeyInfo ?: fail(NOT_SPKI_ERROR) } private fun verifySignature( @@ -94,18 +84,35 @@ object SignatureVerifier { signedContent: String, ): Boolean = try { - val signature = Signature.getInstance("SHA512withRSA") + val signature = Signature.getInstance(pickEcdsaAlgorithm(publicKey)) signature.initVerify(publicKey) signature.update(signedContent.toByteArray(StandardCharsets.UTF_8)) signature.verify(signatureBytes) - } catch (e: NoSuchAlgorithmException) { - errorLog(LOG_TAG, "Signature verification failed", e) - throw IllegalStateException("Failed to verify signature", e) - } catch (e: SignatureException) { - errorLog(LOG_TAG, "Signature verification failed", e) - throw IllegalStateException("Failed to verify signature", e) - } catch (e: InvalidKeyException) { - errorLog(LOG_TAG, "Signature verification failed", e) - throw IllegalStateException("Failed to verify signature", e) + } catch (e: GeneralSecurityException) { + fail(VERIFY_ERROR, e) + } catch (e: IllegalArgumentException) { + fail(VERIFY_ERROR, e) + } + + private fun fail( + message: String, + cause: Throwable? = null, + ): Nothing { + errorLog(LOG_TAG, message, cause) + throw IllegalStateException(message, cause) + } + + private fun pickEcdsaAlgorithm(publicKey: PublicKey): String { + val ecKey = + publicKey as? ECPublicKey + ?: fail("Public key is not EC (ECDSA). Got: ${publicKey.algorithm}") + + val fieldSizeBits = ecKey.params.curve.field.fieldSize + + return when { + fieldSizeBits <= 256 -> "SHA256withECDSA" + fieldSizeBits <= 384 -> "SHA384withECDSA" + else -> "SHA512withECDSA" } + } } diff --git a/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/ConfigurationSignatureVerifierImplTest.kt b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/ConfigurationSignatureVerifierImplTest.kt new file mode 100644 index 000000000..7394d763b --- /dev/null +++ b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/ConfigurationSignatureVerifierImplTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.configuration + +import ee.ria.DigiDoc.configuration.exception.ConfigurationSignatureValidationException +import ee.ria.DigiDoc.configuration.shared.TestConfigurationFiles +import org.junit.Assert.assertThrows +import org.junit.Test + +class ConfigurationSignatureVerifierImplTest { + private val verifier = ConfigurationSignatureVerifierImpl() + + @Test + fun verifyConfigurationSignatureSucceedsForMatchingSignature() { + verifier.verifyConfigurationSignature(config(), publicKey(), signature()) + } + + @Test + fun verifyConfigurationSignatureThrowsValidationExceptionWhenConfigTampered() { + assertThrows(ConfigurationSignatureValidationException::class.java) { + verifier.verifyConfigurationSignature(config() + "a", publicKey(), signature()) + } + } + + @Test + fun verifyConfigurationSignatureThrowsValidationExceptionWhenSignatureCorrupted() { + val corrupted = signature().copyOf() + corrupted[0] = (corrupted[0] + 1).toByte() + + assertThrows(ConfigurationSignatureValidationException::class.java) { + verifier.verifyConfigurationSignature(config(), publicKey(), corrupted) + } + } + + @Test + fun verifyConfigurationSignatureThrowsValidationExceptionWhenSignatureEmpty() { + assertThrows(ConfigurationSignatureValidationException::class.java) { + verifier.verifyConfigurationSignature(config(), publicKey(), ByteArray(0)) + } + } + + @Test + fun verifyConfigurationSignatureThrowsValidationExceptionWhenPublicKeyMalformed() { + assertThrows(ConfigurationSignatureValidationException::class.java) { + verifier.verifyConfigurationSignature(config(), "not a pem", signature()) + } + } + + private fun config(): String = TestConfigurationFiles.config() + + private fun publicKey(): String = TestConfigurationFiles.publicKey() + + private fun signature(): ByteArray = TestConfigurationFiles.signature() +} diff --git a/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderImplTest.kt b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderImplTest.kt new file mode 100644 index 000000000..f5dbf0be4 --- /dev/null +++ b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderImplTest.kt @@ -0,0 +1,176 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.configuration.loader + +import android.content.Context +import android.content.res.AssetManager +import com.google.gson.Gson +import ee.ria.DigiDoc.configuration.ConfigurationProperty +import ee.ria.DigiDoc.configuration.ConfigurationSignatureVerifier +import ee.ria.DigiDoc.configuration.exception.PublicKeyNotFoundException +import ee.ria.DigiDoc.configuration.properties.ConfigurationProperties +import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepository +import ee.ria.DigiDoc.network.proxy.ManualProxy +import ee.ria.DigiDoc.network.proxy.ProxySetting +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertThrows +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` +import org.mockito.kotlin.any +import org.mockito.kotlin.check +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify +import java.io.ByteArrayInputStream +import java.io.File +import java.io.FileNotFoundException +import java.nio.file.Files +import java.time.LocalDateTime + +class ConfigurationLoaderImplTest { + private lateinit var context: Context + private lateinit var assets: AssetManager + private lateinit var centralConfigurationRepository: CentralConfigurationRepository + private lateinit var configurationSignatureVerifier: ConfigurationSignatureVerifier + private lateinit var configurationProperties: ConfigurationProperties + private lateinit var configurationLoader: ConfigurationLoader + + @Before + fun setUp() { + assets = mock(AssetManager::class.java) + context = mock(Context::class.java) + `when`(context.assets).thenReturn(assets) + `when`(assets.open("config/default-config.json")) + .thenReturn(ByteArrayInputStream("{}".toByteArray())) + + centralConfigurationRepository = mock(CentralConfigurationRepository::class.java) + configurationSignatureVerifier = mock(ConfigurationSignatureVerifier::class.java) + configurationProperties = mock(ConfigurationProperties::class.java) + + configurationLoader = + ConfigurationLoaderImpl( + Gson(), + centralConfigurationRepository, + ConfigurationProperty(), + configurationProperties, + configurationSignatureVerifier, + ) + } + + @Test + fun configurationLoader_loadDefaultConfiguration_decodesBase64SignatureCarryingTrailingNewline() { + val bundledKey = "-----BEGIN PUBLIC KEY-----\nBUNDLED\n-----END PUBLIC KEY-----\n" + `when`(assets.open("config/default-config.ecpub")) + .thenReturn(ByteArrayInputStream(bundledKey.toByteArray())) + `when`(assets.open("config/default-config.ecc")) + .thenReturn(ByteArrayInputStream("dGVzdA==\n".toByteArray())) + + try { + runBlocking { configurationLoader.loadDefaultConfiguration(context) } + } catch (_: Exception) { + } + + verify(configurationSignatureVerifier).verifyConfigurationSignature( + any(), + any(), + check { assertArrayEquals("test".toByteArray(), it) }, + ) + } + + @Test + fun configurationLoader_loadCentralConfiguration_verifiesWithBundledKeyNotAServerSuppliedOne() { + val bundledKey = "-----BEGIN PUBLIC KEY-----\nBUNDLED\n-----END PUBLIC KEY-----\n" + val cacheDir = Files.createTempDirectory("loaderCentralTest").toFile() + File(cacheDir, "config").mkdirs() + File(cacheDir, "config/active-config.ecc").writeBytes("stale".toByteArray()) + + `when`(context.cacheDir).thenReturn(cacheDir) + `when`(assets.open("config/default-config.ecpub")) + .thenReturn(ByteArrayInputStream(bundledKey.toByteArray())) + `when`(configurationProperties.getConfigurationProperties(context)) + .thenReturn(ConfigurationProperty("https://example.org", 4, 1, LocalDateTime.now())) + + try { + runBlocking { + `when`(centralConfigurationRepository.fetchSignature()).thenReturn("dGVzdA==") + `when`(centralConfigurationRepository.fetchConfiguration()).thenReturn("{}") + + configurationLoader.loadCentralConfiguration( + context, + ProxySetting.NO_PROXY, + ManualProxy("", 80, "", ""), + ) + } + } catch (_: Exception) { + } + + verify(configurationSignatureVerifier).verifyConfigurationSignature(any(), eq(bundledKey), any()) + } + + @Test + fun configurationLoader_loadDefaultConfiguration_throwsWhenBundledPublicKeyIsEmpty() { + `when`(assets.open("config/default-config.ecpub")) + .thenReturn(ByteArrayInputStream(ByteArray(0))) + + assertThrows(PublicKeyNotFoundException::class.java) { + runBlocking { configurationLoader.loadDefaultConfiguration(context) } + } + } + + @Test + fun configurationLoader_loadDefaultConfiguration_throwsWhenBundledPublicKeyIsBlank() { + `when`(assets.open("config/default-config.ecpub")) + .thenReturn(ByteArrayInputStream(" \n".toByteArray())) + + assertThrows(PublicKeyNotFoundException::class.java) { + runBlocking { configurationLoader.loadDefaultConfiguration(context) } + } + } + + @Test + fun configurationLoader_loadDefaultConfiguration_throwsWhenBundledPublicKeyIsMissing() { + `when`(assets.open("config/default-config.ecpub")) + .thenThrow(FileNotFoundException("config/default-config.ecpub")) + + assertThrows(PublicKeyNotFoundException::class.java) { + runBlocking { configurationLoader.loadDefaultConfiguration(context) } + } + } + + @Test + fun configurationLoader_loadDefaultConfiguration_verifiesWithBundledKeyAndNeverTheServerKey() { + val bundledKey = "-----BEGIN PUBLIC KEY-----\nBUNDLED\n-----END PUBLIC KEY-----\n" + `when`(assets.open("config/default-config.ecpub")) + .thenReturn(ByteArrayInputStream(bundledKey.toByteArray())) + `when`(assets.open("config/default-config.ecc")) + .thenReturn(ByteArrayInputStream(byteArrayOf(1, 2, 3))) + + try { + runBlocking { configurationLoader.loadDefaultConfiguration(context) } + } catch (_: Exception) { + } + + verify(configurationSignatureVerifier).verifyConfigurationSignature(any(), eq(bundledKey), any()) + } +} diff --git a/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt index cf9a8b6d9..b10f1b0f9 100644 --- a/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt +++ b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt @@ -149,17 +149,6 @@ class CentralConfigurationServiceImplTest { assertEquals(expected, result) } - @Test - fun centralConfigurationServiceImpl_fetchPublicKey_returnsResponse() = - runTest { - val expected = "public-key-value" - mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(expected)) - - val result = service.fetchPublicKey() - - assertEquals(expected, result) - } - @Test fun centralConfigurationServiceImpl_fetchSignature_returnsResponse() = runTest { diff --git a/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/shared/TestConfigurationFiles.kt b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/shared/TestConfigurationFiles.kt new file mode 100644 index 000000000..909985b45 --- /dev/null +++ b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/shared/TestConfigurationFiles.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.configuration.shared + +import ee.ria.DigiDoc.utilsLib.file.FileUtil + +internal object TestConfigurationFiles { + fun config(): String = bytes("config.json").toString(Charsets.UTF_8) + + fun publicKey(): String = text("config.ecpub") + + fun signature(): ByteArray = bytes("config.ecc") + + private fun bytes(name: String): ByteArray = + classLoader().getResourceAsStream(name).use { FileUtil.readFileContentBytes(it) } + + private fun text(name: String): String = + classLoader().getResourceAsStream(name).use { FileUtil.readFileContent(it) } + + private fun classLoader(): ClassLoader = + javaClass.classLoader ?: throw IllegalStateException("Unable to get ClassLoader") +} diff --git a/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifierTest.kt b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifierTest.kt index 38ffa82bc..d8f0ccad2 100644 --- a/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifierTest.kt +++ b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/utils/SignatureVerifierTest.kt @@ -21,84 +21,60 @@ package ee.ria.DigiDoc.configuration.utils -import ee.ria.DigiDoc.utilsLib.file.FileUtil +import ee.ria.DigiDoc.configuration.shared.TestConfigurationFiles import junit.framework.TestCase +import org.junit.Assert.assertThrows import org.junit.Test -import java.io.IOException -import java.util.Optional class SignatureVerifierTest { @Test fun verifyValidSignature() { - val classLoader = - Optional - .ofNullable(javaClass.getClassLoader()) - .orElseThrow { - IllegalStateException( - "Unable to get ClassLoader", - ) - } - try { - classLoader.getResourceAsStream("config.json").use { configJsonStream -> - classLoader.getResourceAsStream("config.rsa").use { configSignatureStream -> - classLoader - .getResourceAsStream("config.pub") - .use { configSignaturePublicKeyStream -> - val configJson: String = - FileUtil.readFileContent(configJsonStream) - val configSignature: ByteArray = - FileUtil.readFileContentBytes(configSignatureStream) - val configSignaturePublicKey: String = - FileUtil.readFileContent(configSignaturePublicKeyStream) - TestCase.assertTrue( - SignatureVerifier.verify( - configSignature, - configSignaturePublicKey, - configJson, - ), - ) - } - } - } - } catch (_: IOException) { - throw IllegalStateException("Unable to read resource") - } + TestCase.assertTrue(SignatureVerifier.verify(signature(), publicKey(), config())) } @Test fun verifyInvalidSignature() { - val classLoader = - Optional - .ofNullable(javaClass.getClassLoader()) - .orElseThrow { - IllegalStateException( - "Unable to get ClassLoader", - ) - } - try { - classLoader.getResourceAsStream("config.json").use { configJsonStream -> - classLoader.getResourceAsStream("config.rsa").use { configSignatureStream -> - classLoader - .getResourceAsStream("config.pub") - .use { configSignaturePublicKeyStream -> - val configJson: String = - FileUtil.readFileContent(configJsonStream) - val configSignature: ByteArray = - FileUtil.readFileContentBytes(configSignatureStream) - val configSignaturePublicKey: String = - FileUtil.readFileContent(configSignaturePublicKeyStream) - TestCase.assertFalse( - SignatureVerifier.verify( - configSignature, - configSignaturePublicKey, - configJson + "a", - ), - ) - } - } + TestCase.assertFalse(SignatureVerifier.verify(signature(), publicKey(), config() + "a")) + } + + @Test + fun verifyDoesNotAcceptGarbageSignatureBytes() { + val accepted = + try { + SignatureVerifier.verify(byteArrayOf(1, 2, 3, 4), publicKey(), config()) + } catch (_: IllegalStateException) { + false } - } catch (_: IOException) { - throw IllegalStateException("Unable to read resource") + + TestCase.assertFalse(accepted) + } + + @Test + fun verifyThrowsIllegalStateWhenPublicKeyPemIsEmpty() { + assertThrows(IllegalStateException::class.java) { + SignatureVerifier.verify(ByteArray(0), "", "content") + } + } + + @Test + fun verifyThrowsIllegalStateWhenPublicKeyPemIsNotPem() { + assertThrows(IllegalStateException::class.java) { + SignatureVerifier.verify(ByteArray(0), "not a pem at all", "content") + } + } + + @Test + fun verifyThrowsIllegalStateWhenPublicKeyPemBodyIsGarbage() { + val garbagePem = + "-----BEGIN PUBLIC KEY-----\nbm90IGEgcHVibGljIGtleQ==\n-----END PUBLIC KEY-----\n" + assertThrows(IllegalStateException::class.java) { + SignatureVerifier.verify(ByteArray(0), garbagePem, "content") } } + + private fun config(): String = TestConfigurationFiles.config() + + private fun publicKey(): String = TestConfigurationFiles.publicKey() + + private fun signature(): ByteArray = TestConfigurationFiles.signature() } diff --git a/config-lib/src/test/resources/config.ecc b/config-lib/src/test/resources/config.ecc new file mode 100644 index 000000000..2155f2674 Binary files /dev/null and b/config-lib/src/test/resources/config.ecc differ diff --git a/config-lib/src/test/resources/config.ecpub b/config-lib/src/test/resources/config.ecpub new file mode 100644 index 000000000..37ab76a78 --- /dev/null +++ b/config-lib/src/test/resources/config.ecpub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBT8nzRGnA7I5s5c+wi/kz4a86j449 +mRfeDzQRoulSKQSEvryfdFRoze4p6XvY++FOVmuHglszbxcRv1oqT5aAEuYBxOS0 +Z85VukvGON3NxD2SBcifANr9f0LTBvu+p9+wOZm+nsK/9CMsCjEGrQ7KGjyQ7Vfk +pJoriwpp3JnCvIuLSak= +-----END PUBLIC KEY----- diff --git a/config-lib/src/test/resources/config.json b/config-lib/src/test/resources/config.json index f9a9928ab..cddc34484 100644 --- a/config-lib/src/test/resources/config.json +++ b/config-lib/src/test/resources/config.json @@ -1,79 +1,113 @@ { - "TERA-SUPPORTED": "1.0.0.5", - "TERA-URL": "https://puhver.ria.ee/tsa", - "TERA-EXCL-DIRS-OSX": "/Users/$user/Library/Caches", - "TERA-EXCL-DIRS-UBUNTU": "/var/tmp", - "TERA-EXCL-DIRS-WIN": "C:/Users/$user/AppData/Local/Temp", "META-INF": { + "VER": 1, + "SERIAL": 200, "URL": "https://id.eesti.ee/config.json", - "DATE": "20190816123015Z", - "SERIAL": 94, - "VER": 1 + "DATE": "20251210103015Z" }, - "PICTURE-URL": "https://sisene.www.eesti.ee/idportaal/portaal.idpilt", - "QDIGIDOC4-SUPPORTED": "4.2.0.42", - "QDIGIDOC4-LATEST": "4.2.2.0", - "TERA-DEFAULT-OUT-EXTENSION": ".asics", - "TSA-URL": "http://dd-at.ria.ee/tsa", - "QESTEIDUTIL-LATEST": "3.12.10.1265", - "MID-SIGN-URL": "https://digidocservice.sk.ee", - "OSX-LATEST": "19.7.0.1834", - "QESTEIDUTIL-SUPPORTED": "4.0.0.23", - "EIDUPDATER-URL-TOECC": "https://installer.id.ee/media/teavitus/", - "WIN-DOWNLOAD": "https://installer.id.ee/media/win/Open-EID-19.7.0.1837_x86.exe", - "QDIGIDOC-SUPPORTED": "4.0.0.23", + "TSL-URL": "https://ec.europa.eu/tools/lotl/eu-lotl.xml", "TSL-CERTS": [ - "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" ], - "WIN-LATEST": "19.7.0.1837", - "OCSP-URL-ISSUER": { - "KLASS3-SK 2010": "http://ocsp.sk.ee", - "KLASS3-SK 2016": "http://ocsp.sk.ee", - "TEST of KLASS3-SK 2010": "http://demo.sk.ee/ocsp", - "VRK CA for Test Purposes - G2": "http://demo.sk.ee/ocsp", - "VRK CA for Test Purposes - G3": "http://demo.sk.ee/ocsp", - "TEST of ESTEID-SK 2011": "http://demo.sk.ee/ocsp", - "TEST of ESTEID-SK 2015": "http://demo.sk.ee/ocsp", - "ESTEID-SK 2011": "http://ocsp.sk.ee", - "TEST of KLASS3-SK 2016": "http://demo.sk.ee/ocsp", - "EID-SK 2016": "http://ocsp.sk.ee", - "EID-SK 2011": "http://ocsp.sk.ee", - "ESTEID-SK 2015": "http://ocsp.sk.ee", - "Nacionalinis sertifikavimo centras (IssuingCA A)": "http://demo.sk.ee/ocsp" - }, - "WIN-UPGRADECODE": "{f1c4d351-269d-4bee-8cdb-6ea70c968875}", + "TSA-URL": "https://eid-dd.ria.ee/ts", + "OCSP-URL-ISSUER": {}, "LDAP-PERSON-URL": "ldaps://esteid.ldap.sk.ee", - "OSX-DOWNLOAD": "https://installer.id.ee/media/osx/Open-EID_19.7.0.1834.dmg", "LDAP-CORP-URL": "ldaps://k3.ldap.sk.ee", - "QDIGIDOC-LATEST": "3.13.6.1525", + "MID-PROXY-URL": "https://eid-dd.ria.ee/mid", + "MID-SK-URL": "https://mid.sk.ee/mid-api", + "SIDV2-PROXY-URL": "https://eid-dd.ria.ee/sid/v2", + "SIDV2-SK-URL": "https://rp-api.smart-id.com/v2", + "SIVA-URL": "https://siva.eesti.ee/V3/validate", + "SIVA-CERT": "", + "LDAP-PERSON-URLS": [ + "ldaps://esteid.ldap.sk.ee", + "ldaps://ldap.eidpki.ee/dc=ldap,dc=eidpki,dc=ee" + ], + "CDOC2-CONF": { + "00000000-0000-0000-0000-000000000000": { + "NAME": "RIA", + "POST": "https://cdoc2.id.ee:8443", + "FETCH": "https://cdoc2.id.ee:8444" + } + }, + "CDOC2-USE-KEYSERVER": false, + "CDOC2-DEFAULT-KEYSERVER": "00000000-0000-0000-0000-000000000000", + "QDIGIDOC4-SUPPORTED": "4.8.2.0", + "QDIGIDOC4-LATEST": "4.9.0.0", + "WIN-LATEST": "25.10.23.8403", + "WIN-DOWNLOAD": "https://installer.id.ee/media/win/Open-EID-25.10.23.8403.exe", + "WIN-UPGRADECODE": "{f1c4d351-269d-4bee-8cdb-6ea70c968875}", + "OSX-LATEST": "25.10.0.235", + "OSX-DOWNLOAD": "https://installer.id.ee/media/osx/Open-EID_25.10.0.235.dmg", "CERT-BUNDLE": [ - "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" ], - "TSL-URL": "https://ec.europa.eu/tools/lotl/eu-lotl.xml", - "EIDUPDATER-URL": "https://installer.id.ee/media/teavitus/", - "EMAIL-ACTIVATE-URL": "https://sisene.www.eesti.ee/idportaal/postisysteem.lisa_suunamine?=%1", - "MID-SIGN-TEST-URL": "https://tsp.demo.sk.ee", - "EMAIL-REDIRECT-URL": "https://sisene.www.eesti.ee/idportaal/postisysteem.naita_suunamised", - "SIVA-URL": "https://siva.eesti.ee/V2/validate" -} \ No newline at end of file + "LDAP-CERTS": [ + "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" + ], + "ALLOWED-EXTENSIONS": [ + "ddoc", + "bdoc", + "edoc", + "adoc", + "asice", + "cdoc", + "asics", + "txt", + "doc", + "docx", + "odt", + "ods", + "tex", + "wks", + "wps", + "wpd", + "rtf", + "xlr", + "xls", + "xlsx", + "pdf", + "key", + "odp", + "pps", + "ppt", + "pptx", + "png", + "jpg", + "jpeg", + "bmp", + "ai", + "gif", + "ico", + "ps", + "psd", + "tif", + "tiff", + "csv", + "cdoc2" + ] +} diff --git a/config-lib/src/test/resources/config.pub b/config-lib/src/test/resources/config.pub deleted file mode 100644 index a3935c91f..000000000 --- a/config-lib/src/test/resources/config.pub +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN RSA PUBLIC KEY----- -MIIBCgKCAQEAu7GeS3jgvLjKL8ZgA4DlT3n3+/IQpoSlO0ErRIVT5upbY7yDf0mj -Cc8Xaqr91aBHjYnrEAz+nkmb12BuJCp/C3xQXnXEQchF2J+HfMbIClJ7TCl2951k -kuKdZpNeZ/iim0uRTs1UA0ews0+CPLAAklEpC1h5+fK8Uwl9+5h+v5jj/9P06ey0 -8H3VpxX4Nu0XTU2QcXocv5BTYJKxLKQicEL+nstpBrRI2Cqgwcd2Fz2qp8M9KJvI -TKW0TD2EKgq6nYELXPRNl/KOsVt7RGyUutd809GARfcOotvhHy2qzHoWlr3pY2ZO -zMSpZbisPvbOJ4fIyPhkbWBr7a3ytquOmQIDAQAB ------END RSA PUBLIC KEY----- \ No newline at end of file diff --git a/config-lib/src/test/resources/config.rsa b/config-lib/src/test/resources/config.rsa deleted file mode 100644 index d4fd7a85b..000000000 Binary files a/config-lib/src/test/resources/config.rsa and /dev/null differ diff --git a/config-lib/src/test/resources/configuration.properties b/config-lib/src/test/resources/configuration.properties index 482ab9f33..015acee7c 100644 --- a/config-lib/src/test/resources/configuration.properties +++ b/config-lib/src/test/resources/configuration.properties @@ -1,4 +1,4 @@ -central-configuration-service.url=https://id.eesti.ee/ +central-configuration-service.url=https://id.eesti.ee configuration.update-interval=4 -configuration.version-serial=94 -configuration.download-date=04-09-2019 15:04:19 \ No newline at end of file +configuration.version-serial=200 +configuration.download-date=07-01-2026 16:33:26 \ No newline at end of file diff --git a/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt b/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt index b4d4f2c91..c98d0a83b 100644 --- a/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt +++ b/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt @@ -169,7 +169,7 @@ class CryptoContainerTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) CryptoInitialization().init( isLoggingEnabled = true, ) diff --git a/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt b/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt index 7e410ee77..35a7dd0d4 100644 --- a/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt +++ b/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt @@ -121,7 +121,7 @@ class IdCardServiceImplTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - val configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + val configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) { diff --git a/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt index 9c0afadb9..5a1fc5d4f 100644 --- a/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt +++ b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt @@ -90,7 +90,7 @@ class SignedContainerTest { ConfigurationPropertiesImpl(), ConfigurationSignatureVerifierImpl(), ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) + configurationRepository = ConfigurationRepositoryImpl(configurationLoader) LibdigidocLibraryLoader().init(context) Initialization(configurationRepository).init(context) } catch (_: Exception) {