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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -245,36 +245,6 @@ class SigningViewModelTest {
assertFalse(isEncryptButtonShown)
}

@Test
fun signingViewModel_isShareButtonShown_returnTrue() =
runTest {
val file =
getResourceFileAsFile(
context,
"example_no_signatures.asice",
R.raw.example_no_signatures,
)

val container = SignedContainer.openOrCreate(context, file, listOf(file), true)

val isShareButtonShown = viewModel.isShareButtonShown(container, false)

assertTrue(isShareButtonShown)
}

@Test
fun signingViewModel_isShareButtonShown_returnFalse() =
runTest {
val file = File.createTempFile("temp", ".txt")
file.writeText("test content")

val container = SignedContainer.openOrCreate(context, file, listOf(file), true)

val isShareButtonShown = viewModel.isShareButtonShown(container, false)

assertFalse(isShareButtonShown)
}

@Test
fun signingViewModel_isExistingContainer_returnTrue() =
runTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepositoryImp
import ee.ria.DigiDoc.configuration.repository.ConfigurationRepository
import ee.ria.DigiDoc.configuration.repository.ConfigurationRepositoryImpl
import ee.ria.DigiDoc.configuration.service.CentralConfigurationServiceImpl
import ee.ria.DigiDoc.cryptolib.CryptoContainer
import ee.ria.DigiDoc.domain.model.ContainerFileOpeningResult
import ee.ria.DigiDoc.domain.repository.fileopening.FileOpeningRepository
import ee.ria.DigiDoc.libdigidoclib.SignedContainer
Expand Down Expand Up @@ -585,6 +586,97 @@ class SharedContainerViewModelTest {
assertFalse(viewModel.isNestedContainer(signedContainer))
}

@Test
fun sharedContainerViewModel_isNestedContainer_returnFalseForFreshContainerWhenStackClearedFirst() {
val staleContainer = SignedContainer(context)
val freshContainer = SignedContainer(context)

viewModel.setSignedContainer(staleContainer)

viewModel.clearContainers()
viewModel.setSignedContainer(freshContainer)

assertEquals(1, viewModel.nestedContainers.size)
assertFalse(viewModel.isNestedContainer(freshContainer))
}

@Test
fun sharedContainerViewModel_isNestedContainer_returnTrueForFreshContainerWhenStaleContainerLeftInStack() {
val staleContainer = SignedContainer(context)
val freshContainer = SignedContainer(context)

viewModel.setSignedContainer(staleContainer)
viewModel.setSignedContainer(freshContainer)

assertEquals(2, viewModel.nestedContainers.size)
assertTrue(viewModel.isNestedContainer(freshContainer))
}

@Test
fun sharedContainerViewModel_setSignedContainer_doesNotGrowStackWhenSameContainerIsSetAgain() {
val signedContainer = SignedContainer(context)

viewModel.setSignedContainer(signedContainer)
viewModel.setSignedContainer(signedContainer)

assertEquals(1, viewModel.nestedContainers.size)
assertTrue(viewModel.nestedContainers.contains(signedContainer))
assertFalse(viewModel.isNestedContainer(signedContainer))
}

@Test
fun sharedContainerViewModel_resetSignedContainer_leavesNestedContainerStackPopulated() {
val signedContainer = SignedContainer(context)

viewModel.setSignedContainer(signedContainer)
viewModel.resetSignedContainer()

assertNull(viewModel.signedContainer.value)
assertEquals(1, viewModel.nestedContainers.size)
}

@Test
fun sharedContainerViewModel_clearContainers_makesFreshCryptoContainerNonNested() {
val staleContainer = SignedContainer(context)
val freshCryptoContainer =
CryptoContainer(
context = context,
file = File.createTempFile("crypto", ".cdoc"),
dataFiles = arrayListOf(),
recipients = arrayListOf(),
decrypted = false,
encrypted = false,
)

viewModel.setSignedContainer(staleContainer)

viewModel.clearContainers()
viewModel.setCryptoContainer(freshCryptoContainer)

assertEquals(1, viewModel.nestedContainers.size)
assertFalse(viewModel.isNestedContainer(freshCryptoContainer))
}

@Test
fun sharedContainerViewModel_isNestedContainer_returnTrueForFreshCryptoContainerWhenStaleContainerLeftInStack() {
val staleContainer = SignedContainer(context)
val freshCryptoContainer =
CryptoContainer(
context = context,
file = File.createTempFile("crypto", ".cdoc"),
dataFiles = arrayListOf(),
recipients = arrayListOf(),
decrypted = false,
encrypted = false,
)

viewModel.setSignedContainer(staleContainer)
viewModel.setCryptoContainer(freshCryptoContainer)

assertEquals(2, viewModel.nestedContainers.size)
assertTrue(viewModel.isNestedContainer(freshCryptoContainer))
}

@Test
fun sharedContainerViewModel_setExternalFileUri_success() =
runTest {
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/kotlin/ee/ria/DigiDoc/exceptions/NFCError.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ sealed class NFCError {
override val message: Int,
) : NFCError()

data class CertificateExpired(
override val message: Int,
) : NFCError()

data class CertificateNotYetValid(
override val message: Int,
) : NFCError()

data class CertificateUnknown(
override val message: Int,
) : NFCError()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ fun CryptoFileOpeningNavigation(
LaunchedEffect(cryptoFileOpeningViewModel.cryptoContainer) {
cryptoFileOpeningViewModel.cryptoContainer.asFlow().collect { cryptoContainer ->
cryptoContainer?.let {
if (!sharedContainerViewModel.nestedContainers.contains(it)) {
sharedContainerViewModel.clearContainers()
}
sharedContainerViewModel.setCryptoContainer(it)
delay(1000)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ fun FileOpeningNavigation(
LaunchedEffect(fileOpeningViewModel.signedContainer) {
fileOpeningViewModel.signedContainer.asFlow().collect { signedContainer ->
signedContainer?.let {
if (!sharedContainerViewModel.nestedContainers.contains(it)) {
sharedContainerViewModel.clearContainers()
}
sharedContainerViewModel.setSignedContainer(it)
delay(1000)

Expand All @@ -235,6 +238,9 @@ fun FileOpeningNavigation(
LaunchedEffect(fileOpeningViewModel.cryptoContainer) {
fileOpeningViewModel.cryptoContainer.asFlow().collect { cryptoContainer ->
cryptoContainer?.let {
if (!sharedContainerViewModel.nestedContainers.contains(it)) {
sharedContainerViewModel.clearContainers()
}
sharedContainerViewModel.setCryptoContainer(it)

navController.navigate(Route.Encrypt.route) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ class MobileIdViewModel
).put(
MobileCreateSignatureProcessStatus.CERTIFICATE_REVOKED,
R.string.signature_update_signature_error_message_certificate_revoked,
).put(
MobileCreateSignatureProcessStatus.CERTIFICATE_EXPIRED,
R.string.signature_update_signature_error_message_certificate_expired,
).put(
MobileCreateSignatureProcessStatus.CERTIFICATE_NOT_YET_VALID,
R.string.signature_update_signature_error_message_certificate_not_yet_valid,
).put(
MobileCreateSignatureProcessStatus.GENERAL_ERROR,
R.string.error_general_client,
Expand Down
33 changes: 33 additions & 0 deletions app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/NFCViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import ee.ria.DigiDoc.utils.pin.PinCodeUtil.isPINLengthValid
import ee.ria.DigiDoc.utilsLib.extensions.clearSensitive
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog
import ee.ria.DigiDoc.utilsLib.signing.CertificateUtil
import ee.ria.libdigidocpp.ExternalSigner
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -70,6 +71,8 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.bouncycastle.util.encoders.Hex
import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException
import java.util.Base64
import javax.inject.Inject

Expand Down Expand Up @@ -329,6 +332,12 @@ class NFCViewModel
val message = ex.message.orEmpty()

when {
ex is CertificateExpiredException ->
showExpiredCertificateError(ex)

ex is CertificateNotYetValidException ->
showNotYetValidCertificateError(ex)

message.contains("Certificate status: revoked") ->
showRevokedCertificateError(ex)

Expand Down Expand Up @@ -695,6 +704,8 @@ class NFCViewModel
}
}

CertificateUtil.x509Certificate(signerCert).checkValidity()

val hashBytes = Base64.getDecoder().decode(hash)
val (_, signatureArray) = idCardService.sign(card, pin2Code, hashBytes)

Expand All @@ -712,6 +723,12 @@ class NFCViewModel
val message = ex.message.orEmpty()

when {
ex is CertificateExpiredException ->
showExpiredCertificateError(ex)

ex is CertificateNotYetValidException ->
showNotYetValidCertificateError(ex)

message.contains("Certificate status: revoked") ->
showRevokedCertificateError(ex)

Expand Down Expand Up @@ -791,6 +808,22 @@ class NFCViewModel
errorLog(logTag, "Unable to sign with NFC - Certificate status: revoked", e)
}

private fun showExpiredCertificateError(e: Exception) {
_errorState.update {
NFCError.CertificateExpired(R.string.signature_update_signature_error_message_certificate_expired)
}
errorLog(logTag, "Unable to sign with NFC - Signing certificate has expired", e)
}

private fun showNotYetValidCertificateError(e: Exception) {
_errorState.update {
NFCError.CertificateNotYetValid(
R.string.signature_update_signature_error_message_certificate_not_yet_valid,
)
}
errorLog(logTag, "Unable to sign with NFC - Signing certificate is not yet valid", e)
}

private fun showUnknownCertificateError(e: Exception) {
_errorState.update {
NFCError.CertificateUnknown(R.string.signature_update_signature_error_message_certificate_unknown)
Expand Down
10 changes: 0 additions & 10 deletions app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,6 @@ class SigningViewModel
) &&
!isNestedContainer

fun isShareButtonShown(
signedContainer: SignedContainer?,
isNestedContainer: Boolean,
): Boolean =
(
isExistingContainer(signedContainer) ||
!isContainerWithoutSignatures(signedContainer)
) &&
!isNestedContainer

fun isBottomContainerButtonShown(
signedContainer: SignedContainer?,
isNestedContainer: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ class SmartIdViewModel
).put(
SessionStatusResponseProcessStatus.CERTIFICATE_REVOKED,
R.string.signature_update_signature_error_message_certificate_revoked,
).put(
SessionStatusResponseProcessStatus.CERTIFICATE_EXPIRED,
R.string.signature_update_signature_error_message_certificate_expired,
).put(
SessionStatusResponseProcessStatus.CERTIFICATE_NOT_YET_VALID,
R.string.signature_update_signature_error_message_certificate_not_yet_valid,
).put(
SessionStatusResponseProcessStatus.NOT_QUALIFIED,
R.string.signature_update_smart_id_error_message_not_qualified,
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/res/values-et/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@
<string name="sign_blocked_pin2_unchanged_url">https://www.id.ee/artikkel/id-kaardi-pin-ja-puk-koodide-muutmine/</string>
<string name="signature_update_signature_error_message_certificate_revoked">Sertifikaat on kehtetu</string>
<string name="signature_update_signature_error_message_certificate_unknown">Sertifikaadi staatus on teadmata</string>
<string name="signature_update_signature_error_message_certificate_expired">Sertifikaat on aegunud</string>
<string name="signature_update_signature_error_message_certificate_not_yet_valid">Sertifikaat pole veel kehtiv</string>
<string name="id_card_conditional_speech">ID-kaardi</string>
<string name="mobile_id_conditional_speech">Mobiil-ID</string>
<string name="smart_id_conditional_speech">Smart-ID</string>
Expand All @@ -344,10 +346,8 @@
<string name="signature_update_mobile_id_error_session_locked">Mobiilse allkirjastamise sessioon on lukustunud, proovi hiljem uuesti</string>
<string name="signature_update_mobile_id_error_general_user">Viga mobiil-ID kasutamisel kasutaja telefonis</string>
<string name="signature_update_mobile_id_error_not_mobile_id_user">Kasutaja ei ole mobiil-ID klient</string>
<string name="signature_update_mobile_id_error_user_certificate_revoked">Sinu mobiil-ID allkirjastamise sertifikaat on tühistatud</string>
<string name="signature_update_mobile_id_error_user_certificate_status_unknown">Sinu mobiil-ID allkirjastamise sertifikaadi olek on teadmata</string>
<string name="signature_update_mobile_id_error_user_certificate_suspended">Sinu mobiil-ID allkirjastamise sertifikaadi kehtivus on peatatud</string>
<string name="signature_update_mobile_id_error_user_certificate_expired">Sinu mobiil-ID allkirjastamise sertifikaat on aegunud</string>
<string name="signature_update_mobile_id_error_message_exceeds_volume_limit">Liiga palju faile ühes konteineris</string>
<string name="signature_update_mobile_id_error_message_access_rights">Ümbriku allkirjastamine ebaõnnestus. Kontrolli mobiil-ID teenuse ligipääsu seadeid. https://www.id.ee/artikkel/asutustele-kus-allkirjastatakse-digidoc4-kliendi-kaudu-suuremates-kogustes-dokumente/</string>
<string name="signature_update_mobile_id_error_simultaneous_requests_limit_exceeded">Samaaegsete pöördumiste limiit ületatud</string>
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@
<string name="sign_blocked_pin2_unchanged_url">https://www.id.ee/en/article/changing-id-card-pin-codes-and-puk-code/</string>
<string name="signature_update_signature_error_message_certificate_revoked">Certificate status revoked</string>
<string name="signature_update_signature_error_message_certificate_unknown">Certificate status unknown</string>
<string name="signature_update_signature_error_message_certificate_expired">Certificate has expired</string>
<string name="signature_update_signature_error_message_certificate_not_yet_valid">Certificate not yet valid</string>
<string name="id_card_conditional_speech">ID-card\'s</string>
<string name="mobile_id_conditional_speech">Mobile-ID</string>
<string name="smart_id_conditional_speech">Smart-ID</string>
Expand All @@ -344,10 +346,8 @@
<string name="signature_update_mobile_id_error_session_locked">Mobile signing session is locked, try again later</string>
<string name="signature_update_mobile_id_error_general_user">An error occurred with the signers mobile phone</string>
<string name="signature_update_mobile_id_error_not_mobile_id_user">User is not a mobile-ID client</string>
<string name="signature_update_mobile_id_error_user_certificate_revoked">Your mobile signing certificate is revoked</string>
<string name="signature_update_mobile_id_error_user_certificate_status_unknown">Your mobile signing certificate status is unknown</string>
<string name="signature_update_mobile_id_error_user_certificate_suspended">Your mobile signing certificate is suspended</string>
<string name="signature_update_mobile_id_error_user_certificate_expired">Your mobile signing certificate is expired</string>
<string name="signature_update_mobile_id_error_message_exceeds_volume_limit">Too many datafiles in container</string>
<string name="signature_update_mobile_id_error_message_access_rights">Failed to sign container. Check your mobile-ID service access settings. https://www.id.ee/en/article/for-organisations-that-sign-large-quantities-of-documents-using-digidoc4-client/</string>
<string name="signature_update_mobile_id_error_simultaneous_requests_limit_exceeded">Simultaneous requests limit exceeded</string>
Expand Down
1 change: 1 addition & 0 deletions libdigidoc-lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ dependencies {
implementation(libs.androidx.hilt)

testImplementation(libs.junit)
testImplementation(libs.mockito.kotlin)

androidTestImplementation(libs.byte.buddy)
androidTestImplementation(libs.mockito.android) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import ee.ria.DigiDoc.libdigidoclib.SignedContainer
import ee.ria.DigiDoc.libdigidoclib.init.libdigidocppDispatcher
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog
import ee.ria.DigiDoc.utilsLib.signing.CertificateUtil
import ee.ria.DigiDoc.utilsLib.text.TextUtil.removeEmptyStrings
import ee.ria.libdigidocpp.ExternalSigner
import ee.ria.libdigidocpp.Signature
Expand Down Expand Up @@ -61,6 +62,7 @@ class ContainerWrapperImpl : ContainerWrapper {
): ByteArray =
withContext(libdigidocppDispatcher) {
debugLog(logTag, "Preparing signature (with role data: ${roleData != null})")
checkCertificateValidity(cert)
signature =
when {
roleData != null && signedContainer != null -> {
Expand Down Expand Up @@ -103,4 +105,14 @@ class ContainerWrapperImpl : ContainerWrapper {
signedContainer?.rawContainer()?.save()
debugLog(logTag, "Signature finalized and container saved")
}

@Throws(CertificateException::class)
private fun checkCertificateValidity(cert: ByteArray?) {
if (cert == null) {
throw CertificateException("Unable to validate a missing signing certificate")
}
val x509Certificate = CertificateUtil.x509Certificate(cert)
debugLog(logTag, "Signing certificate valid from ${x509Certificate.notBefore} to ${x509Certificate.notAfter}")
x509Certificate.checkValidity()
}
}
Loading
Loading