7 Commits

Author SHA1 Message Date
stargateprovider
1c7e51d69a Merge remote-tracking branch 'origin/MOB-24-user_notifications' into MOB-24-user_notifications
# Conflicts:
#	MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt
#	MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/CanFragment.kt
#	MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java
#	MobileAuthApp/app/src/main/res/values-en/strings.xml
#	MobileAuthApp/app/src/main/res/values-et/strings.xml
#	MobileAuthApp/app/src/main/res/values/strings.xml
2021-12-06 23:26:14 +02:00
stargateprovider
b623dadff5 Rebase and/or merge with main 2021-12-06 23:18:01 +02:00
stargateprovider
4fcdccfb5e More specific errors and notifications 2021-12-06 22:48:08 +02:00
stargateprovider
70991ae682 Added basic notifications to the user
For when exceptions occur when communicating with the ID-card
2021-12-06 22:47:57 +02:00
stargateprovider
dbc758fb14 More specific errors and notifications 2021-12-04 21:22:54 +02:00
stargateprovider
1037d672d5 Merge branch 'main' into MOB-24-user_notifications
# Conflicts:
#	MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/AuthFragment.kt
#	MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java
2021-12-04 19:12:25 +02:00
stargateprovider
73efb00368 Added basic notifications to the user
For when exceptions occur when communicating with the ID-card
2021-11-08 23:51:07 +02:00
16 changed files with 117 additions and 77 deletions

View File

@@ -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
/** /**
@@ -107,6 +111,9 @@ class AuthFragment : Fragment() {
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 {
@@ -119,31 +126,49 @@ 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)
} }

View File

@@ -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);

View File

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

View File

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

View File

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

View File

@@ -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>
@@ -84,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>

View File

@@ -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>
@@ -83,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>

View File

@@ -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>
@@ -82,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>

View File

@@ -1 +0,0 @@
#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50}#nav{padding:30px}#nav a{font-weight:700;color:#2c3e50}#nav a.router-link-exact-active{color:#42b983}.container>div[data-v-2dcb24ca]{margin-top:2vh}.loginButton[data-v-2dcb24ca]{height:4vh;width:20vh;line-height:3vh}.loginButton>p[data-v-2dcb24ca]{font-size:3vh;text-align:center}#canvas[data-v-2dcb24ca]{height:30vh;width:30vh}nav[data-v-21165a6a]{height:5vh}div[data-v-cd8fea1a]{margin-top:2vh}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1 +0,0 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>demo-website</title><link href="/css/app.eb039c1f.css" rel="preload" as="style"><link href="/css/chunk-vendors.a251e031.css" rel="preload" as="style"><link href="/js/app.c2a68e49.js" rel="preload" as="script"><link href="/js/chunk-vendors.22b03028.js" rel="preload" as="script"><link href="/css/chunk-vendors.a251e031.css" rel="stylesheet"><link href="/css/app.eb039c1f.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but demo-website doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.22b03028.js"></script><script src="/js/app.c2a68e49.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long