3 Commits

Author SHA1 Message Date
TanelOrumaa
c28fc2be48 Merge branch 'main' of https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC 2022-01-18 00:35:16 +02:00
Kevin
7edd8189a4 Update README.md 2021-12-14 22:52:08 +02:00
TanelOrumaa
b565f6846d MOB-55 Demo website 2021-12-07 00:05:06 +02:00
17 changed files with 77 additions and 124 deletions

View File

@@ -4,6 +4,7 @@ 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
@@ -17,16 +18,11 @@ 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
/** /**
@@ -111,9 +107,6 @@ 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 {
@@ -126,48 +119,30 @@ 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: android.nfc.TagLostException) {
msgCode = R.string.tag_lost
} catch (e: InvalidCANException) {
msgCode = R.string.wrong_can_text
// If the CAN is wrong we will also delete the saved CAN so that the user won't use it again.
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) {
msgCode = R.string.err_reading_card
} catch (e: Exception) { } catch (e: Exception) {
msgCode = R.string.err_unknown when(e) {
} finally { is TagLostException -> requireActivity().runOnUiThread { binding!!.timeCounter.text = getString(R.string.id_card_removed_early) }
adapter.disableReaderMode(activity) else -> {
} when ("invalid pin") {
in e.message.toString().lowercase() -> requireActivity().runOnUiThread {
if (msgCode != 0) { val messagePieces = e.message.toString().split(" ")
requireActivity().runOnUiThread { binding.timeCounter.text = getString(R.string.wrong_pin, messagePieces[messagePieces.size - 1])
var msg = getString(msgCode) viewModel.deletePin(requireContext())
if (msgArg != null) }
msg = String.format(msg, msgArg) else -> requireActivity().runOnUiThread {
binding.timeCounter.text = msg binding.timeCounter.text = getString(R.string.wrong_can_text)
viewModel.deleteCan(requireContext())
}
}
}
} }
// Gives user some time to read the error message // Give user some time to read the error message
Thread.sleep(2000) Thread.sleep(2000)
cancelAuth() cancelAuth()
} finally {
adapter.disableReaderMode(activity)
} }
} }
}, NfcAdapter.FLAG_READER_NFC_A, null) }, NfcAdapter.FLAG_READER_NFC_A, null)

View File

@@ -3,9 +3,6 @@ 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;
@@ -159,7 +156,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 AuthAppException(String.format("%s failed.", log), 500); throw new RuntimeException(String.format("%s failed.", log));
} }
Log.i(log, Hex.toHexString(response)); Log.i(log, Hex.toHexString(response));
return response; return response;
@@ -207,7 +204,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 AuthAppException("Could not verify chip's MAC.", 448); // *Should* never happen. throw new RuntimeException("Could not verify chip's MAC."); // *Should* never happen.
} }
return new byte[][]{keyEnc, keyMAC}; return new byte[][]{keyEnc, keyMAC};
@@ -224,7 +221,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 AuthAppException(String.format("Could not read %s", info), 500); throw new RuntimeException(String.format("Could not read %s", info));
} }
return encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE); return encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE);
} }
@@ -293,7 +290,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 AuthAppException(String.format("Could not select %s", info), 500); throw new RuntimeException(String.format("Could not select %s", info));
} }
} }
@@ -318,7 +315,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 AuthAppException("Invalid personal data FID.", 500); if (index > 15 || index < 1) throw new RuntimeException("Invalid personal data FID.");
FID[1] = index; FID[1] = index;
// store the decrypted datum // store the decrypted datum
@@ -351,9 +348,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 AuthAppException("Invalid PIN. Authentication method blocked.", 446); throw new RuntimeException("Invalid PIN. Authentication method blocked.");
} else { } else {
throw new InvalidPINException(response[response.length - 1] + 64); throw new RuntimeException(String.format("Invalid PIN. Attempts left: %d.", response[response.length - 1] + 64));
} }
} }
} }
@@ -382,7 +379,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 AuthAppException("Wrong read parameters.", 400); throw new RuntimeException("Wrong read parameters.");
} }
// Set the range containing a portion of the certificate and decrypt it // Set the range containing a portion of the certificate and decrypt it
@@ -417,7 +414,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 AuthAppException("Setting the environment failed.", 500); throw new RuntimeException("Setting the environment failed.");
} }
InternalAuthenticate[4] = (byte) (0x1d + 16 * (token.length / 16)); InternalAuthenticate[4] = (byte) (0x1d + 16 * (token.length / 16));
@@ -425,7 +422,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 AuthAppException("Signing the token failed.", 500); throw new RuntimeException("Signing the token failed.");
} }
byte[] signature = encryptDecryptData(Arrays.copyOfRange(response, 3, 115), Cipher.DECRYPT_MODE); byte[] signature = encryptDecryptData(Arrays.copyOfRange(response, 3, 115), Cipher.DECRYPT_MODE);

View File

@@ -1,10 +0,0 @@
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

@@ -1,7 +0,0 @@
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

@@ -1,10 +0,0 @@
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. %s tries left on the card</string> <string name="wrong_pin">Wrong PIN 1. Tries on the card left %s</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>
@@ -75,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 PIN 1</string> <string name="pin1_add">Add PIN1</string>
<string name="pin1_delete">Delete PIN 1</string> <string name="pin1_delete">Delete PIN1</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>
@@ -84,13 +84,4 @@
<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 katseid järel: %s</string> <string name="wrong_pin">Vale PIN 1. ID kaardil PIN 1 sisetamise kordi alles: %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,13 +83,4 @@
<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. %s tries left on the card</string> <string name="wrong_pin">Wrong PIN 1. Tries on the card left %s</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,13 +82,4 @@
<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

@@ -22,11 +22,4 @@ More info about installing third party applications on the Android phones: https
The project comes with a test mobile application and a test web application that can be used to try the MobileAuthApp authentication feature even if you don't have any web applications or mobile applications that require user authentication. Both projects come with a README file that help with a setup. The project comes with a test mobile application and a test web application that can be used to try the MobileAuthApp authentication feature even if you don't have any web applications or mobile applications that require user authentication. Both projects come with a README file that help with a setup.
The mobile authentication application, when launched by the user not a website or some other application, can also read card holder's information, which can be used to verify whether the application reads the information from the ID card correctly. The mobile authentication application, when launched by the user not a website or some other application, can also read card holder's information, which can be used to verify whether the application reads the information from the ID card correctly.
### Wiki pages relevant for the "Software project" subject ### See the [Wiki](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki) for pages relevant for the "Software project" subject
* [Project Vision](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Project-Vision)
* [Release Notes](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Release-notes)
* [Project tasks](https://tvp-mobile-authentication.atlassian.net/jira/software/projects/MOB/boards/1/backlog) (Ask Tanel for JIRA permissions if needed).
* [Project plan](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Project-plan)
* [Use Cases](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Use-Cases)
* [User stories](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/User-stories)
* [Use Case Tests](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Use-Case-Tests)

View File

@@ -0,0 +1 @@
#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.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1 @@
<!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