mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2025-08-30 15:20:58 +03:00
Compare commits
12 Commits
iter4UI
...
MOB-24-use
Author | SHA1 | Date | |
---|---|---|---|
|
1c7e51d69a | ||
|
b623dadff5 | ||
|
4fcdccfb5e | ||
|
70991ae682 | ||
|
60207319b7 | ||
|
dbc758fb14 | ||
|
1037d672d5 | ||
|
e5300dfa5e | ||
|
d4c2a11521 | ||
|
09c4fa6be3 | ||
|
63bc89b0e4 | ||
|
73efb00368 |
@@ -4,7 +4,6 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.nfc.NfcAdapter
|
import android.nfc.NfcAdapter
|
||||||
import android.nfc.TagLostException
|
|
||||||
import android.nfc.tech.IsoDep
|
import android.nfc.tech.IsoDep
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
@@ -18,11 +17,16 @@ import androidx.fragment.app.activityViewModels
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
||||||
|
import com.tarkvaraprojekt.mobileauthapp.auth.AuthAppException
|
||||||
import com.tarkvaraprojekt.mobileauthapp.auth.Authenticator
|
import com.tarkvaraprojekt.mobileauthapp.auth.Authenticator
|
||||||
|
import com.tarkvaraprojekt.mobileauthapp.auth.InvalidCANException
|
||||||
|
import com.tarkvaraprojekt.mobileauthapp.auth.InvalidPINException
|
||||||
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
|
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
|
||||||
import com.tarkvaraprojekt.mobileauthapp.model.ParametersViewModel
|
import com.tarkvaraprojekt.mobileauthapp.model.ParametersViewModel
|
||||||
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
|
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
|
||||||
|
import java.io.IOException
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
import java.security.GeneralSecurityException
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +40,8 @@ class AuthFragment : Fragment() {
|
|||||||
|
|
||||||
private val paramsModel: ParametersViewModel by activityViewModels()
|
private val paramsModel: ParametersViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentAuthBinding? = null
|
private var _binding: FragmentAuthBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val args: CanFragmentArgs by navArgs()
|
private val args: CanFragmentArgs by navArgs()
|
||||||
|
|
||||||
@@ -49,8 +54,8 @@ class AuthFragment : Fragment() {
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentAuthBinding.inflate(inflater, container, false)
|
_binding = FragmentAuthBinding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -59,9 +64,9 @@ class AuthFragment : Fragment() {
|
|||||||
override fun onTick(p0: Long) {
|
override fun onTick(p0: Long) {
|
||||||
timeRemaining--
|
timeRemaining--
|
||||||
if (timeRemaining == 0) {
|
if (timeRemaining == 0) {
|
||||||
binding?.timeCounter?.text = getString(R.string.no_time)
|
binding.timeCounter.text = getString(R.string.no_time)
|
||||||
} else {
|
} else {
|
||||||
binding?.timeCounter?.text = getString(R.string.time_left, timeRemaining)
|
binding.timeCounter.text = getString(R.string.time_left, timeRemaining)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +76,9 @@ class AuthFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
// The button exists in code for testing reasons, but not visible to the user anymore unless visibility is changed in the code.
|
// The button exists in code for testing reasons, but not visible to the user anymore unless visibility is changed in the code.
|
||||||
binding!!.nextButton.visibility = View.GONE
|
binding.nextButton.visibility = View.GONE
|
||||||
binding!!.nextButton.setOnClickListener { goToNextFragment() }
|
binding.nextButton.setOnClickListener { goToNextFragment() }
|
||||||
binding!!.cancelButton.setOnClickListener { cancelAuth() }
|
binding.cancelButton.setOnClickListener { cancelAuth() }
|
||||||
val adapter = NfcAdapter.getDefaultAdapter(activity)
|
val adapter = NfcAdapter.getDefaultAdapter(activity)
|
||||||
if (adapter != null)
|
if (adapter != null)
|
||||||
getInfoFromIdCard(adapter)
|
getInfoFromIdCard(adapter)
|
||||||
@@ -104,8 +109,11 @@ class AuthFragment : Fragment() {
|
|||||||
adapter.enableReaderMode(activity, { tag ->
|
adapter.enableReaderMode(activity, { tag ->
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
binding!!.timeCounter.text = getString(R.string.card_detected)
|
binding.timeCounter.text = getString(R.string.card_detected)
|
||||||
}
|
}
|
||||||
|
var msgCode = 0
|
||||||
|
var msgArg : Int? = null
|
||||||
|
|
||||||
val card = IsoDep.get(tag)
|
val card = IsoDep.get(tag)
|
||||||
card.timeout = 32768
|
card.timeout = 32768
|
||||||
card.use {
|
card.use {
|
||||||
@@ -118,37 +126,55 @@ class AuthFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
paramsModel.setToken(jws)
|
paramsModel.setToken(jws)
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
|
binding.timeCounter.text = getString(R.string.data_read)
|
||||||
goToNextFragment()
|
goToNextFragment()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: android.nfc.TagLostException) {
|
||||||
when(e) {
|
msgCode = R.string.tag_lost
|
||||||
is TagLostException -> requireActivity().runOnUiThread { binding!!.timeCounter.text = getString(R.string.id_card_removed_early) }
|
} catch (e: InvalidCANException) {
|
||||||
else -> {
|
msgCode = R.string.wrong_can_text
|
||||||
when ("invalid pin") {
|
// If the CAN is wrong we will also delete the saved CAN so that the user won't use it again.
|
||||||
in e.message.toString().lowercase() -> requireActivity().runOnUiThread {
|
|
||||||
val messagePieces = e.message.toString().split(" ")
|
|
||||||
binding!!.timeCounter.text = getString(R.string.wrong_pin, messagePieces[messagePieces.size - 1])
|
|
||||||
viewModel.deletePin(requireContext())
|
|
||||||
}
|
|
||||||
else -> requireActivity().runOnUiThread {
|
|
||||||
binding!!.timeCounter.text = getString(R.string.wrong_can_text)
|
|
||||||
viewModel.deleteCan(requireContext())
|
viewModel.deleteCan(requireContext())
|
||||||
|
} catch (e: InvalidPINException) {
|
||||||
|
msgCode = R.string.wrong_pin
|
||||||
|
msgArg = e.remainingAttempts
|
||||||
|
viewModel.deletePin(requireContext())
|
||||||
|
} catch (e: AuthAppException) {
|
||||||
|
msgCode = when (e.code) {
|
||||||
|
400 -> R.string.err_parameter
|
||||||
|
401 -> R.string.err_authentication
|
||||||
|
446 -> R.string.err_card_locked
|
||||||
|
448 -> R.string.err_bad_data
|
||||||
|
500 -> R.string.err_internal
|
||||||
|
else -> R.string.err_unknown
|
||||||
}
|
}
|
||||||
}
|
} catch (e: GeneralSecurityException) {
|
||||||
}
|
msgCode = R.string.err_internal
|
||||||
}
|
} catch (e: IOException) {
|
||||||
// Give user some time to read the error message
|
msgCode = R.string.err_reading_card
|
||||||
Thread.sleep(2000)
|
} catch (e: Exception) {
|
||||||
cancelAuth()
|
msgCode = R.string.err_unknown
|
||||||
} finally {
|
} finally {
|
||||||
adapter.disableReaderMode(activity)
|
adapter.disableReaderMode(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msgCode != 0) {
|
||||||
|
requireActivity().runOnUiThread {
|
||||||
|
var msg = getString(msgCode)
|
||||||
|
if (msgArg != null)
|
||||||
|
msg = String.format(msg, msgArg)
|
||||||
|
binding.timeCounter.text = msg
|
||||||
|
}
|
||||||
|
// Gives user some time to read the error message
|
||||||
|
Thread.sleep(2000)
|
||||||
|
cancelAuth()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, NfcAdapter.FLAG_READER_NFC_A, null)
|
}, NfcAdapter.FLAG_READER_NFC_A, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -29,7 +29,8 @@ class CanFragment : Fragment() {
|
|||||||
|
|
||||||
private val viewModel: SmartCardViewModel by activityViewModels()
|
private val viewModel: SmartCardViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentCanBinding? = null
|
private var _binding: FragmentCanBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
// Navigation arguments:
|
// Navigation arguments:
|
||||||
// saving = true means that we are navigating here from the settings menu and must return to the settings menu.
|
// saving = true means that we are navigating here from the settings menu and must return to the settings menu.
|
||||||
@@ -40,17 +41,17 @@ class CanFragment : Fragment() {
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentCanBinding.inflate(inflater, container, false)
|
_binding = FragmentCanBinding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
checkIfSkip()
|
checkIfSkip()
|
||||||
binding!!.canTextField.editText?.addTextChangedListener {
|
binding.canTextField.editText?.addTextChangedListener {
|
||||||
checkEnteredCan()
|
checkEnteredCan()
|
||||||
}
|
}
|
||||||
binding!!.buttonCancel.setOnClickListener { goToTheStart() }
|
binding.buttonCancel.setOnClickListener { goToTheStart() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,7 +113,7 @@ class CanFragment : Fragment() {
|
|||||||
* allowed to modify the entered can.
|
* allowed to modify the entered can.
|
||||||
*/
|
*/
|
||||||
private fun checkEnteredCan() {
|
private fun checkEnteredCan() {
|
||||||
val enteredCan = binding!!.canTextField.editText?.text.toString()
|
val enteredCan = binding.canTextField.editText?.text.toString()
|
||||||
if (enteredCan.length == 6) {
|
if (enteredCan.length == 6) {
|
||||||
viewModel.setUserCan(enteredCan)
|
viewModel.setUserCan(enteredCan)
|
||||||
viewModel.storeCan(requireContext()) //Maybe storeCan should always automatically call setUserCan method as well because these methods usually are used together
|
viewModel.storeCan(requireContext()) //Maybe storeCan should always automatically call setUserCan method as well because these methods usually are used together
|
||||||
@@ -127,6 +128,6 @@ class CanFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -39,7 +39,8 @@ class HomeFragment : Fragment() {
|
|||||||
|
|
||||||
private val intentParams: ParametersViewModel by activityViewModels()
|
private val intentParams: ParametersViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentHomeBinding? = null
|
private var _binding: FragmentHomeBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
// The ID card reader mode is enabled on the home fragment when can is saved.
|
// The ID card reader mode is enabled on the home fragment when can is saved.
|
||||||
private var canSaved: Boolean = false
|
private var canSaved: Boolean = false
|
||||||
@@ -54,10 +55,10 @@ class HomeFragment : Fragment() {
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentHomeBinding.inflate(inflater, container, false)
|
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||||
// Making settings menu active again
|
// Making settings menu active again
|
||||||
(activity as MainActivity).menuAvailable = true
|
(activity as MainActivity).menuAvailable = true
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -111,15 +112,29 @@ class HomeFragment : Fragment() {
|
|||||||
intentParams.setAuthUrl(requireActivity().intent.data!!.getQueryParameter("authUrl")!!)
|
intentParams.setAuthUrl(requireActivity().intent.data!!.getQueryParameter("authUrl")!!)
|
||||||
intentParams.setOrigin(requireActivity().intent.data!!.getQueryParameter("originUrl")!!)
|
intentParams.setOrigin(requireActivity().intent.data!!.getQueryParameter("originUrl")!!)
|
||||||
}
|
}
|
||||||
|
goToTheNextFragment(mobile)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// There was a problem with parameters, which means that authentication is not possible.
|
// There was a problem with parameters, which means that authentication is not possible.
|
||||||
// In that case we will cancel the authentication immediately as it would be waste of the user's time to carry on
|
// In that case we will cancel the authentication immediately as it would be waste of the user's time to carry on
|
||||||
// before getting an inevitable error.
|
// before getting an inevitable error.
|
||||||
|
val message = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
message.setTitle(getString(R.string.problem_parameters))
|
||||||
|
if (intentParams.challenge == "") {
|
||||||
|
message.setMessage(getString(R.string.problem_challenge))
|
||||||
|
} else if (intentParams.authUrl == "") {
|
||||||
|
message.setMessage(getString(R.string.problem_authurl))
|
||||||
|
} else if (intentParams.origin == "") {
|
||||||
|
message.setMessage(getString(R.string.problem_originurl))
|
||||||
|
} else {
|
||||||
|
message.setMessage(getString(R.string.problem_other))
|
||||||
|
}
|
||||||
|
message.setPositiveButton(getString(R.string.continue_button)) {_, _ ->
|
||||||
val resultIntent = Intent()
|
val resultIntent = Intent()
|
||||||
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
||||||
requireActivity().finish()
|
requireActivity().finish()
|
||||||
}
|
}
|
||||||
goToTheNextFragment(mobile)
|
message.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,12 +142,12 @@ class HomeFragment : Fragment() {
|
|||||||
*/
|
*/
|
||||||
private fun canState() {
|
private fun canState() {
|
||||||
if (viewModel.userCan.length == 6) {
|
if (viewModel.userCan.length == 6) {
|
||||||
binding!!.canStatusText.text = getString(R.string.can_status_saved)
|
binding.canStatusText.text = getString(R.string.can_status_saved)
|
||||||
binding!!.canStatusLogo.setImageResource(R.drawable.ic_check_logo)
|
binding.canStatusLogo.setImageResource(R.drawable.ic_check_logo)
|
||||||
canSaved = true
|
canSaved = true
|
||||||
} else {
|
} else {
|
||||||
binding!!.canStatusText.text = getString(R.string.can_status_negative)
|
binding.canStatusText.text = getString(R.string.can_status_negative)
|
||||||
binding!!.canStatusLogo.setImageResource(R.drawable.ic_info_logo)
|
binding.canStatusLogo.setImageResource(R.drawable.ic_info_logo)
|
||||||
canSaved = false
|
canSaved = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,11 +157,11 @@ class HomeFragment : Fragment() {
|
|||||||
*/
|
*/
|
||||||
private fun pinState() {
|
private fun pinState() {
|
||||||
if (viewModel.userPin.length in 4..12) {
|
if (viewModel.userPin.length in 4..12) {
|
||||||
binding!!.pinStatusText.text = getString(R.string.pin_status_saved)
|
binding.pinStatusText.text = getString(R.string.pin_status_saved)
|
||||||
binding!!.pinStatusLogo.setImageResource(R.drawable.ic_check_logo)
|
binding.pinStatusLogo.setImageResource(R.drawable.ic_check_logo)
|
||||||
} else {
|
} else {
|
||||||
binding!!.pinStatusText.text = getString(R.string.pin_status_negative)
|
binding.pinStatusText.text = getString(R.string.pin_status_negative)
|
||||||
binding!!.pinStatusLogo.setImageResource(R.drawable.ic_info_logo)
|
binding.pinStatusLogo.setImageResource(R.drawable.ic_info_logo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,10 +187,10 @@ class HomeFragment : Fragment() {
|
|||||||
/**
|
/**
|
||||||
* Displays a help message to the user explaining what the CAN is
|
* Displays a help message to the user explaining what the CAN is
|
||||||
*/
|
*/
|
||||||
private fun displayMessage() {
|
private fun displayMessage(title: String, message: String) {
|
||||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(getString(R.string.can_question))
|
.setTitle(title)
|
||||||
.setMessage(getString(R.string.can_explanation))
|
.setMessage(message)
|
||||||
.setPositiveButton(R.string.return_text){_, _ -> }
|
.setPositiveButton(R.string.return_text){_, _ -> }
|
||||||
.show()
|
.show()
|
||||||
val title = dialog.findViewById<TextView>(R.id.alertTitle)
|
val title = dialog.findViewById<TextView>(R.id.alertTitle)
|
||||||
@@ -188,22 +203,22 @@ class HomeFragment : Fragment() {
|
|||||||
*/
|
*/
|
||||||
private fun updateAction(canIsSaved: Boolean) {
|
private fun updateAction(canIsSaved: Boolean) {
|
||||||
if (canIsSaved) {
|
if (canIsSaved) {
|
||||||
binding!!.detectionActionText.text = getString(R.string.action_detect)
|
binding.detectionActionText.text = getString(R.string.action_detect)
|
||||||
enableReaderMode()
|
enableReaderMode()
|
||||||
binding!!.homeActionButton.visibility = View.GONE
|
binding.homeActionButton.visibility = View.GONE
|
||||||
binding!!.homeHelpButton.visibility = View.GONE
|
binding.homeHelpButton.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
binding!!.detectionActionText.text = getString(R.string.action_detect_unavailable)
|
binding.detectionActionText.text = getString(R.string.action_detect_unavailable)
|
||||||
binding!!.homeActionButton.text = getString(R.string.add_can_text)
|
binding.homeActionButton.text = getString(R.string.add_can_text)
|
||||||
binding!!.homeActionButton.setOnClickListener {
|
binding.homeActionButton.setOnClickListener {
|
||||||
val action = HomeFragmentDirections.actionHomeFragmentToCanFragment(saving = true, fromhome = true)
|
val action = HomeFragmentDirections.actionHomeFragmentToCanFragment(saving = true, fromhome = true)
|
||||||
findNavController().navigate(action)
|
findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
binding!!.homeHelpButton.setOnClickListener {
|
binding.homeHelpButton.setOnClickListener {
|
||||||
displayMessage()
|
displayMessage(getString(R.string.can_question), getString(R.string.can_explanation))
|
||||||
}
|
}
|
||||||
binding!!.homeActionButton.visibility = View.VISIBLE
|
binding.homeActionButton.visibility = View.VISIBLE
|
||||||
binding!!.homeHelpButton.visibility = View.VISIBLE
|
binding.homeHelpButton.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,11 +226,11 @@ class HomeFragment : Fragment() {
|
|||||||
* Resets the error message and allows the user to try again
|
* Resets the error message and allows the user to try again
|
||||||
*/
|
*/
|
||||||
private fun reset() {
|
private fun reset() {
|
||||||
binding!!.homeActionButton.text = getString(R.string.try_again_text)
|
binding.homeActionButton.text = getString(R.string.try_again_text)
|
||||||
binding!!.homeActionButton.setOnClickListener {
|
binding.homeActionButton.setOnClickListener {
|
||||||
updateAction(canSaved)
|
updateAction(canSaved)
|
||||||
}
|
}
|
||||||
binding!!.homeActionButton.visibility = View.VISIBLE
|
binding.homeActionButton.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,11 +239,11 @@ class HomeFragment : Fragment() {
|
|||||||
private fun enableReaderMode() {
|
private fun enableReaderMode() {
|
||||||
val adapter = NfcAdapter.getDefaultAdapter(activity)
|
val adapter = NfcAdapter.getDefaultAdapter(activity)
|
||||||
if (adapter == null || !adapter.isEnabled) {
|
if (adapter == null || !adapter.isEnabled) {
|
||||||
binding!!.detectionActionText.text = getString(R.string.nfc_not_available)
|
binding.detectionActionText.text = getString(R.string.nfc_not_available)
|
||||||
} else {
|
} else {
|
||||||
adapter.enableReaderMode(activity, { tag ->
|
adapter.enableReaderMode(activity, { tag ->
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
binding!!.detectionActionText.text = getString(R.string.card_detected)
|
binding.detectionActionText.text = getString(R.string.card_detected)
|
||||||
}
|
}
|
||||||
val card = IsoDep.get(tag)
|
val card = IsoDep.get(tag)
|
||||||
card.timeout = 32768
|
card.timeout = 32768
|
||||||
@@ -249,11 +264,11 @@ class HomeFragment : Fragment() {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
when(e) {
|
when(e) {
|
||||||
is TagLostException -> requireActivity().runOnUiThread {
|
is TagLostException -> requireActivity().runOnUiThread {
|
||||||
binding!!.detectionActionText.text = getString(R.string.id_card_removed_early)
|
binding.detectionActionText.text = getString(R.string.id_card_removed_early)
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
else -> requireActivity().runOnUiThread {
|
else -> requireActivity().runOnUiThread {
|
||||||
binding!!.detectionActionText.text = getString(R.string.nfc_reading_error)
|
binding.detectionActionText.text = getString(R.string.nfc_reading_error)
|
||||||
viewModel.deleteCan(requireContext())
|
viewModel.deleteCan(requireContext())
|
||||||
canState()
|
canState()
|
||||||
reset()
|
reset()
|
||||||
@@ -269,7 +284,9 @@ class HomeFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
if (receiver != null) {
|
||||||
requireActivity().unregisterReceiver(receiver)
|
requireActivity().unregisterReceiver(receiver)
|
||||||
binding = null
|
}
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -3,6 +3,9 @@ package com.tarkvaraprojekt.mobileauthapp.NFC;
|
|||||||
import android.nfc.tech.IsoDep;
|
import android.nfc.tech.IsoDep;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.tarkvaraprojekt.mobileauthapp.auth.AuthAppException;
|
||||||
|
import com.tarkvaraprojekt.mobileauthapp.auth.InvalidPINException;
|
||||||
|
|
||||||
import org.bouncycastle.crypto.BlockCipher;
|
import org.bouncycastle.crypto.BlockCipher;
|
||||||
import org.bouncycastle.crypto.engines.AESEngine;
|
import org.bouncycastle.crypto.engines.AESEngine;
|
||||||
import org.bouncycastle.crypto.macs.CMac;
|
import org.bouncycastle.crypto.macs.CMac;
|
||||||
@@ -156,7 +159,7 @@ public class Comms {
|
|||||||
private byte[] getResponse(byte[] APDU, String log) throws IOException {
|
private byte[] getResponse(byte[] APDU, String log) throws IOException {
|
||||||
byte[] response = idCard.transceive(APDU);
|
byte[] response = idCard.transceive(APDU);
|
||||||
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
||||||
throw new RuntimeException(String.format("%s failed.", log));
|
throw new AuthAppException(String.format("%s failed.", log), 500);
|
||||||
}
|
}
|
||||||
Log.i(log, Hex.toHexString(response));
|
Log.i(log, Hex.toHexString(response));
|
||||||
return response;
|
return response;
|
||||||
@@ -204,7 +207,7 @@ public class Comms {
|
|||||||
// verify chip's MAC and return session keys
|
// verify chip's MAC and return session keys
|
||||||
MAC = getMAC(createAPDU(dataForMACIncomplete, publicKey.getEncoded(false), 65), keyMAC);
|
MAC = getMAC(createAPDU(dataForMACIncomplete, publicKey.getEncoded(false), 65), keyMAC);
|
||||||
if (!Hex.toHexString(response, 4, 8).equals(Hex.toHexString(MAC))) {
|
if (!Hex.toHexString(response, 4, 8).equals(Hex.toHexString(MAC))) {
|
||||||
throw new RuntimeException("Could not verify chip's MAC."); // *Should* never happen.
|
throw new AuthAppException("Could not verify chip's MAC.", 448); // *Should* never happen.
|
||||||
}
|
}
|
||||||
return new byte[][]{keyEnc, keyMAC};
|
return new byte[][]{keyEnc, keyMAC};
|
||||||
|
|
||||||
@@ -221,7 +224,7 @@ public class Comms {
|
|||||||
selectFile(FID, info);
|
selectFile(FID, info);
|
||||||
byte[] response = getResponse(new byte[0], readFile, "Read binary");
|
byte[] response = getResponse(new byte[0], readFile, "Read binary");
|
||||||
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
||||||
throw new RuntimeException(String.format("Could not read %s", info));
|
throw new AuthAppException(String.format("Could not read %s", info), 500);
|
||||||
}
|
}
|
||||||
return encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE);
|
return encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE);
|
||||||
}
|
}
|
||||||
@@ -290,7 +293,7 @@ public class Comms {
|
|||||||
private void selectFile(byte[] FID, String info) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
|
private void selectFile(byte[] FID, String info) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
|
||||||
byte[] response = getResponse(FID, selectFile, String.format("Select %s", info));
|
byte[] response = getResponse(FID, selectFile, String.format("Select %s", info));
|
||||||
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
||||||
throw new RuntimeException(String.format("Could not select %s", info));
|
throw new AuthAppException(String.format("Could not select %s", info), 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +318,7 @@ public class Comms {
|
|||||||
// select and read the personal data elementary files
|
// select and read the personal data elementary files
|
||||||
for (byte index : lastBytes) {
|
for (byte index : lastBytes) {
|
||||||
|
|
||||||
if (index > 15 || index < 1) throw new RuntimeException("Invalid personal data FID.");
|
if (index > 15 || index < 1) throw new AuthAppException("Invalid personal data FID.", 500);
|
||||||
FID[1] = index;
|
FID[1] = index;
|
||||||
|
|
||||||
// store the decrypted datum
|
// store the decrypted datum
|
||||||
@@ -348,9 +351,9 @@ public class Comms {
|
|||||||
|
|
||||||
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
||||||
if (response[response.length - 2] == 0x69 && response[response.length - 1] == (byte) 0x83) {
|
if (response[response.length - 2] == 0x69 && response[response.length - 1] == (byte) 0x83) {
|
||||||
throw new RuntimeException("Invalid PIN. Authentication method blocked.");
|
throw new AuthAppException("Invalid PIN. Authentication method blocked.", 446);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException(String.format("Invalid PIN. Attempts left: %d.", response[response.length - 1] + 64));
|
throw new InvalidPINException(response[response.length - 1] + 64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,7 +382,7 @@ public class Comms {
|
|||||||
readCert[3] = (byte) (certificate.length % 256);
|
readCert[3] = (byte) (certificate.length % 256);
|
||||||
byte[] response = getResponse(new byte[0], readCert, "Read the certificate");
|
byte[] response = getResponse(new byte[0], readCert, "Read the certificate");
|
||||||
if (response[response.length - 2] == 0x6b && response[response.length - 1] == 0x00) {
|
if (response[response.length - 2] == 0x6b && response[response.length - 1] == 0x00) {
|
||||||
throw new RuntimeException("Wrong read parameters.");
|
throw new AuthAppException("Wrong read parameters.", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the range containing a portion of the certificate and decrypt it
|
// Set the range containing a portion of the certificate and decrypt it
|
||||||
@@ -414,7 +417,7 @@ public class Comms {
|
|||||||
|
|
||||||
byte[] response = getResponse(Env, MSESetEnv, "Set environment");
|
byte[] response = getResponse(Env, MSESetEnv, "Set environment");
|
||||||
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
||||||
throw new RuntimeException("Setting the environment failed.");
|
throw new AuthAppException("Setting the environment failed.", 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalAuthenticate[4] = (byte) (0x1d + 16 * (token.length / 16));
|
InternalAuthenticate[4] = (byte) (0x1d + 16 * (token.length / 16));
|
||||||
@@ -422,7 +425,7 @@ public class Comms {
|
|||||||
response = getResponse(token, InternalAuthenticate, "Internal Authenticate");
|
response = getResponse(token, InternalAuthenticate, "Internal Authenticate");
|
||||||
|
|
||||||
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
||||||
throw new RuntimeException("Signing the token failed.");
|
throw new AuthAppException("Signing the token failed.", 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] signature = encryptDecryptData(Arrays.copyOfRange(response, 3, 115), Cipher.DECRYPT_MODE);
|
byte[] signature = encryptDecryptData(Arrays.copyOfRange(response, 3, 115), Cipher.DECRYPT_MODE);
|
||||||
|
@@ -19,23 +19,24 @@ class Pin2Fragment : Fragment() {
|
|||||||
|
|
||||||
private val viewModel: SmartCardViewModel by activityViewModels()
|
private val viewModel: SmartCardViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentPin2Binding? = null
|
private var _binding: FragmentPin2Binding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentPin2Binding.inflate(inflater, container, false)
|
_binding = FragmentPin2Binding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding!!.nextButton.setOnClickListener {
|
binding.nextButton.setOnClickListener {
|
||||||
checkPin2Length()
|
checkPin2Length()
|
||||||
}
|
}
|
||||||
binding!!.cancelButton.setOnClickListener {
|
binding.cancelButton.setOnClickListener {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +46,7 @@ class Pin2Fragment : Fragment() {
|
|||||||
* then it is saved to the viewModel.
|
* then it is saved to the viewModel.
|
||||||
*/
|
*/
|
||||||
private fun checkPin2Length() {
|
private fun checkPin2Length() {
|
||||||
val enteredPin2 = binding!!.pin2EditText.editText?.text.toString()
|
val enteredPin2 = binding.pin2EditText.editText?.text.toString()
|
||||||
if (enteredPin2.length in 5..12) {
|
if (enteredPin2.length in 5..12) {
|
||||||
viewModel.setUserPin2(enteredPin2)
|
viewModel.setUserPin2(enteredPin2)
|
||||||
} else {
|
} else {
|
||||||
@@ -66,7 +67,7 @@ class Pin2Fragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -29,7 +29,8 @@ class PinFragment : Fragment() {
|
|||||||
|
|
||||||
private val viewModel: SmartCardViewModel by activityViewModels()
|
private val viewModel: SmartCardViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentPinBinding? = null
|
private var _binding: FragmentPinBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
// Navigation arguments:
|
// Navigation arguments:
|
||||||
// saving = true means that the user must be returned to the settings menu
|
// saving = true means that the user must be returned to the settings menu
|
||||||
@@ -42,8 +43,8 @@ class PinFragment : Fragment() {
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentPinBinding.inflate(inflater, container, false)
|
_binding = FragmentPinBinding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -51,28 +52,27 @@ class PinFragment : Fragment() {
|
|||||||
checkIfSkip()
|
checkIfSkip()
|
||||||
// Switch should be not visible when user is in savings mode
|
// Switch should be not visible when user is in savings mode
|
||||||
if (args.saving) {
|
if (args.saving) {
|
||||||
binding!!.savePinQuestion.visibility = View.GONE
|
binding.savePinQuestion.visibility = View.GONE
|
||||||
binding!!.saveLayout.visibility = View.GONE
|
binding.saveLayout.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
saveToggle =
|
saveToggle =
|
||||||
activity?.getPreferences(Context.MODE_PRIVATE)?.getBoolean("saveToggle", true) == true //Android Studio recommendation to get rid of Boolean?.
|
activity?.getPreferences(Context.MODE_PRIVATE)?.getBoolean("saveToggle", true) == true //Android Studio recommendation to get rid of Boolean?.
|
||||||
Log.i("myLogging", activity?.getPreferences(Context.MODE_PRIVATE)?.getBoolean("saveToggle", true).toString())
|
|
||||||
if (!saveToggle) {
|
if (!saveToggle) {
|
||||||
binding!!.saveSwitch.isChecked = false
|
binding.saveSwitch.isChecked = false
|
||||||
}
|
}
|
||||||
binding!!.saveSwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding.saveSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
binding!!.saveStatus.text = getString(R.string.pin_save_on)
|
binding.saveStatus.text = getString(R.string.pin_save_on)
|
||||||
activity?.getPreferences(Context.MODE_PRIVATE)?.edit()?.putBoolean("saveToggle", true)?.apply()
|
activity?.getPreferences(Context.MODE_PRIVATE)?.edit()?.putBoolean("saveToggle", true)?.apply()
|
||||||
} else {
|
} else {
|
||||||
binding!!.saveStatus.text = getString(R.string.pin_save_off)
|
binding.saveStatus.text = getString(R.string.pin_save_off)
|
||||||
activity?.getPreferences(Context.MODE_PRIVATE)?.edit()?.putBoolean("saveToggle", false)?.apply()
|
activity?.getPreferences(Context.MODE_PRIVATE)?.edit()?.putBoolean("saveToggle", false)?.apply()
|
||||||
}
|
}
|
||||||
saveToggle = !saveToggle
|
saveToggle = !saveToggle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding!!.buttonContinue.setOnClickListener { checkEnteredPin() }
|
binding.buttonContinue.setOnClickListener { checkEnteredPin() }
|
||||||
binding!!.buttonCancel.setOnClickListener { goToTheStart() }
|
binding.buttonCancel.setOnClickListener { goToTheStart() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,7 +130,7 @@ class PinFragment : Fragment() {
|
|||||||
* allowed to modify the entered PIN 1.
|
* allowed to modify the entered PIN 1.
|
||||||
*/
|
*/
|
||||||
private fun checkEnteredPin() {
|
private fun checkEnteredPin() {
|
||||||
val enteredPin = binding!!.pinTextField.editText?.text.toString()
|
val enteredPin = binding.pinTextField.editText?.text.toString()
|
||||||
if (enteredPin.length in 4..12) {
|
if (enteredPin.length in 4..12) {
|
||||||
viewModel.setUserPin(enteredPin)
|
viewModel.setUserPin(enteredPin)
|
||||||
if (args.saving) {
|
if (args.saving) {
|
||||||
@@ -152,6 +152,6 @@ class PinFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -24,7 +24,8 @@ class ResultFragment : Fragment() {
|
|||||||
|
|
||||||
private val paramsModel: ParametersViewModel by activityViewModels()
|
private val paramsModel: ParametersViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentResultBinding? = null
|
private var _binding: FragmentResultBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val args: ResultFragmentArgs by navArgs()
|
private val args: ResultFragmentArgs by navArgs()
|
||||||
|
|
||||||
@@ -33,8 +34,8 @@ class ResultFragment : Fragment() {
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentResultBinding.inflate(inflater, container, false)
|
_binding = FragmentResultBinding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -86,7 +87,7 @@ class ResultFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -20,33 +20,34 @@ class UserFragment : Fragment() {
|
|||||||
|
|
||||||
private val viewModel: SmartCardViewModel by activityViewModels()
|
private val viewModel: SmartCardViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentUserBinding? = null
|
private var _binding: FragmentUserBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentUserBinding.inflate(inflater, container, false)
|
_binding = FragmentUserBinding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
displayInformation()
|
displayInformation()
|
||||||
binding!!.clearButton.setOnClickListener { goToTheStart() }
|
binding.clearButton.setOnClickListener { goToTheStart() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assigns text values to the fields in order to display user information.
|
* Assigns text values to the fields in order to display user information.
|
||||||
*/
|
*/
|
||||||
private fun displayInformation() {
|
private fun displayInformation() {
|
||||||
binding!!.userName.text =
|
binding.userName.text =
|
||||||
getString(R.string.user_name, viewModel.userFirstName, viewModel.userLastName)
|
getString(R.string.user_name, viewModel.userFirstName, viewModel.userLastName)
|
||||||
binding!!.identificationNumber.text = viewModel.userIdentificationNumber
|
binding.identificationNumber.text = viewModel.userIdentificationNumber
|
||||||
binding!!.gender.text = viewModel.gender
|
binding.gender.text = viewModel.gender
|
||||||
binding!!.expiration.text = viewModel.expiration.replace(" ", "/")
|
binding.expiration.text = viewModel.expiration.replace(" ", "/")
|
||||||
binding!!.citizenship.text = viewModel.citizenship
|
binding.citizenship.text = viewModel.citizenship
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,6 +60,6 @@ class UserFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
package com.tarkvaraprojekt.mobileauthapp.auth
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specialised RuntimeException class for exceptions related to the mobile authentication app.
|
||||||
|
* Possible error codes can be found at
|
||||||
|
* https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Error-codes
|
||||||
|
* @param message Error message
|
||||||
|
* @param code An error code defined in the project wiki
|
||||||
|
*/
|
||||||
|
open class AuthAppException(message: String, var code: Int) : RuntimeException(message)
|
@@ -0,0 +1,7 @@
|
|||||||
|
package com.tarkvaraprojekt.mobileauthapp.auth
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An AuthAppException for when the user entered CAN does not match the one read from the ID-card
|
||||||
|
* @see AuthAppException
|
||||||
|
*/
|
||||||
|
class InvalidCANException : AuthAppException("Invalid CAN", 400)
|
@@ -0,0 +1,10 @@
|
|||||||
|
package com.tarkvaraprojekt.mobileauthapp.auth
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An AuthAppException for when the user entered PIN is not correct
|
||||||
|
* @see AuthAppException
|
||||||
|
*/
|
||||||
|
class InvalidPINException(val remainingAttempts: Int) : AuthAppException(
|
||||||
|
"Invalid PIN" + (if (remainingAttempts>0) "" else ". Authentication method blocked."),
|
||||||
|
if (remainingAttempts>0) 401 else 446
|
||||||
|
)
|
@@ -25,7 +25,8 @@ class SettingsFragment : Fragment() {
|
|||||||
|
|
||||||
private val viewModel: SmartCardViewModel by activityViewModels()
|
private val viewModel: SmartCardViewModel by activityViewModels()
|
||||||
|
|
||||||
private var binding: FragmentSettingsBinding? = null
|
private var _binding: FragmentSettingsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private var showPin: Boolean = false
|
private var showPin: Boolean = false
|
||||||
|
|
||||||
@@ -34,8 +35,8 @@ class SettingsFragment : Fragment() {
|
|||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
_binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -43,10 +44,10 @@ class SettingsFragment : Fragment() {
|
|||||||
showCanField()
|
showCanField()
|
||||||
showPinField()
|
showPinField()
|
||||||
togglePinButton()
|
togglePinButton()
|
||||||
binding!!.canMenuAction.setOnClickListener { canAction() }
|
binding.canMenuAction.setOnClickListener { canAction() }
|
||||||
binding!!.pinMenuAction.setOnClickListener { pinAction() }
|
binding.pinMenuAction.setOnClickListener { pinAction() }
|
||||||
binding!!.pinMenuShow.setOnClickListener { togglePin() }
|
binding.pinMenuShow.setOnClickListener { togglePin() }
|
||||||
binding!!.returnButton.setOnClickListener { backToHome() }
|
binding.returnButton.setOnClickListener { backToHome() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,11 +65,11 @@ class SettingsFragment : Fragment() {
|
|||||||
*/
|
*/
|
||||||
private fun showCanField() {
|
private fun showCanField() {
|
||||||
if (viewModel.userCan.length == 6) {
|
if (viewModel.userCan.length == 6) {
|
||||||
binding!!.canSaved.text = getString(R.string.saved_can, viewModel.userCan)
|
binding.canSaved.text = getString(R.string.saved_can, viewModel.userCan)
|
||||||
binding!!.canMenuAction.text = getString(R.string.can_delete)
|
binding.canMenuAction.text = getString(R.string.can_delete)
|
||||||
} else {
|
} else {
|
||||||
binding!!.canSaved.text = getString(R.string.saved_can, getString(R.string.missing))
|
binding.canSaved.text = getString(R.string.saved_can, getString(R.string.missing))
|
||||||
binding!!.canMenuAction.text = getString(R.string.add_can_text)
|
binding.canMenuAction.text = getString(R.string.add_can_text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,16 +96,16 @@ class SettingsFragment : Fragment() {
|
|||||||
*/
|
*/
|
||||||
private fun showPinField() {
|
private fun showPinField() {
|
||||||
if (viewModel.userPin.length in 4..12) {
|
if (viewModel.userPin.length in 4..12) {
|
||||||
binding!!.pinMenuShow.visibility = Button.VISIBLE
|
binding.pinMenuShow.visibility = Button.VISIBLE
|
||||||
if (showPin)
|
if (showPin)
|
||||||
binding!!.pinSaved.text = getString(R.string.saved_pin, viewModel.userPin)
|
binding.pinSaved.text = getString(R.string.saved_pin, viewModel.userPin)
|
||||||
else
|
else
|
||||||
binding!!.pinSaved.text = getString(R.string.saved_pin, getString(R.string.hidden_pin))
|
binding.pinSaved.text = getString(R.string.saved_pin, getString(R.string.hidden_pin))
|
||||||
binding!!.pinMenuAction.text = getString(R.string.pin1_delete)
|
binding.pinMenuAction.text = getString(R.string.pin1_delete)
|
||||||
} else {
|
} else {
|
||||||
binding!!.pinMenuShow.visibility = Button.GONE
|
binding.pinMenuShow.visibility = Button.GONE
|
||||||
binding!!.pinSaved.text = getString(R.string.saved_pin, getString(R.string.missing))
|
binding.pinSaved.text = getString(R.string.saved_pin, getString(R.string.missing))
|
||||||
binding!!.pinMenuAction.text = getString(R.string.pin1_add)
|
binding.pinMenuAction.text = getString(R.string.pin1_add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,9 +139,9 @@ class SettingsFragment : Fragment() {
|
|||||||
*/
|
*/
|
||||||
private fun togglePinButton() {
|
private fun togglePinButton() {
|
||||||
if (showPin) {
|
if (showPin) {
|
||||||
binding!!.pinMenuShow.text = getString(R.string.hide)
|
binding.pinMenuShow.text = getString(R.string.hide)
|
||||||
} else {
|
} else {
|
||||||
binding!!.pinMenuShow.text = getString(R.string.show)
|
binding.pinMenuShow.text = getString(R.string.show)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +155,7 @@ class SettingsFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -19,7 +19,7 @@
|
|||||||
<string name="nfc_not_available">NFC is not turned on or is not supported by the phone</string>
|
<string name="nfc_not_available">NFC is not turned on or is not supported by the phone</string>
|
||||||
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
||||||
<string name="id_card_removed_early">ID card was removed too early</string>
|
<string name="id_card_removed_early">ID card was removed too early</string>
|
||||||
<string name="wrong_pin">Wrong PIN 1. Tries on the card left %s</string>
|
<string name="wrong_pin">Wrong PIN 1. %s tries left on the card</string>
|
||||||
|
|
||||||
<!-- string resources for HomeFragment -->
|
<!-- string resources for HomeFragment -->
|
||||||
<string name="pin_status_saved">PIN 1 saved</string>
|
<string name="pin_status_saved">PIN 1 saved</string>
|
||||||
@@ -29,7 +29,11 @@
|
|||||||
<string name="help_text">HELP</string>
|
<string name="help_text">HELP</string>
|
||||||
<string name="can_question">What is CAN?</string>
|
<string name="can_question">What is CAN?</string>
|
||||||
<string name="can_explanation">CAN is a 6 digit code that is needed to communicate with an ID card. It can be found on the ID card under the card holder\'s picture with a title KASUTAJA ALLKIRI/HOLDER\'S SIGNATURE.</string>
|
<string name="can_explanation">CAN is a 6 digit code that is needed to communicate with an ID card. It can be found on the ID card under the card holder\'s picture with a title KASUTAJA ALLKIRI/HOLDER\'S SIGNATURE.</string>
|
||||||
|
<string name="problem_parameters">Problem with parameters</string>
|
||||||
|
<string name="problem_challenge">Challenge is missing</string>
|
||||||
|
<string name="problem_authurl">AuthUrl is missing</string>
|
||||||
|
<string name="problem_originurl">OriginUrl is missing</string>
|
||||||
|
<string name="problem_other">Unspecified problem with parameters</string>
|
||||||
<!-- string resources for PinFragment -->
|
<!-- string resources for PinFragment -->
|
||||||
<string name="pin_view">Please enter PIN 1</string>
|
<string name="pin_view">Please enter PIN 1</string>
|
||||||
<string name="hint_pin">PIN 1</string>
|
<string name="hint_pin">PIN 1</string>
|
||||||
@@ -71,8 +75,8 @@
|
|||||||
<string name="saved_can">CAN: %s</string>
|
<string name="saved_can">CAN: %s</string>
|
||||||
<string name="can_delete">Delete CAN</string>
|
<string name="can_delete">Delete CAN</string>
|
||||||
<string name="saved_pin">PIN1: %s</string>
|
<string name="saved_pin">PIN1: %s</string>
|
||||||
<string name="pin1_add">Add PIN1</string>
|
<string name="pin1_add">Add PIN 1</string>
|
||||||
<string name="pin1_delete">Delete PIN1</string>
|
<string name="pin1_delete">Delete PIN 1</string>
|
||||||
<string name="missing">not saved</string>
|
<string name="missing">not saved</string>
|
||||||
<string name="show">SHOW</string>
|
<string name="show">SHOW</string>
|
||||||
<string name="hide">HIDE</string>
|
<string name="hide">HIDE</string>
|
||||||
@@ -80,4 +84,13 @@
|
|||||||
<string name="menu_unavailable_message">Settings are currently unavailable</string>
|
<string name="menu_unavailable_message">Settings are currently unavailable</string>
|
||||||
<string name="can_deleted">CAN deleted</string>
|
<string name="can_deleted">CAN deleted</string>
|
||||||
<string name="pin_deleted">PIN 1 deleted</string>
|
<string name="pin_deleted">PIN 1 deleted</string>
|
||||||
|
|
||||||
|
<string name="err_unknown">Unknown error</string>
|
||||||
|
<string name="tag_lost">Connection between device and ID-card lost</string>
|
||||||
|
<string name="err_reading_card">Failed to read data from the ID-card</string>
|
||||||
|
<string name="err_internal">Internal error</string>
|
||||||
|
<string name="err_bad_data">Read bad data from the ID-card, try using the card again</string>
|
||||||
|
<string name="err_parameter">Required parameter is missing or invalid</string>
|
||||||
|
<string name="err_authentication">Failed to authenticate</string>
|
||||||
|
<string name="err_card_locked">Card locked</string>
|
||||||
</resources>
|
</resources>
|
@@ -18,7 +18,7 @@
|
|||||||
<string name="nfc_not_available">NFC ei ole sisse lülitatud või puudub telefonil NFC võimekus</string>
|
<string name="nfc_not_available">NFC ei ole sisse lülitatud või puudub telefonil NFC võimekus</string>
|
||||||
<string name="nfc_reading_error">Sisestatud CAN ei ole vastavuses ID kaardiga</string>
|
<string name="nfc_reading_error">Sisestatud CAN ei ole vastavuses ID kaardiga</string>
|
||||||
<string name="id_card_removed_early">ID kaart eemaldati liiga vara</string>
|
<string name="id_card_removed_early">ID kaart eemaldati liiga vara</string>
|
||||||
<string name="wrong_pin">Vale PIN 1. ID kaardil PIN 1 sisetamise kordi alles: %s</string>
|
<string name="wrong_pin">Vale PIN 1. ID kaardil PIN 1 sisetamise katseid järel: %s</string>
|
||||||
|
|
||||||
<!-- string resources for HomeFragment -->
|
<!-- string resources for HomeFragment -->
|
||||||
<string name="pin_status_saved">PIN 1 on salvestatud</string>
|
<string name="pin_status_saved">PIN 1 on salvestatud</string>
|
||||||
@@ -28,7 +28,11 @@
|
|||||||
<string name="help_text">INFO</string>
|
<string name="help_text">INFO</string>
|
||||||
<string name="can_question">Mis on CAN?</string>
|
<string name="can_question">Mis on CAN?</string>
|
||||||
<string name="can_explanation">CAN on 6 kohaline numbritest koosnev kood, mida on vaja ID kaardiga suhtlemiseks. CAN-i leiab ID kaardilt omaniku pildi alt pealkirjaga KASUTAJA ALLKIRI/HOLDER\'S SIGNATURE.</string>
|
<string name="can_explanation">CAN on 6 kohaline numbritest koosnev kood, mida on vaja ID kaardiga suhtlemiseks. CAN-i leiab ID kaardilt omaniku pildi alt pealkirjaga KASUTAJA ALLKIRI/HOLDER\'S SIGNATURE.</string>
|
||||||
|
<string name="problem_parameters">Probleem parameetritega</string>
|
||||||
|
<string name="problem_challenge">Puudub challenge parameeter</string>
|
||||||
|
<string name="problem_authurl">Puudub AuthUrl parameeter</string>
|
||||||
|
<string name="problem_originurl">Puudub OriginUrl parameeter</string>
|
||||||
|
<string name="problem_other">Täpsustamata probleem parameetritega</string>
|
||||||
<!-- string resources for PinFragment -->
|
<!-- string resources for PinFragment -->
|
||||||
<string name="pin_view">Palun sisesta PIN 1</string>
|
<string name="pin_view">Palun sisesta PIN 1</string>
|
||||||
<string name="hint_pin">PIN 1</string>
|
<string name="hint_pin">PIN 1</string>
|
||||||
@@ -79,4 +83,13 @@
|
|||||||
<string name="menu_unavailable_message">Seaded pole hetkel saadaval</string>
|
<string name="menu_unavailable_message">Seaded pole hetkel saadaval</string>
|
||||||
<string name="can_deleted">CAN kustatud</string>
|
<string name="can_deleted">CAN kustatud</string>
|
||||||
<string name="pin_deleted">PIN 1 kustatud</string>
|
<string name="pin_deleted">PIN 1 kustatud</string>
|
||||||
|
|
||||||
|
<string name="err_unknown">Tundmatu viga</string>
|
||||||
|
<string name="tag_lost">Ühendus seadme ja kaardi vahel katkes</string>
|
||||||
|
<string name="err_reading_card">Ei saanud ID-kaardilt andmeid lugeda</string>
|
||||||
|
<string name="err_internal">Rakendusesisene viga</string>
|
||||||
|
<string name="err_bad_data">ID-kaardilt loeti vigased andmed, proovi uuesti kaarti kasutada</string>
|
||||||
|
<string name="err_parameter">Vigane või puuduv parameeter</string>
|
||||||
|
<string name="err_authentication">Autentimine ebaõnnestus</string>
|
||||||
|
<string name="err_card_locked">Kaart lukus</string>
|
||||||
</resources>
|
</resources>
|
@@ -17,7 +17,7 @@
|
|||||||
<string name="nfc_not_available">NFC is not turned on or is not supported by the phone</string>
|
<string name="nfc_not_available">NFC is not turned on or is not supported by the phone</string>
|
||||||
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
<string name="nfc_reading_error">The provided CAN does not match the ID card</string>
|
||||||
<string name="id_card_removed_early">ID card was removed too early</string>
|
<string name="id_card_removed_early">ID card was removed too early</string>
|
||||||
<string name="wrong_pin">Wrong PIN 1. Tries on the card left %s</string>
|
<string name="wrong_pin">Wrong PIN 1. %s tries left on the card</string>
|
||||||
|
|
||||||
<!-- string resources for HomeFragment -->
|
<!-- string resources for HomeFragment -->
|
||||||
<string name="pin_status_saved">PIN 1 saved</string>
|
<string name="pin_status_saved">PIN 1 saved</string>
|
||||||
@@ -27,7 +27,11 @@
|
|||||||
<string name="help_text">HELP</string>
|
<string name="help_text">HELP</string>
|
||||||
<string name="can_question">What is CAN?</string>
|
<string name="can_question">What is CAN?</string>
|
||||||
<string name="can_explanation">CAN is a 6 digit code that is needed to communicate with an ID card. It can be found on the ID card under the card holder\'s picture with a title KASUTAJA ALLKIRI/HOLDER\'S SIGNATURE.</string>
|
<string name="can_explanation">CAN is a 6 digit code that is needed to communicate with an ID card. It can be found on the ID card under the card holder\'s picture with a title KASUTAJA ALLKIRI/HOLDER\'S SIGNATURE.</string>
|
||||||
|
<string name="problem_parameters">Problem with parameters</string>
|
||||||
|
<string name="problem_challenge">Challenge is missing</string>
|
||||||
|
<string name="problem_authurl">AuthUrl is missing</string>
|
||||||
|
<string name="problem_originurl">OriginUrl is missing</string>
|
||||||
|
<string name="problem_other">Unspecified problem with parameters</string>
|
||||||
<!-- string resources for PinFragment -->
|
<!-- string resources for PinFragment -->
|
||||||
<string name="pin_view">Please enter PIN 1</string>
|
<string name="pin_view">Please enter PIN 1</string>
|
||||||
<string name="hint_pin">PIN 1</string>
|
<string name="hint_pin">PIN 1</string>
|
||||||
@@ -78,4 +82,13 @@
|
|||||||
<string name="menu_unavailable_message">Settings are currently unavailable</string>
|
<string name="menu_unavailable_message">Settings are currently unavailable</string>
|
||||||
<string name="can_deleted">CAN deleted</string>
|
<string name="can_deleted">CAN deleted</string>
|
||||||
<string name="pin_deleted">PIN 1 deleted</string>
|
<string name="pin_deleted">PIN 1 deleted</string>
|
||||||
|
|
||||||
|
<string name="err_unknown">Unknown error</string>
|
||||||
|
<string name="tag_lost">Connection between device and ID-card lost</string>
|
||||||
|
<string name="err_reading_card">Failed to read data from the ID-card</string>
|
||||||
|
<string name="err_internal">Internal error</string>
|
||||||
|
<string name="err_bad_data">Read bad data from the ID-card, try using the card again</string>
|
||||||
|
<string name="err_parameter">Required parameter is missing or invalid</string>
|
||||||
|
<string name="err_authentication">Failed to authenticate</string>
|
||||||
|
<string name="err_card_locked">Card locked</string>
|
||||||
</resources>
|
</resources>
|
@@ -9,7 +9,6 @@ import android.view.View
|
|||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import com.example.testmobileapp.databinding.ActivityMainBinding
|
import com.example.testmobileapp.databinding.ActivityMainBinding
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import com.koushikdutta.ion.Ion
|
import com.koushikdutta.ion.Ion
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
@@ -35,9 +34,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
authLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { response ->
|
authLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { response ->
|
||||||
if (response.resultCode == Activity.RESULT_OK) {
|
if (response.resultCode == Activity.RESULT_OK) {
|
||||||
// Currently we are not actually checking whether we get a valid token.
|
|
||||||
// For testing purposes only, to make sure that we are able to get a response at all.
|
|
||||||
binding.loginTextView.text = getString(R.string.auth_success)
|
binding.loginTextView.text = getString(R.string.auth_success)
|
||||||
|
// Logs are used to show what information can be retrieved from the mobileauthapp.
|
||||||
Log.i("getResult", response.data?.getStringExtra("token").toString())
|
Log.i("getResult", response.data?.getStringExtra("token").toString())
|
||||||
Log.i("getResult", response.data?.getStringExtra("result").toString())
|
Log.i("getResult", response.data?.getStringExtra("result").toString())
|
||||||
var user = ""
|
var user = ""
|
||||||
@@ -48,14 +46,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Log.i("getResult", "unable to retrieve name from principal")
|
Log.i("getResult", "unable to retrieve name from principal")
|
||||||
}
|
}
|
||||||
showResult(user)
|
showResult(user)
|
||||||
/*
|
|
||||||
binding.loginOptionNfcButton.text = "Log Out"
|
|
||||||
binding.loginOptionNfcButton.setOnClickListener {
|
|
||||||
binding.loginOptionNfcButton.text = "NFC auth"
|
|
||||||
binding.loginOptionNfcButton.setOnClickListener { getData() }
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
if (response.resultCode == Activity.RESULT_CANCELED) {
|
if (response.resultCode == Activity.RESULT_CANCELED) {
|
||||||
binding.loginTextView.text = getString(R.string.auth_failure)
|
binding.loginTextView.text = getString(R.string.auth_failure)
|
||||||
@@ -114,6 +104,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
binding.resultLayout.visibility = View.VISIBLE
|
binding.resultLayout.visibility = View.VISIBLE
|
||||||
binding.resultObject.text = getString(R.string.hello, user)
|
binding.resultObject.text = getString(R.string.hello, user)
|
||||||
binding.buttonForget.setOnClickListener {
|
binding.buttonForget.setOnClickListener {
|
||||||
|
binding.loginTextView.text = getString(R.string.login_text)
|
||||||
binding.resultObject.text = ""
|
binding.resultObject.text = ""
|
||||||
binding.resultLayout.visibility = View.GONE
|
binding.resultLayout.visibility = View.GONE
|
||||||
binding.loginOptions.visibility = View.VISIBLE
|
binding.loginOptions.visibility = View.VISIBLE
|
||||||
|
Reference in New Issue
Block a user