31 Commits

Author SHA1 Message Date
Henrik Lepson
86c6a32032 Release notes wiki link added to the README file 2021-10-10 14:17:35 +03:00
Henrik Lepson
57ed281aa2 Merge pull request #8 from TanelOrumaa/integrateNFC
The UI and NFC class now work together. Iteration 2 related functionality.
2021-10-10 13:55:19 +03:00
TanelOrumaa
e975a3e262 Created the outline for token creation. 2021-10-09 18:06:13 +03:00
TanelOrumaa
aeb9a5dada Merge pull request #7 from TanelOrumaa/MOB-33
Mob-33 to main
2021-10-09 15:25:28 +03:00
Henrik Lepson
76fecd766f MOB-14 two small fixes 2021-10-08 21:24:21 +03:00
Henrik Lepson
cc35723c07 MOB-14 improved the user experience 2021-10-08 20:09:36 +03:00
Henrik Lepson
d46fb21075 Added link to the Use Case Tests to the readme. 2021-10-08 19:20:54 +03:00
Henrik Lepson
7803e35ac6 Updated use cases in the wiki. 2021-10-08 17:59:32 +03:00
Henrik Lepson
fc56825ed0 MOB-35 started integrating the NFC class with the UI 2021-10-06 21:02:33 +03:00
Lemmo Lavonen
f93b37c535 Merge pull request #6 from TanelOrumaa/nfc
Adds a class for exchanging data with the ID-card over NFC.
2021-10-06 14:18:24 +03:00
Lemmo Lavonen
0e92b3dd8d Remove .idea 2021-10-06 14:01:19 +03:00
Tanel Orumaa
812647cade MOB-33 Reverting invalid test 2021-10-06 11:09:00 +03:00
TanelOrumaa
f92266f187 MOB-33 Enabled CI on all branches. 2021-10-06 11:07:00 +03:00
Tanel Orumaa
20d08f8d6c MOB-33 Testing unit tests CI 2021-10-06 11:02:56 +03:00
TanelOrumaa
efcb784e9b MOB-33 YAML changes to run junit tests. 2021-10-06 10:57:08 +03:00
Henrik Lepson
634d168378 Merge pull request #5 from TanelOrumaa/viewReworks
PIN1 layout changes
2021-10-06 09:23:46 +03:00
Lemmo Lavonen
c98eb92c2f Fixes package name. 2021-10-05 23:52:27 +03:00
Lemmo Lavonen
c30fff7a6f Adds a constructor for convenience in setting up Comms. 2021-10-05 19:58:27 +03:00
Lemmo Lavonen
08006dc1ac Adds a class for exchanging data with the ID-card over NFC. 2021-10-04 23:50:14 +03:00
Henrik Lepson
c4d7da07ad MOB-12 PIN1 view layout changed + pin length check 2021-10-04 20:07:45 +03:00
Henrik Lepson
67ad5296da MOB-12 started working on new PIN1 view layout 2021-10-04 19:54:17 +03:00
Henrik Lepson
be14c30b0b MOB-35 reworked CAN view 2021-10-03 15:45:28 +03:00
Henrik Lepson
a45de7bad4 added missing drawable file 2021-10-03 14:55:19 +03:00
Henrik Lepson
276173365a MOB-14 reworked AuthFragment xml layout + countdown added 2021-10-03 14:46:42 +03:00
Henrik Lepson
7582a317bf MOB-14 change text style and removed unnecessary logs 2021-10-03 13:45:35 +03:00
Henrik Lepson
804b4472e8 MOB-14 changed the placement of views inside the layout 2021-10-03 13:28:09 +03:00
TanelOrumaa
24b5e89751 MOB-9 Added gitignore to ignore intellij and kotlin unnecessary files. 2021-10-02 14:19:47 +03:00
Henrik Lepson
5e92403b93 MOB-12 MOB-14 MOB-35 MOB-36 base for creating views and navigation created 2021-09-26 14:42:38 +03:00
Henrik Lepson
8c4b7b613e MOB-36 basic setup for navigation completed 2021-09-25 16:28:37 +03:00
Henrik Lepson
724ff0a869 MOB-9 create apk with github action 2021-09-24 20:20:52 +03:00
Henrik Lepson
1da4466e1c MOB-9 set up base android project 2021-09-24 20:07:39 +03:00
54 changed files with 2110 additions and 1 deletions

33
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
defaults:
run:
working-directory: ./MobileAuthApp
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: gradlew permission
run: chmod +x ./gradlew
- name: run gradlew
run: ./gradlew assembleDebug
- name: run tests
run: ./gradlew test
- name: upload apk
uses: actions/upload-artifact@v2
with:
name: apk
path: MobileAuthApp/app/build/outputs/apk/debug/app-debug.apk

72
.gitignore vendored Normal file
View File

@@ -0,0 +1,72 @@
.DS_Store
.idea/shelf
/confluence/target
/dependencies/repo
/android.tests.dependencies
/dependencies/android.tests.dependencies
/dist
/local
/gh-pages
/ideaSDK
/clionSDK
/android-studio/sdk
out/
/tmp
/intellij
workspace.xml
*.versionsBackup
/idea/testData/debugger/tinyApp/classes*
/jps-plugin/testData/kannotator
/js/js.translator/testData/out/
/js/js.translator/testData/out-min/
/js/js.translator/testData/out-pir/
.gradle/
build/
!**/src/**/build
!**/test/**/build
*.iml
!**/testData/**/*.iml
.idea
.idea/libraries/Gradle*.xml
.idea/libraries/Maven*.xml
.idea/artifacts/PILL_*.xml
.idea/artifacts/KotlinPlugin.xml
.idea/modules
.idea/runConfigurations/JPS_*.xml
.idea/runConfigurations/PILL_*.xml
.idea/runConfigurations/_FP_*.xml
.idea/runConfigurations/_MT_*.xml
.idea/libraries
.idea/modules.xml
.idea/gradle.xml
.idea/compiler.xml
.idea/inspectionProfiles/profiles_settings.xml
.idea/.name
.idea/artifacts/dist_auto_*
.idea/artifacts/dist.xml
.idea/artifacts/ideaPlugin.xml
.idea/artifacts/kotlinc.xml
.idea/artifacts/kotlin_compiler_jar.xml
.idea/artifacts/kotlin_plugin_jar.xml
.idea/artifacts/kotlin_jps_plugin_jar.xml
.idea/artifacts/kotlin_daemon_client_jar.xml
.idea/artifacts/kotlin_imports_dumper_compiler_plugin_jar.xml
.idea/artifacts/kotlin_main_kts_jar.xml
.idea/artifacts/kotlin_compiler_client_embeddable_jar.xml
.idea/artifacts/kotlin_reflect_jar.xml
.idea/artifacts/kotlin_stdlib_js_ir_*
.idea/artifacts/kotlin_test_js_ir_*
.idea/artifacts/kotlin_stdlib_wasm_*
.idea/jarRepositories.xml
.idea/csv-plugin.xml
.idea/libraries-with-intellij-classes.xml
node_modules/
.rpt2_cache/
libraries/tools/kotlin-test-js-runner/lib/
local.properties
buildSrcTmp/
distTmp/
outTmp/
/test.output
/kotlin-native/dist
kotlin-ide/

9
MobileAuthApp/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/captures
.externalNativeBuild
.cxx
local.properties

1
MobileAuthApp/app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,62 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'androidx.navigation.safeargs.kotlin'
}
android {
compileSdk 31
defaultConfig {
applicationId "com.tarkvaraprojekt.mobileauthapp"
minSdk 26
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
buildFeatures {
viewBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
def lifecycle_version = "2.3.1"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//To use activityViewModels
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
//For navigation component
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
//ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
//For cryptography
implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69'
}

21
MobileAuthApp/app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package com.tarkvaraprojekt.mobileauthapp
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.tarkvaraprojekt.mobileauthapp", appContext.packageName)
}
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tarkvaraprojekt.mobileauthapp">
<uses-permission android:name="android.permission.NFC" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MobileAuthApp">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,117 @@
package com.tarkvaraprojekt.mobileauthapp
import android.app.Activity
import android.content.Context
import android.nfc.NfcAdapter
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
import java.lang.Exception
import kotlin.concurrent.thread
/**
* Fragment that asks the user to detect the ID card with mobile NFC chip.
* Currently contains a next button that won't be needed later on.
* This button is just needed to test navigation between fragments so that every step exists.
*/
class AuthFragment : Fragment() {
private val viewModel: SmartCardViewModel by activityViewModels()
private var binding: FragmentAuthBinding? = null
private lateinit var timer: CountDownTimer
private var timeRemaining: Int = 90
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentAuthBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
timer = object : CountDownTimer((timeRemaining * 1000).toLong(), 1000) {
override fun onTick(p0: Long) {
timeRemaining--
if (timeRemaining == 0) {
binding!!.timeCounter.text = getString(R.string.no_time)
} else {
binding!!.timeCounter.text = getString(R.string.time_left, timeRemaining)
}
}
override fun onFinish() {
Thread.sleep(750)
goToTheStart()
}
}.start()
binding!!.nextButton.setOnClickListener { goToNextFragment() }
binding!!.cancelButton.setOnClickListener { goToTheStart() }
val adapter = NfcAdapter.getDefaultAdapter(activity)
if (adapter != null)
getInfoFromIdCard(adapter)
}
private fun getInfoFromIdCard(adapter: NfcAdapter) {
adapter.enableReaderMode(activity, { tag ->
timer.cancel()
requireActivity().runOnUiThread {
binding!!.timeCounter.text = getString(R.string.card_detected)
}
val card = IsoDep.get(tag)
card.timeout = 32768
card.use {
try {
val comms = Comms(it, viewModel.userCan)
val response = comms.readPersonalData(byteArrayOf(1, 2, 6))
viewModel.setUserFirstName(response[1])
viewModel.setUserLastName(response[0])
viewModel.setUserIdentificationNumber(response[2])
requireActivity().runOnUiThread{
binding!!.timeCounter.text = getString(R.string.data_read)
}
} catch (e: Exception) {
requireActivity().runOnUiThread {
binding!!.timeCounter.text = getString(R.string.no_success)
}
// Gives user some time to read the error message
Thread.sleep(1000)
goToTheStart()
} finally {
adapter.disableReaderMode(activity)
}
}
}, NfcAdapter.FLAG_READER_NFC_A, null)
}
private fun goToNextFragment() {
timer.cancel()
findNavController().navigate(R.id.action_authFragment_to_userFragment)
}
private fun goToTheStart() {
viewModel.clearUserInfo()
timer.cancel()
findNavController().navigate(R.id.action_authFragment_to_homeFragment)
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
}

View File

@@ -0,0 +1,61 @@
package com.tarkvaraprojekt.mobileauthapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentCanBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
/**
* Fragment that deals with asking the user for six digit CAN
*/
class CanFragment : Fragment() {
private val viewModel: SmartCardViewModel by activityViewModels()
private var binding: FragmentCanBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCanBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding!!.nextButton.setOnClickListener { goToNextFragment() }
binding!!.cancelButton.setOnClickListener { goToTheStart() }
}
private fun goToNextFragment() {
val enteredCan = binding!!.canEditText.editText?.text.toString()
if (enteredCan.length != 6) {
Toast.makeText(requireContext(), getString(R.string.length_can), Toast.LENGTH_SHORT)
.show()
} else {
viewModel.setUserCan(
binding!!.canEditText.editText?.text.toString()
)
findNavController().navigate(R.id.action_canFragment_to_authFragment)
}
}
private fun goToTheStart() {
viewModel.clearUserInfo()
findNavController().navigate(R.id.action_canFragment_to_homeFragment)
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
}

View File

@@ -0,0 +1,42 @@
package com.tarkvaraprojekt.mobileauthapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentHomeBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
class HomeFragment : Fragment() {
private val viewModel: SmartCardViewModel by activityViewModels()
private var binding: FragmentHomeBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding!!.beginButton.setOnClickListener { goToNextFragment() }
}
private fun goToNextFragment() {
findNavController().navigate(R.id.action_homeFragment_to_pinFragment)
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}

View File

@@ -0,0 +1,24 @@
package com.tarkvaraprojekt.mobileauthapp
import android.nfc.NfcAdapter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import com.tarkvaraprojekt.mobileauthapp.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var navigationController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navigationController = navHostFragment.navController
}
}

View File

@@ -0,0 +1,328 @@
package com.tarkvaraprojekt.mobileauthapp.NFC;
import android.nfc.tech.IsoDep;
import android.util.Log;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Comms {
private static final byte[] master = { // select Main AID
0, -92, 4, 12, 16, -96, 0, 0, 0, 119, 1, 8, 0, 7, 0, 0, -2, 0, 0, 1, 0
};
private static final byte[] MSESetAT = { // manage security environment: set authentication template
0, 34, -63, -92, 15, -128, 10, 4, 0, 127, 0, 7, 2, 2, 4, 2, 4, -125, 1, 2, 0
};
private static final byte[] GAGetNonce = { // general authenticate: get nonce
16, -122, 0, 0, 2, 124, 0, 0
};
private static final byte[] GAMapNonceIncomplete = {
16, -122, 0, 0, 69, 124, 67, -127, 65
};
private static final byte[] GAKeyAgreementIncomplete = {
16, -122, 0, 0, 69, 124, 67, -125, 65
};
private static final byte[] GAMutualAuthenticationIncomplete = {
0, -122, 0, 0, 12, 124, 10, -123, 8
};
private static final byte[] dataForMACIncomplete = {
127, 73, 79, 6, 10, 4, 0, 127, 0, 7, 2, 2, 4, 2, 4, -122, 65
};
private static final byte[] masterSec = {
12, -92, 4, 12, 45, -121, 33, 1
};
private static final byte[] personal = { // select personal data DF
12, -92, 1, 12, 29, -121, 17, 1
};
private static final byte[] read = { // read binary
12, -80, 0, 0, 13, -105, 1, 0
};
private IsoDep idCard;
private final byte[] keyEnc;
private final byte[] keyMAC;
private byte ssc; // Send sequence counter.
/**
* The constructor performs PACE and stores the session keys
*
* @param idCard link to the card
* @param CAN the card authentication number
*/
public Comms(IsoDep idCard, String CAN) throws IOException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
idCard.connect();
this.idCard = idCard;
long start = System.currentTimeMillis();
byte[][] keys = PACE(CAN);
Log.i("Pace duration", String.valueOf(System.currentTimeMillis() - start));
keyEnc = keys[0];
keyMAC = keys[1];
}
public byte[] getAuthenticationCertificate() {
return new byte[0];
}
/**
* Calculates the message authentication code
*
* @param APDU the byte array on which the CMAC algorithm is performed
* @param keyMAC the key for performing CMAC
* @return MAC
*/
private byte[] getMAC(byte[] APDU, byte[] keyMAC) {
BlockCipher blockCipher = new AESEngine();
CMac cmac = new CMac(blockCipher);
cmac.init(new KeyParameter(keyMAC));
cmac.update(APDU, 0, APDU.length);
byte[] MAC = new byte[cmac.getMacSize()];
cmac.doFinal(MAC, 0);
return Arrays.copyOf(MAC, 8);
}
/**
* Creates an application protocol data unit
*
* @param template the byte array to be used as a template
* @param data the data necessary for completing the APDU
* @param extra the missing length of the APDU being created
* @return the complete APDU
*/
private byte[] createAPDU(byte[] template, byte[] data, int extra) {
byte[] APDU = Arrays.copyOf(template, template.length + extra);
System.arraycopy(data, 0, APDU, template.length, data.length);
return APDU;
}
/**
* Creates a cipher key
*
* @param unpadded the array to be used as the basis for the key
* @param last the last byte in the appended padding
* @return the constructed key
*/
private byte[] createKey(byte[] unpadded, byte last) throws NoSuchAlgorithmException {
byte[] padded = Arrays.copyOf(unpadded, unpadded.length + 4);
padded[padded.length - 1] = last;
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
return messageDigest.digest(padded);
}
/**
* Decrypts the nonce
*
* @param encryptedNonce the encrypted nonce received from the chip
* @param CAN the card access number provided by the user
* @return the decrypted nonce
*/
private byte[] decryptNonce(byte[] encryptedNonce, String CAN) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] decryptionKey = createKey(CAN.getBytes(StandardCharsets.UTF_8), (byte) 3);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptionKey, "AES"), new IvParameterSpec(new byte[16]));
return cipher.doFinal(encryptedNonce);
}
/**
* Attempts to use the PACE protocol to create a secure channel with an Estonian ID-card
*
* @param CAN the card access number
*/
private byte[][] PACE(String CAN) throws IOException, NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
// select the ECC applet on the chip
byte[] response = idCard.transceive(master);
Log.i("Select applet", Hex.toHexString(response));
// initiate PACE
response = idCard.transceive(MSESetAT);
Log.i("Authentication template", Hex.toHexString(response));
// get nonce
response = idCard.transceive(GAGetNonce);
Log.i("Get nonce", Hex.toHexString(response));
byte[] decryptedNonce = decryptNonce(Arrays.copyOfRange(response, 4, response.length - 2), CAN);
// generate an EC keypair and exchange public keys with the chip
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256r1");
BigInteger privateKey = new BigInteger(255, new SecureRandom()).add(BigInteger.ONE); // should be in [1, spec.getN()-1], but this is good enough for this application
ECPoint publicKey = spec.getG().multiply(privateKey).normalize();
byte[] APDU = createAPDU(GAMapNonceIncomplete, publicKey.getEncoded(false), 66);
response = idCard.transceive(APDU);
Log.i("Map nonce", Hex.toHexString(response));
ECPoint cardPublicKey = spec.getCurve().decodePoint(Arrays.copyOfRange(response, 4, 69));
// calculate the new base point, use it to generate a new keypair, and exchange public keys
ECPoint sharedSecret = cardPublicKey.multiply(privateKey);
ECPoint mappedECBasePoint = spec.getG().multiply(new BigInteger(1, decryptedNonce)).add(sharedSecret).normalize();
privateKey = new BigInteger(255, new SecureRandom()).add(BigInteger.ONE);
publicKey = mappedECBasePoint.multiply(privateKey).normalize();
APDU = createAPDU(GAKeyAgreementIncomplete, publicKey.getEncoded(false), 66);
response = idCard.transceive(APDU);
Log.i("Key agreement", Hex.toHexString(response));
cardPublicKey = spec.getCurve().decodePoint(Arrays.copyOfRange(response, 4, 69));
// generate the session keys and exchange MACs to verify them
sharedSecret = cardPublicKey.multiply(privateKey).normalize();
byte[] encodedSecret = sharedSecret.getAffineXCoord().getEncoded();
byte[] keyEnc = createKey(encodedSecret, (byte) 1);
byte[] keyMAC = createKey(encodedSecret, (byte) 2);
APDU = createAPDU(dataForMACIncomplete, cardPublicKey.getEncoded(false), 65);
byte[] MAC = getMAC(APDU, keyMAC);
APDU = createAPDU(GAMutualAuthenticationIncomplete, MAC, 9);
response = idCard.transceive(APDU);
Log.i("Mutual authentication", Hex.toHexString(response));
// if the chip-side verification fails, crash and burn
if (response.length == 2) throw new RuntimeException("Invalid CAN.");
// otherwise verify chip's MAC and return session keys
APDU = createAPDU(dataForMACIncomplete, publicKey.getEncoded(false), 65);
MAC = getMAC(APDU, keyMAC);
if (!Hex.toHexString(response, 4, 8).equals(Hex.toHexString(MAC))) {
throw new RuntimeException("Could not verify chip's MAC."); // Should never happen.
}
return new byte[][]{keyEnc, keyMAC};
}
/**
* Encrypts or decrypts the APDU data
*
* @param data the array containing the data to be processed
* @param mode indicates whether to en- or decrypt the data
* @return the result of encryption or decryption
*/
private byte[] encryptDecryptData(byte[] data, int mode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
SecretKeySpec secretKeySpec = new SecretKeySpec(keyEnc, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] iv = Arrays.copyOf(cipher.doFinal(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ssc}), 16);
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(mode, secretKeySpec, new IvParameterSpec(iv));
return cipher.doFinal(data);
}
/**
* Constructs APDUs suitable for the secure channel.
*
* @param data the data to be encrypted
* @param incomplete the array to be used as a template
* @return the constructed APDU
*/
private byte[] createSecureAPDU(byte[] data, byte[] incomplete) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
ssc++;
byte[] encryptedData = new byte[0];
int length = 16 * (1 + data.length / 16);
// construct the required array and calculate the MAC based on it
byte[] macData = new byte[data.length > 0 ? 48 + length : 48];
macData[15] = ssc; // first block contains the ssc
System.arraycopy(incomplete, 0, macData, 16, 4); // second block has the command
macData[20] = -128; // elements are terminated by 0x80 and zero-padded to the next block
System.arraycopy(incomplete, 5, macData, 32, 3); // third block contains appropriately encapsulated data/Le
if (data.length > 0) { // if the APDU has data, add padding and encrypt it
byte[] paddedData = Arrays.copyOf(data, length);
paddedData[data.length] = -128;
encryptedData = encryptDecryptData(paddedData, Cipher.ENCRYPT_MODE);
System.arraycopy(encryptedData, 0, macData, 35, encryptedData.length);
}
macData[35 + encryptedData.length] = -128;
byte[] MAC = getMAC(macData, keyMAC);
// construct the APDU using the encrypted data and the MAC
byte[] APDU = new byte[incomplete.length + encryptedData.length + MAC.length + 3];
System.arraycopy(incomplete, 0, APDU, 0, incomplete.length);
if (encryptedData.length > 0) {
System.arraycopy(encryptedData, 0, APDU, incomplete.length, encryptedData.length);
}
System.arraycopy(new byte[]{-114, 8}, 0, APDU, incomplete.length + encryptedData.length, 2); // MAC is encapsulated using the tag 0x8E
System.arraycopy(MAC, 0, APDU, incomplete.length + encryptedData.length + 2, MAC.length);
ssc++;
return APDU;
}
/**
* Gets the contents of the personal data dedicated file
*
* @param FID the last bytes of file identifiers being requested
* @return array containing the data strings
*
*/
public String[] readPersonalData(byte[] FID) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
String[] personalData = new String[FID.length];
byte[] data;
byte[] APDU;
byte[] response;
// select the personal data dedicated file
data = new byte[]{80, 0}; // personal data DF FID
APDU = createSecureAPDU(data, personal);
response = idCard.transceive(APDU);
Log.i("Select personal data DF", Hex.toHexString(response));
// select and read the first 8 elementary files in the DF
for (int i = 0; i < FID.length; i++) {
byte index = FID[i];
if (index > 15 || index < 1) throw new RuntimeException("Invalid personal data FID.");
data[1] = index;
APDU = createSecureAPDU(data, personal);
response = idCard.transceive(APDU);
Log.i(String.format("Select EF 500%d", index), Hex.toHexString(response));
APDU = createSecureAPDU(new byte[0], read);
response = idCard.transceive(APDU);
Log.i(String.format("Read binary EF 500%d", index), Hex.toHexString(response));
// store the decrypted datum
byte[] raw = encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE);
int indexOfTerminator = Hex.toHexString(raw).lastIndexOf("80") / 2;
personalData[i] = new String(Arrays.copyOfRange(raw, 0, indexOfTerminator));
}
return personalData;
}
}

View File

@@ -0,0 +1,67 @@
package com.tarkvaraprojekt.mobileauthapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentPinBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
/**
* Fragment that deals with asking the user for PIN1
*/
class PinFragment : Fragment() {
private val viewModel: SmartCardViewModel by activityViewModels()
private var binding: FragmentPinBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentPinBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding!!.nextButton.setOnClickListener { goToNextFragment() }
binding!!.cancelButton.setOnClickListener { goToTheStart() }
// Currently PIN 1 is not required and thus this step is immediately skipped.
// In the future the UI flow will be changed in the nav_graph.
goToNextFragment()
}
private fun goToNextFragment() {
val enteredPin1 = binding!!.pinEditText.editText?.text.toString()
if (enteredPin1.length in 4..12) {
viewModel.setUserPin(
binding!!.pinEditText.editText?.text.toString()
)
findNavController().navigate(R.id.action_pinFragment_to_canFragment)
} else {
// Currently it is not important to enter PIN1 so we will allow the user to leave this field empty
//Toast.makeText(requireContext(), getString(R.string.length_pin), Toast.LENGTH_SHORT)
// .show()
viewModel.setUserPin("1234")
findNavController().navigate(R.id.action_pinFragment_to_canFragment)
}
}
private fun goToTheStart() {
viewModel.clearUserInfo()
findNavController().navigate(R.id.action_pinFragment_to_homeFragment)
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
}

View File

@@ -0,0 +1,52 @@
package com.tarkvaraprojekt.mobileauthapp
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentUserBinding
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
/**
* Fragment that is used to display the persons name and national identification number.
* Currently needed in order to test that the app is working and information is read
* from the ID card via NFC.
*/
class UserFragment : Fragment() {
private val viewModel: SmartCardViewModel by activityViewModels()
private var binding: FragmentUserBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentUserBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding!!.userName.text =
getString(R.string.user_name, viewModel.userFirstName, viewModel.userLastName)
binding!!.identificationNumber.text = viewModel.userIdentificationNumber
binding!!.clearButton.setOnClickListener { goToTheStart() }
}
private fun goToTheStart() {
viewModel.clearUserInfo()
findNavController().navigate(R.id.action_userFragment_to_homeFragment)
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
}

View File

@@ -0,0 +1,22 @@
package com.tarkvaraprojekt.mobileauthapp.auth
import android.nfc.tech.IsoDep
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
import java.math.BigInteger
class Authenticator(val comms : Comms) {
public fun authenticate(nonce: BigInteger, challengeUrl: String, pin1: String) {
// Ask PIN 1 from the user and get the authentication certificate from the ID card.
val authenticationCertificate : ByteArray = comms.getAuthenticationCertificate();
// Create the authentication token (OpenID X509)
// Hash the authentication token.
// Send the authentication token hash to the ID card for signing and get signed authentication token as response.
// Return the signed authentication token.
}
}

View File

@@ -0,0 +1,50 @@
package com.tarkvaraprojekt.mobileauthapp.model
import androidx.lifecycle.ViewModel
class SmartCardViewModel: ViewModel() {
private var _userPin: String = ""
val userPin get() = _userPin
private var _userCan: String = ""
val userCan get() = _userCan
private var _userFirstName: String = ""
val userFirstName get() = _userFirstName
private var _userLastName: String = ""
val userLastName get() = _userLastName
private var _userIdentificationNumber: String = ""
val userIdentificationNumber get() = _userIdentificationNumber
fun clearUserInfo() {
_userPin = ""
_userCan = ""
_userFirstName = ""
_userLastName = ""
_userIdentificationNumber = ""
}
fun setUserPin(newUserPin: String) {
_userPin = newUserPin
}
fun setUserCan(newUserCan: String) {
_userCan = newUserCan
}
fun setUserFirstName(newUserFirstName: String) {
_userFirstName = newUserFirstName
}
fun setUserLastName(newUserLastName: String) {
_userLastName = newUserLastName
}
fun setUserIdentificationNumber(newUserIdentificationNumber: String) {
_userIdentificationNumber = newUserIdentificationNumber
}
}

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,4H4C2.9,4 2,4.9 2,6v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.1,4 20,4zM7.64,15H6.49v-4.5l-0.9,0.66l-0.58,-0.89L6.77,9h0.87V15zM13.5,15H9.61v-1.02c1.07,-1.07 1.77,-1.77 2.13,-2.15c0.4,-0.42 0.54,-0.69 0.54,-1.06c0,-0.4 -0.31,-0.72 -0.81,-0.72c-0.52,0 -0.8,0.39 -0.9,0.72l-1.01,-0.42c0.01,-0.02 0.18,-0.76 1,-1.15c0.69,-0.33 1.48,-0.2 1.95,0.03c0.86,0.44 0.91,1.24 0.91,1.48c0,0.64 -0.31,1.26 -0.92,1.86c-0.25,0.25 -0.72,0.71 -1.4,1.39l0.03,0.05h2.37V15zM18.75,14.15C18.67,14.28 18.19,15 16.99,15c-0.04,0 -1.6,0.08 -2.05,-1.51l1.03,-0.41c0.03,0.1 0.19,0.86 1.02,0.86c0.41,0 0.89,-0.28 0.89,-0.77c0,-0.55 -0.48,-0.79 -1.04,-0.79h-0.5v-1h0.46c0.33,0 0.88,-0.14 0.88,-0.72c0,-0.39 -0.31,-0.65 -0.75,-0.65c-0.5,0 -0.74,0.32 -0.85,0.64l-0.99,-0.41C15.2,9.9 15.68,9 16.94,9c1.09,0 1.54,0.64 1.62,0.75c0.33,0.5 0.28,1.16 0.02,1.57c-0.15,0.22 -0.32,0.38 -0.52,0.48v0.07c0.28,0.11 0.51,0.28 0.68,0.52C19.11,12.91 19.07,13.66 18.75,14.15z"/>
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,20L4,20L4,4h16v16zM18,6h-5c-1.1,0 -2,0.9 -2,2v2.28c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72L13,8h3v8L8,16L8,8h2L10,6L6,6v12h12L18,6z"/>
</vector>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
tools:context=".AuthFragment">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20sp">
<TextView
android:id="@+id/auth_fragment_instruction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:gravity="center"
android:text="@string/auth_instruction_text"
android:textSize="20sp" />
<ImageView
android:id="@+id/nfc_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="6dp"
android:src="@drawable/nfc_logo" />
<TextView
android:id="@+id/time_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/auth_fragment_instruction"
tools:text="@string/time_left" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- later there will be no button, but the app will continue automatically -->
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/next_text"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/cancel_button"
app:layout_constraintTop_toBottomOf="@id/card_view" />
<Button
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/cancel_text"
android:textSize="18sp"
app:layout_constraintEnd_toStartOf="@id/next_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
tools:context=".CanFragment">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20sp">
<TextView
android:id="@+id/enter_can"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:text="@string/enter_can"
android:textSize="20sp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/can_edit_text"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:hint="@string/text_can"
app:counterEnabled="true"
app:counterMaxLength="6"
app:endIconMode="password_toggle"
app:errorEnabled="true"
app:helperText="@string/example_can"
app:helperTextEnabled="true"
app:startIconDrawable="@drawable/can_logo">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberPassword"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/next_text"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/cancel_button"
app:layout_constraintTop_toBottomOf="@id/card_view" />
<Button
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/cancel_text"
android:textSize="18dp"
app:layout_constraintEnd_toStartOf="@id/next_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
tools:context=".HomeFragment">
<TextView
android:id="@+id/home_fragment_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/home_fragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Temporary button for testing purposes -->
<Button
android:id="@+id/begin_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/begin_text"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@id/home_fragment_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
tools:context=".PinFragment">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:id="@+id/pin_fragment_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:text="@string/pin_fragment" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/pin_edit_text"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:hint="@string/enter_pin"
app:counterEnabled="true"
app:counterMaxLength="12"
app:endIconMode="password_toggle"
app:errorEnabled="true"
app:helperText="@string/example_pin"
app:helperTextEnabled="true"
app:startIconDrawable="@drawable/can_logo">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberPassword"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/next_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/cancel_button"
app:layout_constraintTop_toBottomOf="@id/card_view" />
<Button
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/cancel_text"
app:layout_constraintEnd_toStartOf="@id/next_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
tools:context=".UserFragment">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20sp">
<TextView
android:id="@+id/user_name_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/user_name_label"
android:textSize="14sp" />
<TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/user_name"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/identification_number_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/identification_number_label"
android:textSize="14sp" />
<TextView
android:id="@+id/identification_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<Button
android:id="@+id/clear_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/clear_button"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.tarkvaraprojekt.mobileauthapp.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" >
<action
android:id="@+id/action_homeFragment_to_pinFragment"
app:destination="@id/pinFragment"
/>
</fragment>
<fragment
android:id="@+id/pinFragment"
android:name="com.tarkvaraprojekt.mobileauthapp.PinFragment"
android:label="fragment_pin"
tools:layout="@layout/fragment_pin" >
<action
android:id="@+id/action_pinFragment_to_canFragment"
app:destination="@id/canFragment"
app:popUpTo="@id/homeFragment" />
<action
android:id="@+id/action_pinFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/canFragment"
android:name="com.tarkvaraprojekt.mobileauthapp.CanFragment"
android:label="fragment_can"
tools:layout="@layout/fragment_can" >
<action
android:id="@+id/action_canFragment_to_authFragment"
app:destination="@id/authFragment"
app:popUpTo="@id/homeFragment"/>
<action
android:id="@+id/action_canFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/authFragment"
android:name="com.tarkvaraprojekt.mobileauthapp.AuthFragment"
android:label="fragment_auth"
tools:layout="@layout/fragment_auth" >
<action
android:id="@+id/action_authFragment_to_userFragment"
app:destination="@id/userFragment"
app:popUpTo="@id/homeFragment"/>
<action
android:id="@+id/action_authFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/userFragment"
android:name="com.tarkvaraprojekt.mobileauthapp.UserFragment"
android:label="fragment_user"
tools:layout="@layout/fragment_user" >
<action
android:id="@+id/action_userFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true"/>
</fragment>
</navigation>

View File

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MobileAuthApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/blue_200</item>
<item name="colorPrimaryVariant">@color/blue_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/orange_200</item>
<item name="colorSecondaryVariant">@color/orange_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="blue_200">#90caf9</color>
<color name="blue_500">#2196f3</color>
<color name="blue_700">#1976d2</color>
<color name="orange_200">#ffcc80</color>
<color name="orange_700">#f57c00</color>
</resources>

View File

@@ -0,0 +1,34 @@
<resources>
<string name="app_name">Mobile Authenticator</string>
<string name="home_fragment">Work in progress</string>
<string name="begin_text">ALUSTA</string>
<string name="next_text">EDASI</string>
<string name="cancel_text">KATKESTA</string>
<!-- string resources for PinFragment -->
<string name="pin_fragment">Palun sisesta PIN 1</string>
<string name="enter_pin">PIN 1</string>
<string name="example_pin">Näide. 1234</string>
<string name="length_pin">PIN 1 lubatud pikkus on 4..12</string>
<!-- string resources for CanFragment -->
<string name="example_can">Näide. 123456</string>
<string name="text_can">CAN</string>
<string name="enter_can">Sisesta ID kaardi CAN (Card Access Number)</string>
<string name="length_can">CANi pikkus on vale</string>
<string name="card_detected">Kaart on tuvastatud. Hoia kaarti vastu telefoni.</string>
<string name="data_read">Andmed loetud. Võid edasi minna.</string>
<!-- string resources for AuthFragment layout -->
<string name="auth_instruction_text">ID kaardiga ühenduse loomiseks pane kaart vastu telefoni</string>
<string name="time_left">Aega on jäänud %d sek</string>
<string name="no_time">Aeg on otsas</string>
<string name="no_success">Vale CAN</string>
<!-- string resources for UserFragment layout -->
<string name="user_name_label">NIMI</string>
<string name="user_name">%1$s %2$s</string>
<string name="identification_number_label">ISIKUKOOD</string>
<string name="clear_button">UNUSTA</string>
</resources>

View File

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MobileAuthApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/blue_500</item>
<item name="colorPrimaryVariant">@color/blue_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/orange_200</item>
<item name="colorSecondaryVariant">@color/orange_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,17 @@
package com.tarkvaraprojekt.mobileauthapp
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@@ -0,0 +1,22 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
ext {
nav_version = "2.3.1"
kotlin_version = "1.4.30"
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Fri Sep 24 20:04:57 EEST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

185
MobileAuthApp/gradlew vendored Normal file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
MobileAuthApp/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,10 @@
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
}
rootProject.name = "Mobile Auth App"
include ':app'

View File

@@ -4,10 +4,14 @@ This is a proof-of-concept project for creating an Android app for authenticatin
[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)
[Use Cases](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Use-Cases) *updated 08.10*
[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)

0
gradle.properties Normal file
View File