Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
-dname "CN=Android Debug,O=Android,C=US"
echo "Generated debug.keystore at ${{ github.workspace }}/app/src/main/assets/keystore/debug.keystore"
- name: Set up Android SDK
uses: android-actions/setup-android@v3
uses: android-actions/setup-android@v4
- name: Run fetch default configuration
run: |
./gradlew fetchAndPackageDefaultConfiguration
Expand Down Expand Up @@ -147,7 +147,7 @@ jobs:

- name: Set up Android SDK
if: matrix.build-mode == 'manual'
uses: android-actions/setup-android@v3
uses: android-actions/setup-android@v4

- name: Setup Gradle
if: matrix.build-mode == 'manual'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ import ee.ria.DigiDoc.domain.model.IdCardData
import ee.ria.DigiDoc.domain.preferences.DataStore
import ee.ria.DigiDoc.domain.service.IdCardService
import ee.ria.DigiDoc.exceptions.NFCError
import ee.ria.DigiDoc.idcard.CodeNotActivatedException
import ee.ria.DigiDoc.idcard.CodeType
import ee.ria.DigiDoc.idcard.CodeVerificationException
import ee.ria.DigiDoc.idcard.PaceTunnelException
import ee.ria.DigiDoc.libdigidoclib.SignedContainer
import ee.ria.DigiDoc.libdigidoclib.domain.model.ContainerWrapper
import ee.ria.DigiDoc.libdigidoclib.domain.model.ContainerWrapperImpl
import ee.ria.DigiDoc.libdigidoclib.init.Initialization
import ee.ria.DigiDoc.libdigidoclib.init.LibdigidocLibraryLoader
import ee.ria.DigiDoc.smartcardreader.ApduResponseException
import ee.ria.DigiDoc.smartcardreader.CardConnectionLostException
import ee.ria.DigiDoc.smartcardreader.SmartCardReaderException
import ee.ria.DigiDoc.smartcardreader.nfc.NfcSmartCardReaderManager
import ee.ria.DigiDoc.smartcardreader.nfc.NfcSmartCardReaderManager.NfcStatus
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -54,6 +60,7 @@ import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.atLeastOnce
import java.io.File
import java.io.IOException

@RunWith(MockitoJUnitRunner::class)
class NFCViewModelTest {
Expand Down Expand Up @@ -536,4 +543,116 @@ class NFCViewModelTest {

viewModel.userData.removeObserver(userDataObserver)
}

private fun handleException(
ex: SmartCardReaderException,
codeType: CodeType = CodeType.PIN2,
) = viewModel.handleSmartCardReaderException(ex, codeType, codeType.name)

@Test
fun nfcViewModel_handleSmartCardReaderException_cardConnectionLostReportsTagLost() =
runTest {
handleException(CardConnectionLostException(IOException("tag left the field")))
assertEquals(
NFCError.TagLost(R.string.signature_update_nfc_tag_lost),
viewModel.errorState.value,
)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_codeNotActivatedShowsDialogWhenSigning() =
runTest {
handleException(CodeNotActivatedException(CodeType.PIN2))
assertEquals(R.string.sign_blocked_pin2_unchanged_message, viewModel.dialogError.value)
assertNull(viewModel.errorState.value)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_codeNotActivatedIsTechnicalErrorWhenNotSigning() =
runTest {
handleException(CodeNotActivatedException(CodeType.PIN2), CodeType.PIN1)
assertEquals(
NFCError.TechnicalError(R.string.signature_update_nfc_technical_error),
viewModel.errorState.value,
)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_twoRetriesLeftReportsWrongPin() =
runTest {
handleException(CodeVerificationException(CodeType.PIN2, 2))
assertEquals(
NFCError.WrongPin("PIN2", 2, R.string.id_card_sign_pin_invalid),
viewModel.errorState.value,
)
assertTrue(viewModel.shouldResetPIN.value == true)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_oneRetryLeftReportsFinalAttempt() =
runTest {
handleException(CodeVerificationException(CodeType.PIN2, 1))
assertEquals(
NFCError.WrongPin("PIN2", 1, R.string.id_card_sign_pin_invalid_final),
viewModel.errorState.value,
)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_noRetriesLeftReportsPinBlocked() =
runTest {
handleException(CodeVerificationException(CodeType.PIN2, 0))
assertEquals(
NFCError.PinBlocked("PIN2", R.string.id_card_sign_pin_locked),
viewModel.errorState.value,
)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_pin1FlowReportsItsOwnRetryCount() =
runTest {
handleException(CodeVerificationException(CodeType.PIN1, 1), CodeType.PIN1)
assertEquals(
NFCError.WrongPin("PIN1", 1, R.string.id_card_sign_pin_invalid_final),
viewModel.errorState.value,
)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_codeTypeMismatchFallsThroughToTechnicalError() =
runTest {
handleException(CodeVerificationException(CodeType.PUK, 2))
assertEquals(
NFCError.TechnicalError(R.string.signature_update_nfc_technical_error),
viewModel.errorState.value,
)
assertFalse(viewModel.shouldResetPIN.value == true)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_apduResponseReportsTechnicalError() =
runTest {
handleException(ApduResponseException(0x6A.toByte(), 0x88.toByte()))
assertEquals(
NFCError.ApduResponse(R.string.signature_update_nfc_technical_error),
viewModel.errorState.value,
)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_paceTunnelReportsWrongCan() =
runTest {
handleException(PaceTunnelException(IOException("wrong CAN")))
assertEquals(
NFCError.WrongCan(R.string.signature_update_nfc_wrong_can),
viewModel.errorState.value,
)
}

@Test
fun nfcViewModel_handleSmartCardReaderException_signingFailureClearsSignStatus() =
runTest {
handleException(CardConnectionLostException(IOException()))
assertFalse(viewModel.signStatus.value == true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import ee.ria.DigiDoc.CertificateCreator
import ee.ria.DigiDoc.IdCardDataCreator.Companion.createMockIdCardData
import ee.ria.DigiDoc.MainActivity
import ee.ria.DigiDoc.R
import ee.ria.DigiDoc.common.Constant
import ee.ria.DigiDoc.domain.model.IdCardData
import ee.ria.DigiDoc.domain.model.pin.PinChangeVariant
import ee.ria.DigiDoc.domain.preferences.DataStore
Expand Down Expand Up @@ -327,19 +326,19 @@ class SharedMyEidViewModelTest {
@Test
fun sharedMyEidViewModel_isPinCodeTooEasy_returnsCorrectMinimumLengthForPIN1() {
val result = viewModel.getPinCodeMinimumLength(CodeType.PIN1)
assertEquals(Constant.MyEID.PIN1_MINIMUM_LENGTH, result)
assertEquals(4, result)
}

@Test
fun sharedMyEidViewModel_isPinCodeTooEasy_returnsCorrectMinimumLengthForPIN2() {
val result = viewModel.getPinCodeMinimumLength(CodeType.PIN2)
assertEquals(Constant.MyEID.PIN2_MINIMUM_LENGTH, result)
assertEquals(5, result)
}

@Test
fun sharedMyEidViewModel_isPinCodeTooEasy_returnsCorrectMinimumLengthForPUK() {
val result = viewModel.getPinCodeMinimumLength(CodeType.PUK)
assertEquals(Constant.MyEID.PUK_MINIMUM_LENGTH, result)
assertEquals(8, result)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.asFlow
import androidx.navigation.NavHostController
import ee.ria.DigiDoc.R
import ee.ria.DigiDoc.common.Constant
import ee.ria.DigiDoc.idcard.CodeType
import ee.ria.DigiDoc.idcard.PaceTunnelException
import ee.ria.DigiDoc.smartcardreader.ApduResponseException
import ee.ria.DigiDoc.smartcardreader.CardConnectionLostException
import ee.ria.DigiDoc.ui.component.menu.SettingsMenuBottomSheet
import ee.ria.DigiDoc.ui.component.shared.PrimaryOutlinedButton
import ee.ria.DigiDoc.ui.component.shared.SecurePinTextField
Expand Down Expand Up @@ -152,7 +152,7 @@ fun MyEidPinScreen(
sharedMyEidViewModel.getPinCodeMinimumLength(
currentPinCodeType,
),
Constant.MyEID.PIN_MAXIMUM_LENGTH,
currentPinCodeType.maxLength,
)}. ${if (isForgottenPin && showCurrentPinField.value) {
stringResource(R.string.myeid_puk_info)
} else {
Expand Down Expand Up @@ -438,14 +438,15 @@ fun MyEidPinScreen(
}
}
} else {
if (exc?.message?.contains("TagLostException") == true) {
showMessage(context, R.string.signature_update_nfc_tag_lost)
} else if (exc is ApduResponseException) {
showMessage(context, R.string.signature_update_nfc_technical_error)
} else if (exc is PaceTunnelException) {
showMessage(context, R.string.signature_update_nfc_wrong_can)
} else {
showMessage(context, R.string.signature_update_nfc_technical_error)
when (exc) {
is CardConnectionLostException ->
showMessage(context, R.string.signature_update_nfc_tag_lost)
is ApduResponseException ->
showMessage(context, R.string.signature_update_nfc_technical_error)
is PaceTunnelException ->
showMessage(context, R.string.signature_update_nfc_wrong_can)
else ->
showMessage(context, R.string.signature_update_nfc_technical_error)
}
showNFCScreen.value = false
}
Expand All @@ -469,14 +470,15 @@ fun MyEidPinScreen(
}
}
} else {
if (exc?.message?.contains("TagLostException") == true) {
showMessage(context, R.string.signature_update_nfc_tag_lost)
} else if (exc is ApduResponseException) {
showMessage(context, R.string.signature_update_nfc_technical_error)
} else if (exc is PaceTunnelException) {
showMessage(context, R.string.signature_update_nfc_wrong_can)
} else {
showMessage(context, R.string.signature_update_nfc_technical_error)
when (exc) {
is CardConnectionLostException ->
showMessage(context, R.string.signature_update_nfc_tag_lost)
is ApduResponseException ->
showMessage(context, R.string.signature_update_nfc_technical_error)
is PaceTunnelException ->
showMessage(context, R.string.signature_update_nfc_wrong_can)
else ->
showMessage(context, R.string.signature_update_nfc_technical_error)
}
showNFCScreen.value = false
}
Expand Down
16 changes: 4 additions & 12 deletions app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/NFCView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,11 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.asFlow
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import ee.ria.DigiDoc.R
import ee.ria.DigiDoc.common.Constant.NFCConstants.CAN_LENGTH
import ee.ria.DigiDoc.common.Constant.NFCConstants.PIN1_MIN_LENGTH
import ee.ria.DigiDoc.common.Constant.NFCConstants.PIN2_MIN_LENGTH
import ee.ria.DigiDoc.common.Constant.NFCConstants.PIN_MAX_LENGTH
import ee.ria.DigiDoc.domain.model.IdCardData
import ee.ria.DigiDoc.domain.model.IdentityAction
import ee.ria.DigiDoc.exceptions.NFCError
import ee.ria.DigiDoc.idcard.CodeType
import ee.ria.DigiDoc.idcard.TokenWithPace.CAN_LENGTH
import ee.ria.DigiDoc.libdigidoclib.domain.model.RoleData
import ee.ria.DigiDoc.smartcardreader.nfc.NfcSmartCardReaderManager.NfcStatus
import ee.ria.DigiDoc.ui.component.shared.CancelAndOkButtonRow
Expand Down Expand Up @@ -235,20 +232,15 @@ fun NFCView(

val pinCodeLabel = stringResource(id = R.string.signature_update_nfc_pin, pinType)

val pinMinLength =
if (identityAction == IdentityAction.SIGN) {
PIN2_MIN_LENGTH
} else {
PIN1_MIN_LENGTH
}

val codeType =
if (identityAction == IdentityAction.SIGN) {
CodeType.PIN2
} else {
CodeType.PIN1
}

val pinMinLength = codeType.minLength

val webEidAuth = webEidViewModel?.authRequest?.collectAsState()?.value
val originString = webEidAuth?.origin ?: ""
val challengeString = webEidAuth?.challenge ?: ""
Expand Down Expand Up @@ -925,7 +917,7 @@ fun NFCView(
stringResource(id = R.string.id_card_sign_pin_invalid_length),
pinType,
pinMinLength,
PIN_MAX_LENGTH.toString(),
codeType.maxLength.toString(),
)
} else {
""
Expand Down
11 changes: 1 addition & 10 deletions app/src/main/kotlin/ee/ria/DigiDoc/utils/pin/PinCodeUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@

package ee.ria.DigiDoc.utils.pin

import ee.ria.DigiDoc.common.Constant.NFCConstants.PIN1_MIN_LENGTH
import ee.ria.DigiDoc.common.Constant.NFCConstants.PIN2_MIN_LENGTH
import ee.ria.DigiDoc.common.Constant.NFCConstants.PIN_MAX_LENGTH
import ee.ria.DigiDoc.common.Constant.NFCConstants.PUK_MIN_LENGTH
import ee.ria.DigiDoc.idcard.CodeType

object PinCodeUtil {
Expand All @@ -20,10 +16,5 @@ object PinCodeUtil {
fun isPINLengthValid(
pinCode: ByteArray,
codeType: CodeType,
): Boolean =
when (codeType) {
CodeType.PIN1 -> pinCode.size in PIN1_MIN_LENGTH..PIN_MAX_LENGTH
CodeType.PIN2 -> pinCode.size in PIN2_MIN_LENGTH..PIN_MAX_LENGTH
CodeType.PUK -> pinCode.size > PUK_MIN_LENGTH
}
): Boolean = codeType.isLengthValid(pinCode.size)
}
Loading
Loading