diff --git a/.gitignore b/.gitignore index a74f48d..aa724b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,15 @@ -.gradle/ -build/ -local.properties -.idea *.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/BUILD.md b/BUILD.md deleted file mode 100644 index c7fc797..0000000 --- a/BUILD.md +++ /dev/null @@ -1,6 +0,0 @@ -Building -========= -1) ./gradlew assemble : build apks -2) ./gradlew test : run unit tests -3) ./gradlew connectedCheck : run tests on emulator -4) ./gradlew installDebug : install apk in emulator diff --git a/LICENSE b/LICENSE deleted file mode 100644 index b30e350..0000000 --- a/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2021 - -This file is part of Android Token. - -Android Token is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Android Token is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Android Token. If not, see . diff --git a/README.md b/README.md deleted file mode 100644 index 0ff5366..0000000 --- a/README.md +++ /dev/null @@ -1,18 +0,0 @@ -Auth Token (formerly, Android Token) -============= -Auth Token, (formerly Android Token) is an Android applicaion used to generate one time passwords (OTP). The application is open source and written in Java. The application supports both HOTP (event tokens, http://tools.ietf.org/html/rfc4226) and TOTP tokens (time tokens, http://tools.ietf.org/html/draft-mraihi-totp-timebased-00). - -The application supports provisioning tokens using -- [KeyUriFormat](https://github.com/google/google-authenticator/wiki/Key-Uri-Format) -- QR codes -- Manual creation. - -The application can optionally be protected with a PIN to stop unauthorised access to the software tokens. - -Tokens can be exported as a QR code or by manually copying the seed to the clipboard. - -Screen Shots ------------- -![Main View](https://github.com/markmcavoy/androidtoken/blob/wiki/mainlist.png) -![Manually adding a new token](https://github.com/markmcavoy/androidtoken/blob/wiki/add_token.png) -![Settings page](https://github.com/markmcavoy/androidtoken/blob/wiki/settings.png) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..5992a67 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,56 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "com.fly.otp" + minSdkVersion 16 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation "androidx.preference:preference:1.1.0" + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.biometric:biometric:1.0.1' + implementation 'androidx.navigation:navigation-fragment:2.3.0' + implementation 'androidx.navigation:navigation-ui:2.3.0' + implementation 'androidmads.library.qrgenearator:QRGenearator:1.0.4' + implementation 'com.google.zxing:core:3.3.2' + + testImplementation 'junit:junit:4.13' + testImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + testImplementation 'com.google.truth:truth:1.0.1' + + if(is_remote_maven){ + //com.fly.otp:optlib:0.0.1 + implementation "$groupId:optlib:$cfgvs.optlib" +// implementation "com.fly.otp:optlib:0.0.1" + }else { + implementation project(path: ':optlib') + } + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/src/androidTest/AndroidManifest.xml b/app/src/androidTest/AndroidManifest.xml similarity index 100% rename from src/androidTest/AndroidManifest.xml rename to app/src/androidTest/AndroidManifest.xml diff --git a/src/androidTest/res/drawable/icon.png b/app/src/androidTest/res/drawable/icon.png similarity index 100% rename from src/androidTest/res/drawable/icon.png rename to app/src/androidTest/res/drawable/icon.png diff --git a/src/androidTest/res/layout/main.xml b/app/src/androidTest/res/layout/main.xml similarity index 100% rename from src/androidTest/res/layout/main.xml rename to app/src/androidTest/res/layout/main.xml diff --git a/src/androidTest/res/values/strings.xml b/app/src/androidTest/res/values/strings.xml similarity index 100% rename from src/androidTest/res/values/strings.xml rename to app/src/androidTest/res/values/strings.xml diff --git a/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml similarity index 100% rename from src/main/AndroidManifest.xml rename to app/src/main/AndroidManifest.xml diff --git a/src/main/assets/fonts/fa-brands-400.ttf b/app/src/main/assets/fonts/fa-brands-400.ttf similarity index 100% rename from src/main/assets/fonts/fa-brands-400.ttf rename to app/src/main/assets/fonts/fa-brands-400.ttf diff --git a/src/main/assets/fonts/fa-regular-400.ttf b/app/src/main/assets/fonts/fa-regular-400.ttf similarity index 100% rename from src/main/assets/fonts/fa-regular-400.ttf rename to app/src/main/assets/fonts/fa-regular-400.ttf diff --git a/src/main/assets/fonts/fa-solid-900.ttf b/app/src/main/assets/fonts/fa-solid-900.ttf similarity index 100% rename from src/main/assets/fonts/fa-solid-900.ttf rename to app/src/main/assets/fonts/fa-solid-900.ttf diff --git a/src/main/java/uk/co/bitethebullet/android/token/About.java b/app/src/main/java/uk/co/bitethebullet/android/token/About.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/About.java rename to app/src/main/java/uk/co/bitethebullet/android/token/About.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/PinChange.java b/app/src/main/java/uk/co/bitethebullet/android/token/PinChange.java similarity index 96% rename from src/main/java/uk/co/bitethebullet/android/token/PinChange.java rename to app/src/main/java/uk/co/bitethebullet/android/token/PinChange.java index 64e0bc1..a7ef5e4 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/PinChange.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/PinChange.java @@ -1,135 +1,135 @@ -/* - * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 - * - * This file is part of Android Token. - * - * Android Token is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Android Token is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Android Token. If not, see . - * - */ -package uk.co.bitethebullet.android.token; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.EditText; - -import androidx.appcompat.app.AppCompatActivity; - -public class PinChange extends AppCompatActivity { - - private static final int DIALOG_INVALID_EXISTING_PIN = 0; - private static final int DIALOG_DIFF_NEW_PIN = 1; - private static final int DIALOG_NO_NEW_PIN = 2; - - Boolean hasExistingPin = true; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.pinchange); - - if(!PinManager.hasPinDefined(this)){ - hasExistingPin = false; - EditText existPinEdit = (EditText)findViewById(R.id.pinChangeExistingPinEdit); - existPinEdit.setEnabled(false); - } - - Button submitBtn = (Button)findViewById(R.id.pinChangeSubmit); - submitBtn.setOnClickListener(submitClick); - } - - private OnClickListener submitClick = new OnClickListener() { - - public void onClick(View v) { - //validate the existing pin - if(hasExistingPin){ - - String existingPin = ((EditText)findViewById(R.id.pinChangeExistingPinEdit)).getText().toString(); - - if(!PinManager.validatePin(v.getContext(), existingPin)){ - //the pin entered is not the one stored, show - //warning and stop - showDialog(DIALOG_INVALID_EXISTING_PIN); - return; - } - } - - //validate the two new pins match - String newPin1 = ((EditText)findViewById(R.id.pinChangeNew1Edit)).getText().toString(); - String newPin2 = ((EditText)findViewById(R.id.pinChangeNew2Edit)).getText().toString(); - - if(newPin1.length() == 0){ - showDialog(DIALOG_NO_NEW_PIN); - return; - } - - if(!newPin1.contentEquals(newPin2)){ - showDialog(DIALOG_DIFF_NEW_PIN); - return; - } - - //store - PinManager.storePin(v.getContext(), newPin1); - finish(); - } - }; - - private Dialog createAlertDialog(int messageId){ - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(messageId) - .setCancelable(false) - .setPositiveButton(R.string.dialogPositive, dialogClose); - - return builder.create(); - - } - - @Override - protected Dialog onCreateDialog(int id) { - Dialog d; - - switch(id){ - case DIALOG_DIFF_NEW_PIN: - d = createAlertDialog(R.string.pinAlertNewPinsDifferent); - break; - - case DIALOG_INVALID_EXISTING_PIN: - d = createAlertDialog(R.string.pinAlertInvalidPin); - break; - - case DIALOG_NO_NEW_PIN: - d = createAlertDialog(R.string.pinAlertNewPinBlank); - break; - - default: - d = null; - } - - return d; - } - - private DialogInterface.OnClickListener dialogClose = new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }; - -} +/* + * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 + * + * This file is part of Android Token. + * + * Android Token is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Android Token is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Android Token. If not, see . + * + */ +package uk.co.bitethebullet.android.token; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.EditText; + +import androidx.appcompat.app.AppCompatActivity; + +public class PinChange extends AppCompatActivity { + + private static final int DIALOG_INVALID_EXISTING_PIN = 0; + private static final int DIALOG_DIFF_NEW_PIN = 1; + private static final int DIALOG_NO_NEW_PIN = 2; + + Boolean hasExistingPin = true; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.pinchange); + + if(!PinManager.hasPinDefined(this)){ + hasExistingPin = false; + EditText existPinEdit = (EditText)findViewById(R.id.pinChangeExistingPinEdit); + existPinEdit.setEnabled(false); + } + + Button submitBtn = (Button)findViewById(R.id.pinChangeSubmit); + submitBtn.setOnClickListener(submitClick); + } + + private OnClickListener submitClick = new OnClickListener() { + + public void onClick(View v) { + //validate the existing pin + if(hasExistingPin){ + + String existingPin = ((EditText)findViewById(R.id.pinChangeExistingPinEdit)).getText().toString(); + + if(!PinManager.validatePin(v.getContext(), existingPin)){ + //the pin entered is not the one stored, show + //warning and stop + showDialog(DIALOG_INVALID_EXISTING_PIN); + return; + } + } + + //validate the two new pins match + String newPin1 = ((EditText)findViewById(R.id.pinChangeNew1Edit)).getText().toString(); + String newPin2 = ((EditText)findViewById(R.id.pinChangeNew2Edit)).getText().toString(); + + if(newPin1.length() == 0){ + showDialog(DIALOG_NO_NEW_PIN); + return; + } + + if(!newPin1.contentEquals(newPin2)){ + showDialog(DIALOG_DIFF_NEW_PIN); + return; + } + + //store + PinManager.storePin(v.getContext(), newPin1); + finish(); + } + }; + + private Dialog createAlertDialog(int messageId){ + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(messageId) + .setCancelable(false) + .setPositiveButton(R.string.dialogPositive, dialogClose); + + return builder.create(); + + } + + @Override + protected Dialog onCreateDialog(int id) { + Dialog d; + + switch(id){ + case DIALOG_DIFF_NEW_PIN: + d = createAlertDialog(R.string.pinAlertNewPinsDifferent); + break; + + case DIALOG_INVALID_EXISTING_PIN: + d = createAlertDialog(R.string.pinAlertInvalidPin); + break; + + case DIALOG_NO_NEW_PIN: + d = createAlertDialog(R.string.pinAlertNewPinBlank); + break; + + default: + d = null; + } + + return d; + } + + private DialogInterface.OnClickListener dialogClose = new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }; + +} diff --git a/src/main/java/uk/co/bitethebullet/android/token/PinManager.java b/app/src/main/java/uk/co/bitethebullet/android/token/PinManager.java similarity index 96% rename from src/main/java/uk/co/bitethebullet/android/token/PinManager.java rename to app/src/main/java/uk/co/bitethebullet/android/token/PinManager.java index f4dc4c9..8a9a760 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/PinManager.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/PinManager.java @@ -1,91 +1,91 @@ -/* - * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 - * - * This file is part of Android Token. - * - * Android Token is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Android Token is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Android Token. If not, see . - * - */ -package uk.co.bitethebullet.android.token; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; - -import androidx.preference.PreferenceManager; - -import uk.co.bitethebullet.android.token.datalayer.TokenDbAdapter; -import uk.co.bitethebullet.android.token.tokens.HotpToken; - -public class PinManager { - - private static final String SALT = "EE08F4A6-8497-4330-8CD5-8A4ABD93CD46"; - public static final String PIN_CODE_SETTING = "PIN_CODE"; - - public static Boolean hasPinDefined(Context c){ - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(c); - - return sharedPreferences.getString("securityLock", "0").equals("1"); - } - - public static Boolean validatePin(Context c, String pin){ - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(c); - String storedPin = sharedPreferences.getString(PIN_CODE_SETTING, ""); - - return storedPin.equals(pin); - } - - public static void storePin(Context c, String pin){ - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(c); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString(PinManager.PIN_CODE_SETTING, pin); - editor.commit(); - } - -// public static void removePin(Context c){ -// TokenDbAdapter db = new TokenDbAdapter(c); -// db.open(); -// -// db.deletePin(); -// -// db.close(); -// } - - private static String createPinHash(String pin) { - - try{ - - String toHash = SALT + pin; - - MessageDigest md = MessageDigest.getInstance("SHA1"); - md.reset(); - md.update(toHash.getBytes()); - byte[] hashOutput = md.digest(); - - return HotpToken.byteArrayToHexString(hashOutput); - - }catch(NoSuchAlgorithmException ex){ - return null; - } - - } - -} +/* + * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 + * + * This file is part of Android Token. + * + * Android Token is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Android Token is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Android Token. If not, see . + * + */ +package uk.co.bitethebullet.android.token; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; + +import androidx.preference.PreferenceManager; + +import uk.co.bitethebullet.android.token.datalayer.TokenDbAdapter; +import uk.co.bitethebullet.android.token.tokens.HotpToken; + +public class PinManager { + + private static final String SALT = "EE08F4A6-8497-4330-8CD5-8A4ABD93CD46"; + public static final String PIN_CODE_SETTING = "PIN_CODE"; + + public static Boolean hasPinDefined(Context c){ + SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(c); + + return sharedPreferences.getString("securityLock", "0").equals("1"); + } + + public static Boolean validatePin(Context c, String pin){ + + SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(c); + String storedPin = sharedPreferences.getString(PIN_CODE_SETTING, ""); + + return storedPin.equals(pin); + } + + public static void storePin(Context c, String pin){ + SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(c); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(PinManager.PIN_CODE_SETTING, pin); + editor.commit(); + } + +// public static void removePin(Context c){ +// TokenDbAdapter db = new TokenDbAdapter(c); +// db.open(); +// +// db.deletePin(); +// +// db.close(); +// } + + private static String createPinHash(String pin) { + + try{ + + String toHash = SALT + pin; + + MessageDigest md = MessageDigest.getInstance("SHA1"); + md.reset(); + md.update(toHash.getBytes()); + byte[] hashOutput = md.digest(); + + return HotpToken.byteArrayToHexString(hashOutput); + + }catch(NoSuchAlgorithmException ex){ + return null; + } + + } + +} diff --git a/src/main/java/uk/co/bitethebullet/android/token/PinRemove.java b/app/src/main/java/uk/co/bitethebullet/android/token/PinRemove.java similarity index 96% rename from src/main/java/uk/co/bitethebullet/android/token/PinRemove.java rename to app/src/main/java/uk/co/bitethebullet/android/token/PinRemove.java index 209ec1e..85ee9b2 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/PinRemove.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/PinRemove.java @@ -1,102 +1,102 @@ -/* - * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 - * - * This file is part of Android Token. - * - * Android Token is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Android Token is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Android Token. If not, see . - * - */ -package uk.co.bitethebullet.android.token; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.EditText; - -public class PinRemove extends Activity { - - private static final int DIALOG_INVALID_PIN = 0; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.pinremove); - - Button removePinBtn = (Button)findViewById(R.id.pinRemoveSubmit); - removePinBtn.setOnClickListener(removeBtn); - } - - private OnClickListener removeBtn = new OnClickListener() { - - public void onClick(View v) { - //valid the pin - - String pin = ((EditText)findViewById(R.id.pinRemoveExistingPinEdit)).getText().toString(); - - if(PinManager.validatePin(v.getContext(), pin)){ - //todo: MM fix me - //PinManager.removePin(v.getContext()); - finish(); - }else{ - // the pin isn't the same as the one stored, do nothing - showDialog(DIALOG_INVALID_PIN); - return; - } - - } - }; - - @Override - protected Dialog onCreateDialog(int id) { - Dialog d; - - switch(id){ - - case DIALOG_INVALID_PIN: - d = createAlertDialog(R.string.pinAlertInvalidPin); - break; - - default: - d = null; - break; - - } - - return d; - } - - private Dialog createAlertDialog(int messageId){ - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(messageId) - .setCancelable(false) - .setPositiveButton(R.string.dialogPositive, dialogClose); - - return builder.create(); - - } - - private DialogInterface.OnClickListener dialogClose = new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }; - -} +/* + * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 + * + * This file is part of Android Token. + * + * Android Token is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Android Token is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Android Token. If not, see . + * + */ +package uk.co.bitethebullet.android.token; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.EditText; + +public class PinRemove extends Activity { + + private static final int DIALOG_INVALID_PIN = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.pinremove); + + Button removePinBtn = (Button)findViewById(R.id.pinRemoveSubmit); + removePinBtn.setOnClickListener(removeBtn); + } + + private OnClickListener removeBtn = new OnClickListener() { + + public void onClick(View v) { + //valid the pin + + String pin = ((EditText)findViewById(R.id.pinRemoveExistingPinEdit)).getText().toString(); + + if(PinManager.validatePin(v.getContext(), pin)){ + //todo: MM fix me + //PinManager.removePin(v.getContext()); + finish(); + }else{ + // the pin isn't the same as the one stored, do nothing + showDialog(DIALOG_INVALID_PIN); + return; + } + + } + }; + + @Override + protected Dialog onCreateDialog(int id) { + Dialog d; + + switch(id){ + + case DIALOG_INVALID_PIN: + d = createAlertDialog(R.string.pinAlertInvalidPin); + break; + + default: + d = null; + break; + + } + + return d; + } + + private Dialog createAlertDialog(int messageId){ + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(messageId) + .setCancelable(false) + .setPositiveButton(R.string.dialogPositive, dialogClose); + + return builder.create(); + + } + + private DialogInterface.OnClickListener dialogClose = new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }; + +} diff --git a/src/main/java/uk/co/bitethebullet/android/token/QRCodeActivity.java b/app/src/main/java/uk/co/bitethebullet/android/token/QRCodeActivity.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/QRCodeActivity.java rename to app/src/main/java/uk/co/bitethebullet/android/token/QRCodeActivity.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/SettingActivity.java b/app/src/main/java/uk/co/bitethebullet/android/token/SettingActivity.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/SettingActivity.java rename to app/src/main/java/uk/co/bitethebullet/android/token/SettingActivity.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/SettingsFragment.java b/app/src/main/java/uk/co/bitethebullet/android/token/SettingsFragment.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/SettingsFragment.java rename to app/src/main/java/uk/co/bitethebullet/android/token/SettingsFragment.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/TokenAdd.java b/app/src/main/java/uk/co/bitethebullet/android/token/TokenAdd.java similarity index 96% rename from src/main/java/uk/co/bitethebullet/android/token/TokenAdd.java rename to app/src/main/java/uk/co/bitethebullet/android/token/TokenAdd.java index 176320a..964f454 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/TokenAdd.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/TokenAdd.java @@ -1,550 +1,550 @@ -/* - * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 - * - * This file is part of Android Token. - * - * Android Token is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Android Token is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Android Token. If not, see . - * - */ -package uk.co.bitethebullet.android.token; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.RadioButton; -import android.widget.RelativeLayout; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.AdapterView.OnItemSelectedListener; - -import androidx.appcompat.app.AppCompatActivity; - -import uk.co.bitethebullet.android.token.datalayer.TokenDbAdapter; -import uk.co.bitethebullet.android.token.tokens.HotpToken; -import uk.co.bitethebullet.android.token.util.*; - -public class TokenAdd extends AppCompatActivity { - - private static final int DIALOG_STEP1_NO_NAME = 0; - private static final int DIALOG_STEP1_NO_SERIAL = 1; - private static final int DIALOG_STEP2_NO_SEED = 2; - private static final int DIALOG_STEP2_INVALID_SEED_TOO_SHORT = 3; - private static final int DIALOG_STEP2_INVALID_SEED_NOT_HEX = 4; - private static final int DIALOG_STEP2_SEED_CONVERT_ERROR = 5; - private static final int DIALOG_STEP2_UNABLE_TO_SWITCH_FORMAT = 6; - - //defines the define steps the activity can display - private static final int ACTIVITY_STEP_ONE = 0; - private static final int ACTIVITY_STEP_TWO = 1; - - private static final String KEY_ACTIVITY_STATE = "currentState"; - private static final String KEY_TOKEN_TYPE = "tokenType"; - private static final String KEY_OTP_LENGTH = "otpLength"; - private static final String KEY_TIME_STEP = "timeStep"; - private static final String KEY_NAME = "tokenName"; - private static final String KEY_SERIAL = "tokenSerial"; - private static final String KEY_SEED_FORMAT = "tokenSeedFormat"; - private static final String KEY_ORGANISATION = "tokenOrganisation"; - - //current state of the activity - private int mCurrentActivityStep = ACTIVITY_STEP_ONE; - - //holds the data from step 1 - private String mName; - private String mOrganisation; - private String mSerial; - private int mTokenType; - private int mOtpLength; - private int mTimeStep; - private int mTokenSeedFormat; - private Boolean mAcceptWeakSeed; - - private static final int RANDOM_SEED_LENGTH = 160; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mAcceptWeakSeed = false; - - setContentView(R.layout.token_add); - - loadSpinnerArrayData(R.id.tokenTypeSpinner, R.array.tokenType, 1); - loadSpinnerArrayData(R.id.tokenOtpSpinner, R.array.otpLength); - loadSpinnerArrayData(R.id.tokenTimeStepSpinner, R.array.timeStep); - loadSpinnerArrayData(R.id.tokenSeedFormat, R.array.tokenSeedFormatType, 1); - - if(savedInstanceState != null){ - mCurrentActivityStep = savedInstanceState.getInt(KEY_ACTIVITY_STATE); - - int tokenType = savedInstanceState.getInt(KEY_TOKEN_TYPE); - int otpLength = savedInstanceState.getInt(KEY_OTP_LENGTH); - int timeStep = savedInstanceState.getInt(KEY_TIME_STEP); - String tokenName = savedInstanceState.getString(KEY_NAME); - String tokenSerial = savedInstanceState.getString(KEY_SERIAL); - String tokenOrganisation = savedInstanceState.getString(KEY_ORGANISATION); - - if(mCurrentActivityStep == ACTIVITY_STEP_TWO){ - //step 2 - mName = tokenName; - mSerial = tokenSerial; - mTokenType = tokenType; - mOtpLength = otpLength; - mTimeStep = timeStep; - mOrganisation = tokenOrganisation; - - showStepTwo(); - - }else{ - //step 1 - ((Spinner)findViewById(R.id.tokenTypeSpinner)).setSelection(tokenType); - ((Spinner)findViewById(R.id.tokenOtpSpinner)).setSelection(otpLength); - ((Spinner)findViewById(R.id.tokenTimeStepSpinner)).setSelection(timeStep); - } - } - - Button btnNext = (Button)findViewById(R.id.btnAddStep2); - btnNext.setOnClickListener(buttonNext); - - RadioButton rbManual = (RadioButton)findViewById(R.id.rbSeedManual); - RadioButton rbRandom = (RadioButton)findViewById(R.id.rbSeedRandom); - RadioButton rbPassword = (RadioButton)findViewById(R.id.rbSeedPassword); - - rbManual.setOnClickListener(radioSeed); - rbRandom.setOnClickListener(radioSeed); - rbPassword.setOnClickListener(radioSeed); - - Button btnComplete = (Button)findViewById(R.id.tokenAddComplete); - btnComplete.setOnClickListener(buttonComplete); - - Spinner tokenType = (Spinner)findViewById(R.id.tokenTypeSpinner); - tokenType.setOnItemSelectedListener(tokenTypeSelected); - - Spinner tokenSeedFormat = (Spinner)findViewById(R.id.tokenSeedFormat); - tokenSeedFormat.setOnItemSelectedListener(tokenSeedFormatSelected); - } - - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - int tokenType; - int otpLength; - int timeStep; - - if(mCurrentActivityStep == ACTIVITY_STEP_ONE){ - tokenType = ((Spinner)findViewById(R.id.tokenTypeSpinner)).getSelectedItemPosition(); - otpLength = ((Spinner)findViewById(R.id.tokenOtpSpinner)).getSelectedItemPosition(); - timeStep = ((Spinner)findViewById(R.id.tokenTimeStepSpinner)).getSelectedItemPosition(); - - outState.putInt(KEY_TOKEN_TYPE, tokenType); - outState.putInt(KEY_OTP_LENGTH, otpLength); - outState.putInt(KEY_TIME_STEP, timeStep); - }else{ - outState.putInt(KEY_TOKEN_TYPE, mTokenType); - outState.putInt(KEY_OTP_LENGTH, mOtpLength); - outState.putInt(KEY_TIME_STEP, mTimeStep); - } - - outState.putInt(KEY_ACTIVITY_STATE, mCurrentActivityStep); - outState.putString(KEY_NAME, mName); - outState.putString(KEY_SERIAL, mSerial); - outState.putString(KEY_ORGANISATION, mOrganisation); - } - - - private OnItemSelectedListener tokenTypeSelected = new OnItemSelectedListener() { - - public void onItemSelected(AdapterView arg0, View arg1, int arg2, - long arg3) { - - TextView caption = (TextView)findViewById(R.id.tokenTimeStep); - Spinner spinner = (Spinner)findViewById(R.id.tokenTimeStepSpinner); - - if(arg2 == 0){ - caption.setVisibility(View.INVISIBLE); - spinner.setVisibility(View.INVISIBLE); - }else{ - caption.setVisibility(View.VISIBLE); - spinner.setVisibility(View.VISIBLE); - } - - } - - public void onNothingSelected(AdapterView arg0) { - //ignore - } - - }; - - - private OnItemSelectedListener tokenSeedFormatSelected = new OnItemSelectedListener() { - - public void onItemSelected(AdapterView arg0, View arg1, int arg2, - long arg3) { - - EditText tokenSeedEdit = (EditText)findViewById(R.id.tokenSeedEdit); - String seed = tokenSeedEdit.getText().toString(); - - try{ - //only convert if we have something entered - if(seed.length() > 0) { - seed = SeedConvertor.ConvertFromBA(SeedConvertor.ConvertFromEncodingToBA(seed, mTokenSeedFormat), arg2); - } - } - catch(Exception ex){ - //cancel the change of seed format, if have - //some error - showDialog(DIALOG_STEP2_UNABLE_TO_SWITCH_FORMAT); - - Spinner tokenSeedFormat = (Spinner)findViewById(R.id.tokenSeedFormat); - tokenSeedFormat.setSelection(mTokenSeedFormat); - return; - } - - tokenSeedEdit.setText(seed); - mTokenSeedFormat = arg2; - } - - public void onNothingSelected(AdapterView arg0) { - // ignore not required - } - - }; - - - - @Override - protected Dialog onCreateDialog(int id) { - Dialog d; - - switch(id){ - case DIALOG_STEP1_NO_NAME: - d = createAlertDialog(R.string.tokenAddDialogNoName); - break; - - case DIALOG_STEP1_NO_SERIAL: - d = createAlertDialog(R.string.tokenAddDialogNoSerial); - break; - - case DIALOG_STEP2_NO_SEED: - d = createAlertDialog(R.string.tokenAddDialogNoSeed); - break; - - case DIALOG_STEP2_INVALID_SEED_TOO_SHORT: - d = createAlertDialog(R.string.tokenAddDialogInvalidSeedTooShort2); - break; - - case DIALOG_STEP2_INVALID_SEED_NOT_HEX: - d = createAlertDialog(R.string.tokenAddDialogInvalidSeedNotHex); - break; - - case DIALOG_STEP2_SEED_CONVERT_ERROR: - d = createAlertDialog(R.string.tokenAddDialogInvalidSeedConvertion); - break; - - case DIALOG_STEP2_UNABLE_TO_SWITCH_FORMAT: - d = createAlertDialog(R.string.tokenAddDialogUnableToSwitchFormat); - break; - - default: - d = null; - } - - return d; - } - - private Dialog createAlertDialog(int messageId){ - return this.createAlertDialog(messageId, null, dialogClose, null); - } - - private Dialog createAlertDialog(int messageId, - String additionalMessage, - DialogInterface.OnClickListener positiveClick, - DialogInterface.OnClickListener negativeClick){ - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(messageId) - .setPositiveButton(R.string.dialogPositive, positiveClick); - - if(negativeClick != null){ - builder.setNegativeButton(R.string.cancel, negativeClick); - }else{ - builder.setCancelable(false); - } - - //load the resource and optional additional information - if(additionalMessage != null){ - builder.setMessage(String.format(getResources().getString(messageId), additionalMessage)); - } - - return builder.create(); - } - - private DialogInterface.OnClickListener dialogClose = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }; - - /*** - * handles the user accepting the weak seed when we warn then in a dialod - */ - private DialogInterface.OnClickListener dialogAcceptWeakSeed = new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - Log.d("Android Token", "dialogAcceptWeakSeed called"); - mAcceptWeakSeed = true; - dialog.dismiss(); - - buttonComplete.onClick(getCurrentFocus()); - mAcceptWeakSeed = false; - } - }; - - private OnClickListener buttonNext = new OnClickListener() { - - public void onClick(View v) { - //validate we have the required completed fields - - boolean isValid = true; - - String name = ((EditText)findViewById(R.id.tokenNameEdit)).getText().toString(); - String organisation = ((EditText)findViewById(R.id.tokenOrganisationEdit)).getText().toString(); - String serial = ((EditText)findViewById(R.id.tokenSerialEdit)).getText().toString(); - - if(name.length() == 0){ - isValid = false; - showDialog(DIALOG_STEP1_NO_NAME); - } - - if(isValid){ - //store step 1 values in members vars - mName = name; - mOrganisation = organisation; - mSerial = serial; - mTokenType = ((Spinner)findViewById(R.id.tokenTypeSpinner)).getSelectedItemPosition();; - mOtpLength = Integer.parseInt(((Spinner)findViewById(R.id.tokenOtpSpinner)).getSelectedItem().toString()); - mTimeStep = ((Spinner)findViewById(R.id.tokenTimeStepSpinner)).getSelectedItemPosition() == 0 ? 30 : 60; - - showStepTwo(); - mCurrentActivityStep = ACTIVITY_STEP_TWO; - } - } - }; - - private OnClickListener buttonComplete = new OnClickListener() { - - public void onClick(View v) { - //validate we have a valid serial - Boolean isValid = true; - - RadioButton rbPassword = (RadioButton)findViewById(R.id.rbSeedPassword); - String seed = ((EditText)findViewById(R.id.tokenSeedEdit)).getText().toString(); - - try { - //if the seed is not in hex format convert this to hex - //then make sure the length is - if(mTokenSeedFormat == 1){ - //base32 - seed = SeedConvertor.ConvertFromBA(SeedConvertor.ConvertFromEncodingToBA(seed, 1), 0); - }else if(mTokenSeedFormat == 2){ - //base 64 - seed = SeedConvertor.ConvertFromBA(SeedConvertor.ConvertFromEncodingToBA(seed, 2), 0); - } - } catch (Exception e) { - showDialog(DIALOG_STEP2_SEED_CONVERT_ERROR); - return; - } - - if(seed.length() == 0){ - isValid = false; - showDialog(DIALOG_STEP2_NO_SEED); - return; - } - - if(!rbPassword.isChecked()){ - - //validate the length - int seedLength = seed.length(); - - //valid the chars in the seed - Pattern p = Pattern.compile("[A-Fa-f0-9]*"); - Matcher matcher = p.matcher(seed); - - if(!matcher.matches()){ - showDialog(DIALOG_STEP2_INVALID_SEED_NOT_HEX); - return; - } - - if(seedLength < 2){ - showDialog(DIALOG_STEP2_INVALID_SEED_TOO_SHORT); - return; - } - else if(seedLength < 32 && !mAcceptWeakSeed){ - isValid = false; - - //the seed is shorter than the 128bit minimum as recommended - //in the RFC, therefore warn the user but give them then - //ability to create a weak seed anyway - - Dialog d = createAlertDialog(R.string.tokenAddDialogInvalidSeedTooShort, - "" + seedLength * 4, - dialogAcceptWeakSeed, - dialogClose); - d.show(); - - return; - } - - }else{ - //when creating a seed from password we simple sha1 the data then - //use that as a seed to to concat with the data again - // - // h1 = sha1(password) - // h2 = sha1(password + h1) - // - //h2 should then be stored as a hex string in the database - try{ - - byte[] input = seed.getBytes(); - MessageDigest md = MessageDigest.getInstance("SHA1"); - - md.reset(); - byte[] h1 = md.digest(input); - md.reset(); - byte[] h2 = md.digest(mergeByteArray(input, h1)); - - seed = HotpToken.byteArrayToHexString(h2); - - }catch(NoSuchAlgorithmException nsae){ - - } - - } - - if(isValid){ - //store token in db - TokenDbAdapter db = new TokenDbAdapter(v.getContext()); - db.open(); - db.createToken(mName, mSerial, seed, - mTokenType, mOtpLength, - mTimeStep, mOrganisation); - db.close(); - - setResult(RESULT_OK); - - finish(); - } - - } - }; - - private byte[] mergeByteArray(byte[] b1, byte[] b2){ - - byte[] result = new byte[b1.length + b2.length]; - - int i = 0; - - for(byte b : b1){ - result[i] = b; - i++; - } - - for(byte b : b2){ - result[i] = b; - i++; - } - - return result; - } - - private OnClickListener radioSeed = new OnClickListener() { - - public void onClick(View v) { - RadioButton rb = (RadioButton)v; - - //if we are entering the seed manually/randomly we should display the - //the format option to allow the user to enter as hex/base64/base32 - - if(rb.getId() == R.id.rbSeedRandom){ - EditText seedEdit = (EditText)findViewById(R.id.tokenSeedEdit); - - try{ - //create a new random seed, this will be in hex format. - //see if we need to convert this to whatever we have input - //format selected as - String hexSeed = HotpToken.generateNewSeed(RANDOM_SEED_LENGTH); - byte[] ba = SeedConvertor.ConvertFromEncodingToBA(hexSeed, - SeedConvertor.HEX_FORMAT); - - seedEdit.setText(SeedConvertor.ConvertFromBA(ba, mTokenSeedFormat)); - }catch(Exception ex){ - } - - } - - Spinner tokenSeedFormat = (Spinner)findViewById(R.id.tokenSeedFormat); - - if(rb.getId() == R.id.rbSeedManual || rb.getId() == R.id.rbSeedRandom){ - tokenSeedFormat.setEnabled(true); - }else{ - tokenSeedFormat.setEnabled(false); - } - } - }; - - private void loadSpinnerArrayData(int spinnerId, int arrayData){ - loadSpinnerArrayData(spinnerId, arrayData, -1); - } - - private void loadSpinnerArrayData(int spinnerId, int arrayData, int selectedPosition){ - Spinner spinner = (Spinner)findViewById(spinnerId); - ArrayAdapter adapter = ArrayAdapter.createFromResource(this, - arrayData, - android.R.layout.simple_spinner_item); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinner.setAdapter(adapter); - - if(selectedPosition >= 0) { - spinner.setSelection(selectedPosition); - } - } - - - private void showStepTwo() { - //show the next step - RelativeLayout step1 = (RelativeLayout)findViewById(R.id.tokenAddStep1); - RelativeLayout step2 = (RelativeLayout)findViewById(R.id.tokenAddStep2); - - step1.setVisibility(View.GONE); - step2.setVisibility(View.VISIBLE); - } - -} +/* + * Copyright Mark McAvoy - www.bitethebullet.co.uk 2009 - 2020 + * + * This file is part of Android Token. + * + * Android Token is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Android Token is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Android Token. If not, see . + * + */ +package uk.co.bitethebullet.android.token; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RelativeLayout; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.AdapterView.OnItemSelectedListener; + +import androidx.appcompat.app.AppCompatActivity; + +import uk.co.bitethebullet.android.token.datalayer.TokenDbAdapter; +import uk.co.bitethebullet.android.token.tokens.HotpToken; +import uk.co.bitethebullet.android.token.util.*; + +public class TokenAdd extends AppCompatActivity { + + private static final int DIALOG_STEP1_NO_NAME = 0; + private static final int DIALOG_STEP1_NO_SERIAL = 1; + private static final int DIALOG_STEP2_NO_SEED = 2; + private static final int DIALOG_STEP2_INVALID_SEED_TOO_SHORT = 3; + private static final int DIALOG_STEP2_INVALID_SEED_NOT_HEX = 4; + private static final int DIALOG_STEP2_SEED_CONVERT_ERROR = 5; + private static final int DIALOG_STEP2_UNABLE_TO_SWITCH_FORMAT = 6; + + //defines the define steps the activity can display + private static final int ACTIVITY_STEP_ONE = 0; + private static final int ACTIVITY_STEP_TWO = 1; + + private static final String KEY_ACTIVITY_STATE = "currentState"; + private static final String KEY_TOKEN_TYPE = "tokenType"; + private static final String KEY_OTP_LENGTH = "otpLength"; + private static final String KEY_TIME_STEP = "timeStep"; + private static final String KEY_NAME = "tokenName"; + private static final String KEY_SERIAL = "tokenSerial"; + private static final String KEY_SEED_FORMAT = "tokenSeedFormat"; + private static final String KEY_ORGANISATION = "tokenOrganisation"; + + //current state of the activity + private int mCurrentActivityStep = ACTIVITY_STEP_ONE; + + //holds the data from step 1 + private String mName; + private String mOrganisation; + private String mSerial; + private int mTokenType; + private int mOtpLength; + private int mTimeStep; + private int mTokenSeedFormat; + private Boolean mAcceptWeakSeed; + + private static final int RANDOM_SEED_LENGTH = 160; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAcceptWeakSeed = false; + + setContentView(R.layout.token_add); + + loadSpinnerArrayData(R.id.tokenTypeSpinner, R.array.tokenType, 1); + loadSpinnerArrayData(R.id.tokenOtpSpinner, R.array.otpLength); + loadSpinnerArrayData(R.id.tokenTimeStepSpinner, R.array.timeStep); + loadSpinnerArrayData(R.id.tokenSeedFormat, R.array.tokenSeedFormatType, 1); + + if(savedInstanceState != null){ + mCurrentActivityStep = savedInstanceState.getInt(KEY_ACTIVITY_STATE); + + int tokenType = savedInstanceState.getInt(KEY_TOKEN_TYPE); + int otpLength = savedInstanceState.getInt(KEY_OTP_LENGTH); + int timeStep = savedInstanceState.getInt(KEY_TIME_STEP); + String tokenName = savedInstanceState.getString(KEY_NAME); + String tokenSerial = savedInstanceState.getString(KEY_SERIAL); + String tokenOrganisation = savedInstanceState.getString(KEY_ORGANISATION); + + if(mCurrentActivityStep == ACTIVITY_STEP_TWO){ + //step 2 + mName = tokenName; + mSerial = tokenSerial; + mTokenType = tokenType; + mOtpLength = otpLength; + mTimeStep = timeStep; + mOrganisation = tokenOrganisation; + + showStepTwo(); + + }else{ + //step 1 + ((Spinner)findViewById(R.id.tokenTypeSpinner)).setSelection(tokenType); + ((Spinner)findViewById(R.id.tokenOtpSpinner)).setSelection(otpLength); + ((Spinner)findViewById(R.id.tokenTimeStepSpinner)).setSelection(timeStep); + } + } + + Button btnNext = (Button)findViewById(R.id.btnAddStep2); + btnNext.setOnClickListener(buttonNext); + + RadioButton rbManual = (RadioButton)findViewById(R.id.rbSeedManual); + RadioButton rbRandom = (RadioButton)findViewById(R.id.rbSeedRandom); + RadioButton rbPassword = (RadioButton)findViewById(R.id.rbSeedPassword); + + rbManual.setOnClickListener(radioSeed); + rbRandom.setOnClickListener(radioSeed); + rbPassword.setOnClickListener(radioSeed); + + Button btnComplete = (Button)findViewById(R.id.tokenAddComplete); + btnComplete.setOnClickListener(buttonComplete); + + Spinner tokenType = (Spinner)findViewById(R.id.tokenTypeSpinner); + tokenType.setOnItemSelectedListener(tokenTypeSelected); + + Spinner tokenSeedFormat = (Spinner)findViewById(R.id.tokenSeedFormat); + tokenSeedFormat.setOnItemSelectedListener(tokenSeedFormatSelected); + } + + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + int tokenType; + int otpLength; + int timeStep; + + if(mCurrentActivityStep == ACTIVITY_STEP_ONE){ + tokenType = ((Spinner)findViewById(R.id.tokenTypeSpinner)).getSelectedItemPosition(); + otpLength = ((Spinner)findViewById(R.id.tokenOtpSpinner)).getSelectedItemPosition(); + timeStep = ((Spinner)findViewById(R.id.tokenTimeStepSpinner)).getSelectedItemPosition(); + + outState.putInt(KEY_TOKEN_TYPE, tokenType); + outState.putInt(KEY_OTP_LENGTH, otpLength); + outState.putInt(KEY_TIME_STEP, timeStep); + }else{ + outState.putInt(KEY_TOKEN_TYPE, mTokenType); + outState.putInt(KEY_OTP_LENGTH, mOtpLength); + outState.putInt(KEY_TIME_STEP, mTimeStep); + } + + outState.putInt(KEY_ACTIVITY_STATE, mCurrentActivityStep); + outState.putString(KEY_NAME, mName); + outState.putString(KEY_SERIAL, mSerial); + outState.putString(KEY_ORGANISATION, mOrganisation); + } + + + private OnItemSelectedListener tokenTypeSelected = new OnItemSelectedListener() { + + public void onItemSelected(AdapterView arg0, View arg1, int arg2, + long arg3) { + + TextView caption = (TextView)findViewById(R.id.tokenTimeStep); + Spinner spinner = (Spinner)findViewById(R.id.tokenTimeStepSpinner); + + if(arg2 == 0){ + caption.setVisibility(View.INVISIBLE); + spinner.setVisibility(View.INVISIBLE); + }else{ + caption.setVisibility(View.VISIBLE); + spinner.setVisibility(View.VISIBLE); + } + + } + + public void onNothingSelected(AdapterView arg0) { + //ignore + } + + }; + + + private OnItemSelectedListener tokenSeedFormatSelected = new OnItemSelectedListener() { + + public void onItemSelected(AdapterView arg0, View arg1, int arg2, + long arg3) { + + EditText tokenSeedEdit = (EditText)findViewById(R.id.tokenSeedEdit); + String seed = tokenSeedEdit.getText().toString(); + + try{ + //only convert if we have something entered + if(seed.length() > 0) { + seed = SeedConvertor.ConvertFromBA(SeedConvertor.ConvertFromEncodingToBA(seed, mTokenSeedFormat), arg2); + } + } + catch(Exception ex){ + //cancel the change of seed format, if have + //some error + showDialog(DIALOG_STEP2_UNABLE_TO_SWITCH_FORMAT); + + Spinner tokenSeedFormat = (Spinner)findViewById(R.id.tokenSeedFormat); + tokenSeedFormat.setSelection(mTokenSeedFormat); + return; + } + + tokenSeedEdit.setText(seed); + mTokenSeedFormat = arg2; + } + + public void onNothingSelected(AdapterView arg0) { + // ignore not required + } + + }; + + + + @Override + protected Dialog onCreateDialog(int id) { + Dialog d; + + switch(id){ + case DIALOG_STEP1_NO_NAME: + d = createAlertDialog(R.string.tokenAddDialogNoName); + break; + + case DIALOG_STEP1_NO_SERIAL: + d = createAlertDialog(R.string.tokenAddDialogNoSerial); + break; + + case DIALOG_STEP2_NO_SEED: + d = createAlertDialog(R.string.tokenAddDialogNoSeed); + break; + + case DIALOG_STEP2_INVALID_SEED_TOO_SHORT: + d = createAlertDialog(R.string.tokenAddDialogInvalidSeedTooShort2); + break; + + case DIALOG_STEP2_INVALID_SEED_NOT_HEX: + d = createAlertDialog(R.string.tokenAddDialogInvalidSeedNotHex); + break; + + case DIALOG_STEP2_SEED_CONVERT_ERROR: + d = createAlertDialog(R.string.tokenAddDialogInvalidSeedConvertion); + break; + + case DIALOG_STEP2_UNABLE_TO_SWITCH_FORMAT: + d = createAlertDialog(R.string.tokenAddDialogUnableToSwitchFormat); + break; + + default: + d = null; + } + + return d; + } + + private Dialog createAlertDialog(int messageId){ + return this.createAlertDialog(messageId, null, dialogClose, null); + } + + private Dialog createAlertDialog(int messageId, + String additionalMessage, + DialogInterface.OnClickListener positiveClick, + DialogInterface.OnClickListener negativeClick){ + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(messageId) + .setPositiveButton(R.string.dialogPositive, positiveClick); + + if(negativeClick != null){ + builder.setNegativeButton(R.string.cancel, negativeClick); + }else{ + builder.setCancelable(false); + } + + //load the resource and optional additional information + if(additionalMessage != null){ + builder.setMessage(String.format(getResources().getString(messageId), additionalMessage)); + } + + return builder.create(); + } + + private DialogInterface.OnClickListener dialogClose = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }; + + /*** + * handles the user accepting the weak seed when we warn then in a dialod + */ + private DialogInterface.OnClickListener dialogAcceptWeakSeed = new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + Log.d("Android Token", "dialogAcceptWeakSeed called"); + mAcceptWeakSeed = true; + dialog.dismiss(); + + buttonComplete.onClick(getCurrentFocus()); + mAcceptWeakSeed = false; + } + }; + + private OnClickListener buttonNext = new OnClickListener() { + + public void onClick(View v) { + //validate we have the required completed fields + + boolean isValid = true; + + String name = ((EditText)findViewById(R.id.tokenNameEdit)).getText().toString(); + String organisation = ((EditText)findViewById(R.id.tokenOrganisationEdit)).getText().toString(); + String serial = ((EditText)findViewById(R.id.tokenSerialEdit)).getText().toString(); + + if(name.length() == 0){ + isValid = false; + showDialog(DIALOG_STEP1_NO_NAME); + } + + if(isValid){ + //store step 1 values in members vars + mName = name; + mOrganisation = organisation; + mSerial = serial; + mTokenType = ((Spinner)findViewById(R.id.tokenTypeSpinner)).getSelectedItemPosition();; + mOtpLength = Integer.parseInt(((Spinner)findViewById(R.id.tokenOtpSpinner)).getSelectedItem().toString()); + mTimeStep = ((Spinner)findViewById(R.id.tokenTimeStepSpinner)).getSelectedItemPosition() == 0 ? 30 : 60; + + showStepTwo(); + mCurrentActivityStep = ACTIVITY_STEP_TWO; + } + } + }; + + private OnClickListener buttonComplete = new OnClickListener() { + + public void onClick(View v) { + //validate we have a valid serial + Boolean isValid = true; + + RadioButton rbPassword = (RadioButton)findViewById(R.id.rbSeedPassword); + String seed = ((EditText)findViewById(R.id.tokenSeedEdit)).getText().toString(); + + try { + //if the seed is not in hex format convert this to hex + //then make sure the length is + if(mTokenSeedFormat == 1){ + //base32 + seed = SeedConvertor.ConvertFromBA(SeedConvertor.ConvertFromEncodingToBA(seed, 1), 0); + }else if(mTokenSeedFormat == 2){ + //base 64 + seed = SeedConvertor.ConvertFromBA(SeedConvertor.ConvertFromEncodingToBA(seed, 2), 0); + } + } catch (Exception e) { + showDialog(DIALOG_STEP2_SEED_CONVERT_ERROR); + return; + } + + if(seed.length() == 0){ + isValid = false; + showDialog(DIALOG_STEP2_NO_SEED); + return; + } + + if(!rbPassword.isChecked()){ + + //validate the length + int seedLength = seed.length(); + + //valid the chars in the seed + Pattern p = Pattern.compile("[A-Fa-f0-9]*"); + Matcher matcher = p.matcher(seed); + + if(!matcher.matches()){ + showDialog(DIALOG_STEP2_INVALID_SEED_NOT_HEX); + return; + } + + if(seedLength < 2){ + showDialog(DIALOG_STEP2_INVALID_SEED_TOO_SHORT); + return; + } + else if(seedLength < 32 && !mAcceptWeakSeed){ + isValid = false; + + //the seed is shorter than the 128bit minimum as recommended + //in the RFC, therefore warn the user but give them then + //ability to create a weak seed anyway + + Dialog d = createAlertDialog(R.string.tokenAddDialogInvalidSeedTooShort, + "" + seedLength * 4, + dialogAcceptWeakSeed, + dialogClose); + d.show(); + + return; + } + + }else{ + //when creating a seed from password we simple sha1 the data then + //use that as a seed to to concat with the data again + // + // h1 = sha1(password) + // h2 = sha1(password + h1) + // + //h2 should then be stored as a hex string in the database + try{ + + byte[] input = seed.getBytes(); + MessageDigest md = MessageDigest.getInstance("SHA1"); + + md.reset(); + byte[] h1 = md.digest(input); + md.reset(); + byte[] h2 = md.digest(mergeByteArray(input, h1)); + + seed = HotpToken.byteArrayToHexString(h2); + + }catch(NoSuchAlgorithmException nsae){ + + } + + } + + if(isValid){ + //store token in db + TokenDbAdapter db = new TokenDbAdapter(v.getContext()); + db.open(); + db.createToken(mName, mSerial, seed, + mTokenType, mOtpLength, + mTimeStep, mOrganisation); + db.close(); + + setResult(RESULT_OK); + + finish(); + } + + } + }; + + private byte[] mergeByteArray(byte[] b1, byte[] b2){ + + byte[] result = new byte[b1.length + b2.length]; + + int i = 0; + + for(byte b : b1){ + result[i] = b; + i++; + } + + for(byte b : b2){ + result[i] = b; + i++; + } + + return result; + } + + private OnClickListener radioSeed = new OnClickListener() { + + public void onClick(View v) { + RadioButton rb = (RadioButton)v; + + //if we are entering the seed manually/randomly we should display the + //the format option to allow the user to enter as hex/base64/base32 + + if(rb.getId() == R.id.rbSeedRandom){ + EditText seedEdit = (EditText)findViewById(R.id.tokenSeedEdit); + + try{ + //create a new random seed, this will be in hex format. + //see if we need to convert this to whatever we have input + //format selected as + String hexSeed = HotpToken.generateNewSeed(RANDOM_SEED_LENGTH); + byte[] ba = SeedConvertor.ConvertFromEncodingToBA(hexSeed, + SeedConvertor.HEX_FORMAT); + + seedEdit.setText(SeedConvertor.ConvertFromBA(ba, mTokenSeedFormat)); + }catch(Exception ex){ + } + + } + + Spinner tokenSeedFormat = (Spinner)findViewById(R.id.tokenSeedFormat); + + if(rb.getId() == R.id.rbSeedManual || rb.getId() == R.id.rbSeedRandom){ + tokenSeedFormat.setEnabled(true); + }else{ + tokenSeedFormat.setEnabled(false); + } + } + }; + + private void loadSpinnerArrayData(int spinnerId, int arrayData){ + loadSpinnerArrayData(spinnerId, arrayData, -1); + } + + private void loadSpinnerArrayData(int spinnerId, int arrayData, int selectedPosition){ + Spinner spinner = (Spinner)findViewById(spinnerId); + ArrayAdapter adapter = ArrayAdapter.createFromResource(this, + arrayData, + android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + + if(selectedPosition >= 0) { + spinner.setSelection(selectedPosition); + } + } + + + private void showStepTwo() { + //show the next step + RelativeLayout step1 = (RelativeLayout)findViewById(R.id.tokenAddStep1); + RelativeLayout step2 = (RelativeLayout)findViewById(R.id.tokenAddStep2); + + step1.setVisibility(View.GONE); + step2.setVisibility(View.VISIBLE); + } + +} diff --git a/src/main/java/uk/co/bitethebullet/android/token/TokenCountDownView.java b/app/src/main/java/uk/co/bitethebullet/android/token/TokenCountDownView.java similarity index 95% rename from src/main/java/uk/co/bitethebullet/android/token/TokenCountDownView.java rename to app/src/main/java/uk/co/bitethebullet/android/token/TokenCountDownView.java index d7274f8..fe9fe13 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/TokenCountDownView.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/TokenCountDownView.java @@ -1,42 +1,42 @@ -package uk.co.bitethebullet.android.token; - -/////////////////////////////////////// -//this is not used!! -////////////////////////////////////// - - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.view.View; - -public class TokenCountDownView extends View { - - public TokenCountDownView(Context context){ - super(context); - } - - @Override - protected void onDraw(Canvas canvas) { - - Paint p = new Paint(); - p.setAntiAlias(true); - p.setStyle(Paint.Style.FILL); - p.setColor(0x88FF0000); - - RectF oval = new RectF( 0, 0, 50, 50); - drawArc(canvas, oval, true, p); - } - - - private void drawArc(Canvas canvas, RectF oval, boolean useCenter, Paint paint) { - - float mStart = 0; - float mSweep = 360; - - canvas.drawArc(oval, mStart, mSweep, useCenter, paint); - } - - -} +package uk.co.bitethebullet.android.token; + +/////////////////////////////////////// +//this is not used!! +////////////////////////////////////// + + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.view.View; + +public class TokenCountDownView extends View { + + public TokenCountDownView(Context context){ + super(context); + } + + @Override + protected void onDraw(Canvas canvas) { + + Paint p = new Paint(); + p.setAntiAlias(true); + p.setStyle(Paint.Style.FILL); + p.setColor(0x88FF0000); + + RectF oval = new RectF( 0, 0, 50, 50); + drawArc(canvas, oval, true, p); + } + + + private void drawArc(Canvas canvas, RectF oval, boolean useCenter, Paint paint) { + + float mStart = 0; + float mSweep = 360; + + canvas.drawArc(oval, mStart, mSweep, useCenter, paint); + } + + +} diff --git a/src/main/java/uk/co/bitethebullet/android/token/TokenList.java b/app/src/main/java/uk/co/bitethebullet/android/token/TokenList.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/TokenList.java rename to app/src/main/java/uk/co/bitethebullet/android/token/TokenList.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/adapters/TokenAdapter.java b/app/src/main/java/uk/co/bitethebullet/android/token/adapters/TokenAdapter.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/adapters/TokenAdapter.java rename to app/src/main/java/uk/co/bitethebullet/android/token/adapters/TokenAdapter.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/datalayer/TokenDbAdapter.java b/app/src/main/java/uk/co/bitethebullet/android/token/datalayer/TokenDbAdapter.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/datalayer/TokenDbAdapter.java rename to app/src/main/java/uk/co/bitethebullet/android/token/datalayer/TokenDbAdapter.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenDialog.java b/app/src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenDialog.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenDialog.java rename to app/src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenDialog.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenPickerDialog.java b/app/src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenPickerDialog.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenPickerDialog.java rename to app/src/main/java/uk/co/bitethebullet/android/token/dialogs/DeleteTokenPickerDialog.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/dialogs/PinDefintionDialog.java b/app/src/main/java/uk/co/bitethebullet/android/token/dialogs/PinDefintionDialog.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/dialogs/PinDefintionDialog.java rename to app/src/main/java/uk/co/bitethebullet/android/token/dialogs/PinDefintionDialog.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/parse/OtpAuthUriException.java b/app/src/main/java/uk/co/bitethebullet/android/token/parse/OtpAuthUriException.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/parse/OtpAuthUriException.java rename to app/src/main/java/uk/co/bitethebullet/android/token/parse/OtpAuthUriException.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/parse/UrlParser.java b/app/src/main/java/uk/co/bitethebullet/android/token/parse/UrlParser.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/parse/UrlParser.java rename to app/src/main/java/uk/co/bitethebullet/android/token/parse/UrlParser.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/HotpToken.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/HotpToken.java similarity index 97% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/HotpToken.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/HotpToken.java index fb0aa09..049a499 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/tokens/HotpToken.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/HotpToken.java @@ -56,7 +56,15 @@ public class HotpToken implements IToken { // 0 1 2 3 4 5 6 7 8 = {1,10,100,1000,10000,100000,1000000,10000000,100000000}; - + /** + * + * @param name + * @param serial + * @param seed + * @param eventCount 这个每次生成时 有变动!! + * @param otpLength + * @param organisation + */ public HotpToken(String name, String serial, String seed, long eventCount, int otpLength, String organisation){ mName = name; diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/IToken.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/IToken.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/IToken.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/IToken.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/ITokenMeta.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/ITokenMeta.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/ITokenMeta.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/ITokenMeta.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/IconSuggestor.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/IconSuggestor.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/IconSuggestor.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/IconSuggestor.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenFactory.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenFactory.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/TokenFactory.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenFactory.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenHelper.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenHelper.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/TokenHelper.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenHelper.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenMetaData.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenMetaData.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/TokenMetaData.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/TokenMetaData.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/tokens/TotpToken.java b/app/src/main/java/uk/co/bitethebullet/android/token/tokens/TotpToken.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/tokens/TotpToken.java rename to app/src/main/java/uk/co/bitethebullet/android/token/tokens/TotpToken.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/util/Base32.java b/app/src/main/java/uk/co/bitethebullet/android/token/util/Base32.java similarity index 97% rename from src/main/java/uk/co/bitethebullet/android/token/util/Base32.java rename to app/src/main/java/uk/co/bitethebullet/android/token/util/Base32.java index 5f298a6..f7e397a 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/util/Base32.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/util/Base32.java @@ -1,235 +1,235 @@ -package uk.co.bitethebullet.android.token.util; - -import java.util.ArrayList; - -public class Base32 { - - private static final String DEF_ENCODING_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - private static final char DEF_PADDING = '='; - - private String eTable; //Encoding table - private char padding; - private byte[] dTable; //Decoding table - - public Base32 () - { - this (DEF_ENCODING_TABLE, DEF_PADDING); - } - - public Base32 (char padding){ - this (DEF_ENCODING_TABLE, padding); - } - public Base32 (String encodingTable){ - this (encodingTable, DEF_PADDING); - } - - public Base32 (String encodingTable, char padding) { - this.eTable = encodingTable; - this.padding = padding; - dTable = new byte[0x80]; - InitialiseDecodingTable (); - } - - public String encodeBytes (byte[] input) { - StringBuffer output = new StringBuffer (); - int specialLength = input.length % 5; - int normalLength = input.length - specialLength; - - for (int i = 0; i < normalLength; i += 5) { - int b1 = input[i] & 0xff; - int b2 = input[i + 1] & 0xff; - int b3 = input[i + 2] & 0xff; - int b4 = input[i + 3] & 0xff; - int b5 = input[i + 4] & 0xff; - - output.append(eTable.charAt((b1 >> 3) & 0x1f)); - output.append(eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); - output.append(eTable.charAt((b2 >> 1) & 0x1f)); - output.append(eTable.charAt(((b2 << 4) | (b3 >> 4)) & 0x1f)); - output.append(eTable.charAt(((b3 << 1) | (b4 >> 7)) & 0x1f)); - output.append(eTable.charAt((b4 >> 2) & 0x1f)); - output.append(eTable.charAt(((b4 << 3) | (b5 >> 5)) & 0x1f)); - output.append(eTable.charAt(b5 & 0x1f)); - } - - switch (specialLength) { - case 1: { - int b1 = input[normalLength] & 0xff; - output.append (eTable.charAt((b1 >> 3) & 0x1f)); - output.append (eTable.charAt((b1 << 2) & 0x1f)); - output.append (padding).append (padding).append (padding).append (padding).append (padding).append (padding); - break; - } - - case 2: { - int b1 = input[normalLength] & 0xff; - int b2 = input[normalLength + 1] & 0xff; - output.append (eTable.charAt((b1 >> 3) & 0x1f)); - output.append (eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); - output.append (eTable.charAt((b2 >> 1) & 0x1f)); - output.append (eTable.charAt((b2 << 4) & 0x1f)); - output.append (padding).append (padding).append (padding).append (padding); - break; - } - case 3: { - int b1 = input[normalLength] & 0xff; - int b2 = input[normalLength + 1] & 0xff; - int b3 = input[normalLength + 2] & 0xff; - output.append (eTable.charAt((b1 >> 3) & 0x1f)); - output.append (eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); - output.append (eTable.charAt((b2 >> 1) & 0x1f)); - output.append (eTable.charAt(((b2 << 4) | (b3 >> 4)) & 0x1f)); - output.append (eTable.charAt((b3 << 1) & 0x1f)); - output.append (padding).append (padding).append (padding); - break; - } - case 4: { - int b1 = input[normalLength] & 0xff; - int b2 = input[normalLength + 1] & 0xff; - int b3 = input[normalLength + 2] & 0xff; - int b4 = input[normalLength + 3] & 0xff; - output.append (eTable.charAt((b1 >> 3) & 0x1f)); - output.append (eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); - output.append (eTable.charAt((b2 >> 1) & 0x1f)); - output.append (eTable.charAt(((b2 << 4) | (b3 >> 4)) & 0x1f)); - output.append (eTable.charAt(((b3 << 1) | (b4 >> 7)) & 0x1f)); - output.append (eTable.charAt((b4 >> 2) & 0x1f)); - output.append (eTable.charAt((b4 << 3) & 0x1)); - output.append (padding); - break; - } - } - - return output.toString(); - } - - public byte[] decodeBytes (String data) { - ArrayList outStream = new ArrayList(); - - int length = data.length(); - - while (length > 0) { - if (!this.Ignore (data.charAt(length - 1))) break; - length--; - } - - int i = 0; - int finish = length - 8; - for (i = this.NextI (data, i, finish); i < finish; i = this.NextI (data, i, finish)) { - byte b1 = dTable[data.charAt(i++)]; - i = this.NextI (data, i, finish); - byte b2 = dTable[data.charAt(i++)]; - i = this.NextI (data, i, finish); - byte b3 = dTable[data.charAt(i++)]; - i = this.NextI (data, i, finish); - byte b4 = dTable[data.charAt(i++)]; - i = this.NextI (data, i, finish); - byte b5 = dTable[data.charAt(i++)]; - i = this.NextI (data, i, finish); - byte b6 = dTable[data.charAt(i++)]; - i = this.NextI (data, i, finish); - byte b7 = dTable[data.charAt(i++)]; - i = this.NextI (data, i, finish); - byte b8 = dTable[data.charAt(i++)]; - - outStream.add ((byte)((b1 << 3) | (b2 >> 2))); - outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); - outStream.add ((byte)((b4 << 4) | (b5 >> 1))); - outStream.add ((byte)((b5 << 7) | (b6 << 2) | (b7 >> 3))); - outStream.add ((byte)((b7 << 5) | b8)); - } - this.DecodeLastBlock (outStream, - data.charAt(length - 8), data.charAt(length - 7), data.charAt(length - 6), data.charAt(length - 5), - data.charAt(length - 4), data.charAt(length - 3), data.charAt(length - 2), data.charAt(length - 1)); - - byte[] result = new byte[outStream.size()]; - for(int j = 0; j < outStream.size(); j++){ - result[j] = outStream.get(j).byteValue(); - } - - return result; - } - - protected int DecodeLastBlock (ArrayList outStream, char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8) { - if (c3 == padding) { - byte b1 = dTable[c1]; - byte b2 = dTable[c2]; - outStream.add ((byte)((b1 << 3) | (b2 >> 2))); - return 1; - } - - if (c5 == padding) { - byte b1 = dTable[c1]; - byte b2 = dTable[c2]; - byte b3 = dTable[c3]; - byte b4 = dTable[c4]; - outStream.add ((byte)((b1 << 3) | (b2 >> 2))); - outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); - return 2; - } - - if (c6 == padding) { - byte b1 = dTable[c1]; - byte b2 = dTable[c2]; - byte b3 = dTable[c3]; - byte b4 = dTable[c4]; - byte b5 = dTable[c5]; - - outStream.add ((byte)((b1 << 3) | (b2 >> 2))); - outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); - outStream.add ((byte)((b4 << 4) | (b5 >> 1))); - return 3; - } - - if (c8 == padding) { - byte b1 = dTable[c1]; - byte b2 = dTable[c2]; - byte b3 = dTable[c3]; - byte b4 = dTable[c4]; - byte b5 = dTable[c5]; - byte b6 = dTable[c6]; - byte b7 = dTable[c7]; - - outStream.add ((byte)((b1 << 3) | (b2 >> 2))); - outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); - outStream.add ((byte)((b4 << 4) | (b5 >> 1))); - outStream.add ((byte)((b5 << 7) | (b6 << 2) | (b7 >> 3))); - return 4; - } - - else { - byte b1 = dTable[c1]; - byte b2 = dTable[c2]; - byte b3 = dTable[c3]; - byte b4 = dTable[c4]; - byte b5 = dTable[c5]; - byte b6 = dTable[c6]; - byte b7 = dTable[c7]; - byte b8 = dTable[c8]; - outStream.add ((byte)((b1 << 3) | (b2 >> 2))); - outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); - outStream.add ((byte)((b4 << 4) | (b5 >> 1))); - outStream.add ((byte)((b5 << 7) | (b6 << 2) | (b7 >> 3))); - outStream.add ((byte)((b7 << 5) | b8)); - return 5; - } - } - - protected int NextI (String data, int i, int finish) { - while ((i < finish) && this.Ignore (data.charAt(i))) i++; - - return i; - } - - protected boolean Ignore (char c) { - return (c == '\n') || (c == '\r') || (c == '\t') || (c == ' ') || (c == '-'); - } - - protected void InitialiseDecodingTable () { - for (int i = 0; i < eTable.length(); i++) { - dTable[eTable.charAt(i)] = (byte)i; - } - } - - -} +package uk.co.bitethebullet.android.token.util; + +import java.util.ArrayList; + +public class Base32 { + + private static final String DEF_ENCODING_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + private static final char DEF_PADDING = '='; + + private String eTable; //Encoding table + private char padding; + private byte[] dTable; //Decoding table + + public Base32 () + { + this (DEF_ENCODING_TABLE, DEF_PADDING); + } + + public Base32 (char padding){ + this (DEF_ENCODING_TABLE, padding); + } + public Base32 (String encodingTable){ + this (encodingTable, DEF_PADDING); + } + + public Base32 (String encodingTable, char padding) { + this.eTable = encodingTable; + this.padding = padding; + dTable = new byte[0x80]; + InitialiseDecodingTable (); + } + + public String encodeBytes (byte[] input) { + StringBuffer output = new StringBuffer (); + int specialLength = input.length % 5; + int normalLength = input.length - specialLength; + + for (int i = 0; i < normalLength; i += 5) { + int b1 = input[i] & 0xff; + int b2 = input[i + 1] & 0xff; + int b3 = input[i + 2] & 0xff; + int b4 = input[i + 3] & 0xff; + int b5 = input[i + 4] & 0xff; + + output.append(eTable.charAt((b1 >> 3) & 0x1f)); + output.append(eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); + output.append(eTable.charAt((b2 >> 1) & 0x1f)); + output.append(eTable.charAt(((b2 << 4) | (b3 >> 4)) & 0x1f)); + output.append(eTable.charAt(((b3 << 1) | (b4 >> 7)) & 0x1f)); + output.append(eTable.charAt((b4 >> 2) & 0x1f)); + output.append(eTable.charAt(((b4 << 3) | (b5 >> 5)) & 0x1f)); + output.append(eTable.charAt(b5 & 0x1f)); + } + + switch (specialLength) { + case 1: { + int b1 = input[normalLength] & 0xff; + output.append (eTable.charAt((b1 >> 3) & 0x1f)); + output.append (eTable.charAt((b1 << 2) & 0x1f)); + output.append (padding).append (padding).append (padding).append (padding).append (padding).append (padding); + break; + } + + case 2: { + int b1 = input[normalLength] & 0xff; + int b2 = input[normalLength + 1] & 0xff; + output.append (eTable.charAt((b1 >> 3) & 0x1f)); + output.append (eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); + output.append (eTable.charAt((b2 >> 1) & 0x1f)); + output.append (eTable.charAt((b2 << 4) & 0x1f)); + output.append (padding).append (padding).append (padding).append (padding); + break; + } + case 3: { + int b1 = input[normalLength] & 0xff; + int b2 = input[normalLength + 1] & 0xff; + int b3 = input[normalLength + 2] & 0xff; + output.append (eTable.charAt((b1 >> 3) & 0x1f)); + output.append (eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); + output.append (eTable.charAt((b2 >> 1) & 0x1f)); + output.append (eTable.charAt(((b2 << 4) | (b3 >> 4)) & 0x1f)); + output.append (eTable.charAt((b3 << 1) & 0x1f)); + output.append (padding).append (padding).append (padding); + break; + } + case 4: { + int b1 = input[normalLength] & 0xff; + int b2 = input[normalLength + 1] & 0xff; + int b3 = input[normalLength + 2] & 0xff; + int b4 = input[normalLength + 3] & 0xff; + output.append (eTable.charAt((b1 >> 3) & 0x1f)); + output.append (eTable.charAt(((b1 << 2) | (b2 >> 6)) & 0x1f)); + output.append (eTable.charAt((b2 >> 1) & 0x1f)); + output.append (eTable.charAt(((b2 << 4) | (b3 >> 4)) & 0x1f)); + output.append (eTable.charAt(((b3 << 1) | (b4 >> 7)) & 0x1f)); + output.append (eTable.charAt((b4 >> 2) & 0x1f)); + output.append (eTable.charAt((b4 << 3) & 0x1)); + output.append (padding); + break; + } + } + + return output.toString(); + } + + public byte[] decodeBytes (String data) { + ArrayList outStream = new ArrayList(); + + int length = data.length(); + + while (length > 0) { + if (!this.Ignore (data.charAt(length - 1))) break; + length--; + } + + int i = 0; + int finish = length - 8; + for (i = this.NextI (data, i, finish); i < finish; i = this.NextI (data, i, finish)) { + byte b1 = dTable[data.charAt(i++)]; + i = this.NextI (data, i, finish); + byte b2 = dTable[data.charAt(i++)]; + i = this.NextI (data, i, finish); + byte b3 = dTable[data.charAt(i++)]; + i = this.NextI (data, i, finish); + byte b4 = dTable[data.charAt(i++)]; + i = this.NextI (data, i, finish); + byte b5 = dTable[data.charAt(i++)]; + i = this.NextI (data, i, finish); + byte b6 = dTable[data.charAt(i++)]; + i = this.NextI (data, i, finish); + byte b7 = dTable[data.charAt(i++)]; + i = this.NextI (data, i, finish); + byte b8 = dTable[data.charAt(i++)]; + + outStream.add ((byte)((b1 << 3) | (b2 >> 2))); + outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); + outStream.add ((byte)((b4 << 4) | (b5 >> 1))); + outStream.add ((byte)((b5 << 7) | (b6 << 2) | (b7 >> 3))); + outStream.add ((byte)((b7 << 5) | b8)); + } + this.DecodeLastBlock (outStream, + data.charAt(length - 8), data.charAt(length - 7), data.charAt(length - 6), data.charAt(length - 5), + data.charAt(length - 4), data.charAt(length - 3), data.charAt(length - 2), data.charAt(length - 1)); + + byte[] result = new byte[outStream.size()]; + for(int j = 0; j < outStream.size(); j++){ + result[j] = outStream.get(j).byteValue(); + } + + return result; + } + + protected int DecodeLastBlock (ArrayList outStream, char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8) { + if (c3 == padding) { + byte b1 = dTable[c1]; + byte b2 = dTable[c2]; + outStream.add ((byte)((b1 << 3) | (b2 >> 2))); + return 1; + } + + if (c5 == padding) { + byte b1 = dTable[c1]; + byte b2 = dTable[c2]; + byte b3 = dTable[c3]; + byte b4 = dTable[c4]; + outStream.add ((byte)((b1 << 3) | (b2 >> 2))); + outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); + return 2; + } + + if (c6 == padding) { + byte b1 = dTable[c1]; + byte b2 = dTable[c2]; + byte b3 = dTable[c3]; + byte b4 = dTable[c4]; + byte b5 = dTable[c5]; + + outStream.add ((byte)((b1 << 3) | (b2 >> 2))); + outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); + outStream.add ((byte)((b4 << 4) | (b5 >> 1))); + return 3; + } + + if (c8 == padding) { + byte b1 = dTable[c1]; + byte b2 = dTable[c2]; + byte b3 = dTable[c3]; + byte b4 = dTable[c4]; + byte b5 = dTable[c5]; + byte b6 = dTable[c6]; + byte b7 = dTable[c7]; + + outStream.add ((byte)((b1 << 3) | (b2 >> 2))); + outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); + outStream.add ((byte)((b4 << 4) | (b5 >> 1))); + outStream.add ((byte)((b5 << 7) | (b6 << 2) | (b7 >> 3))); + return 4; + } + + else { + byte b1 = dTable[c1]; + byte b2 = dTable[c2]; + byte b3 = dTable[c3]; + byte b4 = dTable[c4]; + byte b5 = dTable[c5]; + byte b6 = dTable[c6]; + byte b7 = dTable[c7]; + byte b8 = dTable[c8]; + outStream.add ((byte)((b1 << 3) | (b2 >> 2))); + outStream.add ((byte)((b2 << 6) | (b3 << 1) | (b4 >> 4))); + outStream.add ((byte)((b4 << 4) | (b5 >> 1))); + outStream.add ((byte)((b5 << 7) | (b6 << 2) | (b7 >> 3))); + outStream.add ((byte)((b7 << 5) | b8)); + return 5; + } + } + + protected int NextI (String data, int i, int finish) { + while ((i < finish) && this.Ignore (data.charAt(i))) i++; + + return i; + } + + protected boolean Ignore (char c) { + return (c == '\n') || (c == '\r') || (c == '\t') || (c == ' ') || (c == '-'); + } + + protected void InitialiseDecodingTable () { + for (int i = 0; i < eTable.length(); i++) { + dTable[eTable.charAt(i)] = (byte)i; + } + } + + +} diff --git a/src/main/java/uk/co/bitethebullet/android/token/util/Base64.java b/app/src/main/java/uk/co/bitethebullet/android/token/util/Base64.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/util/Base64.java rename to app/src/main/java/uk/co/bitethebullet/android/token/util/Base64.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/util/FontManager.java b/app/src/main/java/uk/co/bitethebullet/android/token/util/FontManager.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/util/FontManager.java rename to app/src/main/java/uk/co/bitethebullet/android/token/util/FontManager.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/util/SeedConvertor.java b/app/src/main/java/uk/co/bitethebullet/android/token/util/SeedConvertor.java similarity index 96% rename from src/main/java/uk/co/bitethebullet/android/token/util/SeedConvertor.java rename to app/src/main/java/uk/co/bitethebullet/android/token/util/SeedConvertor.java index b2e71e7..8b9df84 100644 --- a/src/main/java/uk/co/bitethebullet/android/token/util/SeedConvertor.java +++ b/app/src/main/java/uk/co/bitethebullet/android/token/util/SeedConvertor.java @@ -1,61 +1,61 @@ -/* - * Copyright Mark McAvoy - www.bitethebullet.co.uk 2011 - * - * This file is part of Android Token. - * - * Android Token is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Android Token is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Android Token. If not, see . - * - */ -package uk.co.bitethebullet.android.token.util; - -import java.io.IOException; -import uk.co.bitethebullet.android.token.tokens.HotpToken; - -public class SeedConvertor { - - public static final int HEX_FORMAT = 0; - public static final int BASE32_FORMAT = 1; - public static final int BASE64_FORMAT = 2; - - public static byte[] ConvertFromEncodingToBA(String input, int currentFormat) throws IOException{ - - if(currentFormat == 0){ - //hex - return HotpToken.stringToHex(input); - }else if(currentFormat == 1){ - //base 32 - Base32 base32 = new Base32(); - return base32.decodeBytes(input.toUpperCase()); - }else if(currentFormat == 2){ - //base64 - return Base64.decode(input); - }else - return null; - } - - public static String ConvertFromBA(byte[] input, int targetFormat){ - if(targetFormat == 0){ - //hex - return HotpToken.byteArrayToHexString(input); - }else if(targetFormat == 1){ - //base 32 - Base32 base32 = new Base32(); - return base32.encodeBytes(input); - }else if(targetFormat == 2){ - //base64 - return Base64.encodeBytes(input); - }else - return null; - } -} +/* + * Copyright Mark McAvoy - www.bitethebullet.co.uk 2011 + * + * This file is part of Android Token. + * + * Android Token is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Android Token is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Android Token. If not, see . + * + */ +package uk.co.bitethebullet.android.token.util; + +import java.io.IOException; +import uk.co.bitethebullet.android.token.tokens.HotpToken; + +public class SeedConvertor { + + public static final int HEX_FORMAT = 0; + public static final int BASE32_FORMAT = 1; + public static final int BASE64_FORMAT = 2; + + public static byte[] ConvertFromEncodingToBA(String input, int currentFormat) throws IOException{ + + if(currentFormat == 0){ + //hex + return HotpToken.stringToHex(input); + }else if(currentFormat == 1){ + //base 32 + Base32 base32 = new Base32(); + return base32.decodeBytes(input.toUpperCase()); + }else if(currentFormat == 2){ + //base64 + return Base64.decode(input); + }else + return null; + } + + public static String ConvertFromBA(byte[] input, int targetFormat){ + if(targetFormat == 0){ + //hex + return HotpToken.byteArrayToHexString(input); + }else if(targetFormat == 1){ + //base 32 + Base32 base32 = new Base32(); + return base32.encodeBytes(input); + }else if(targetFormat == 2){ + //base64 + return Base64.encodeBytes(input); + }else + return null; + } +} diff --git a/src/main/java/uk/co/bitethebullet/android/token/zxing/IntentIntegrator.java b/app/src/main/java/uk/co/bitethebullet/android/token/zxing/IntentIntegrator.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/zxing/IntentIntegrator.java rename to app/src/main/java/uk/co/bitethebullet/android/token/zxing/IntentIntegrator.java diff --git a/src/main/java/uk/co/bitethebullet/android/token/zxing/IntentResult.java b/app/src/main/java/uk/co/bitethebullet/android/token/zxing/IntentResult.java similarity index 100% rename from src/main/java/uk/co/bitethebullet/android/token/zxing/IntentResult.java rename to app/src/main/java/uk/co/bitethebullet/android/token/zxing/IntentResult.java diff --git a/src/main/res/drawable/add.png b/app/src/main/res/drawable/add.png similarity index 100% rename from src/main/res/drawable/add.png rename to app/src/main/res/drawable/add.png diff --git a/src/main/res/drawable/android50.png b/app/src/main/res/drawable/android50.png similarity index 100% rename from src/main/res/drawable/android50.png rename to app/src/main/res/drawable/android50.png diff --git a/src/main/res/drawable/androidtoken.png b/app/src/main/res/drawable/androidtoken.png similarity index 100% rename from src/main/res/drawable/androidtoken.png rename to app/src/main/res/drawable/androidtoken.png diff --git a/src/main/res/drawable/baseline_add_black_24dp.png b/app/src/main/res/drawable/baseline_add_black_24dp.png similarity index 100% rename from src/main/res/drawable/baseline_add_black_24dp.png rename to app/src/main/res/drawable/baseline_add_black_24dp.png diff --git a/src/main/res/drawable/icon.png b/app/src/main/res/drawable/icon.png similarity index 100% rename from src/main/res/drawable/icon.png rename to app/src/main/res/drawable/icon.png diff --git a/src/main/res/drawable/xclock.png b/app/src/main/res/drawable/xclock.png similarity index 100% rename from src/main/res/drawable/xclock.png rename to app/src/main/res/drawable/xclock.png diff --git a/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml similarity index 100% rename from src/main/res/layout/about.xml rename to app/src/main/res/layout/about.xml diff --git a/src/main/res/layout/activity_q_r_code.xml b/app/src/main/res/layout/activity_q_r_code.xml similarity index 100% rename from src/main/res/layout/activity_q_r_code.xml rename to app/src/main/res/layout/activity_q_r_code.xml diff --git a/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml similarity index 100% rename from src/main/res/layout/activity_setting.xml rename to app/src/main/res/layout/activity_setting.xml diff --git a/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml similarity index 100% rename from src/main/res/layout/main.xml rename to app/src/main/res/layout/main.xml diff --git a/src/main/res/layout/otpdialog.xml b/app/src/main/res/layout/otpdialog.xml similarity index 100% rename from src/main/res/layout/otpdialog.xml rename to app/src/main/res/layout/otpdialog.xml diff --git a/src/main/res/layout/pinchange.xml b/app/src/main/res/layout/pinchange.xml similarity index 100% rename from src/main/res/layout/pinchange.xml rename to app/src/main/res/layout/pinchange.xml diff --git a/src/main/res/layout/pindefinitiondialog.xml b/app/src/main/res/layout/pindefinitiondialog.xml similarity index 100% rename from src/main/res/layout/pindefinitiondialog.xml rename to app/src/main/res/layout/pindefinitiondialog.xml diff --git a/src/main/res/layout/pinremove.xml b/app/src/main/res/layout/pinremove.xml similarity index 100% rename from src/main/res/layout/pinremove.xml rename to app/src/main/res/layout/pinremove.xml diff --git a/src/main/res/layout/token_add.xml b/app/src/main/res/layout/token_add.xml similarity index 100% rename from src/main/res/layout/token_add.xml rename to app/src/main/res/layout/token_add.xml diff --git a/src/main/res/layout/token_list_row.xml b/app/src/main/res/layout/token_list_row.xml similarity index 100% rename from src/main/res/layout/token_list_row.xml rename to app/src/main/res/layout/token_list_row.xml diff --git a/src/main/res/menu/token_item_menu.xml b/app/src/main/res/menu/token_item_menu.xml similarity index 100% rename from src/main/res/menu/token_item_menu.xml rename to app/src/main/res/menu/token_item_menu.xml diff --git a/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml similarity index 100% rename from src/main/res/navigation/nav_graph.xml rename to app/src/main/res/navigation/nav_graph.xml diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml new file mode 100644 index 0000000..a6b3dae --- /dev/null +++ b/app/src/main/res/values-zh/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml similarity index 100% rename from src/main/res/values/arrays.xml rename to app/src/main/res/values/arrays.xml diff --git a/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml similarity index 100% rename from src/main/res/values/dimens.xml rename to app/src/main/res/values/dimens.xml diff --git a/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml similarity index 100% rename from src/main/res/values/strings.xml rename to app/src/main/res/values/strings.xml diff --git a/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml similarity index 100% rename from src/main/res/values/styles.xml rename to app/src/main/res/values/styles.xml diff --git a/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml similarity index 100% rename from src/main/res/xml/root_preferences.xml rename to app/src/main/res/xml/root_preferences.xml diff --git a/src/test/java/uk/co/bitethebullet/android/token/HotpTokenTests.java b/app/src/test/java/uk/co/bitethebullet/android/token/HotpTokenTests.java similarity index 100% rename from src/test/java/uk/co/bitethebullet/android/token/HotpTokenTests.java rename to app/src/test/java/uk/co/bitethebullet/android/token/HotpTokenTests.java diff --git a/src/test/java/uk/co/bitethebullet/android/token/ParseUrlTests.java b/app/src/test/java/uk/co/bitethebullet/android/token/ParseUrlTests.java similarity index 100% rename from src/test/java/uk/co/bitethebullet/android/token/ParseUrlTests.java rename to app/src/test/java/uk/co/bitethebullet/android/token/ParseUrlTests.java diff --git a/src/test/java/uk/co/bitethebullet/android/token/SeedConvertorTests.java b/app/src/test/java/uk/co/bitethebullet/android/token/SeedConvertorTests.java similarity index 100% rename from src/test/java/uk/co/bitethebullet/android/token/SeedConvertorTests.java rename to app/src/test/java/uk/co/bitethebullet/android/token/SeedConvertorTests.java diff --git a/src/test/java/uk/co/bitethebullet/android/token/TotpTokenTests.java b/app/src/test/java/uk/co/bitethebullet/android/token/TotpTokenTests.java similarity index 100% rename from src/test/java/uk/co/bitethebullet/android/token/TotpTokenTests.java rename to app/src/test/java/uk/co/bitethebullet/android/token/TotpTokenTests.java diff --git a/build.gradle b/build.gradle index efb6b53..4aa146a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,60 +1,41 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply from: "upload_version.gradle" buildscript { + ext { + kotlin_version = '1.5.0' + } repositories { - jcenter() google() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' - } -} -repositories { - mavenCentral() - jcenter() - maven { - url "https://maven.google.com" + classpath "com.android.tools.build:gradle:4.1.3" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files } } - -apply plugin: 'com.android.application' - - -android { - compileSdkVersion 30 - - buildTypes { - release { - minifyEnabled false - proguardFile getDefaultProguardFile('proguard-android.txt') +allprojects { + repositories { + google() + jcenter() + maven { url "https://jitpack.io" } + maven { + url 'https://maven.aliyun.com/repository/public' } - } - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 6 - versionName "3.0" - } - - packagingOptions { - exclude 'LICENSE.txt' +// maven { +// credentials { +// username= "$maven_name" +// password="$maven_password" +// } +// url "$release_url" +// } } } -dependencies { - - implementation "androidx.preference:preference:1.1.0" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'com.google.android.material:material:1.1.0' - implementation 'androidx.biometric:biometric:1.0.1' - implementation 'androidx.navigation:navigation-fragment:2.3.0' - implementation 'androidx.navigation:navigation-ui:2.3.0' - implementation 'androidmads.library.qrgenearator:QRGenearator:1.0.4' - implementation 'com.google.zxing:core:3.3.2' - - testImplementation 'junit:junit:4.13' - testImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' - testImplementation 'com.google.truth:truth:1.0.1' -} +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/demo/build.gradle b/demo/build.gradle new file mode 100644 index 0000000..2d32744 --- /dev/null +++ b/demo/build.gradle @@ -0,0 +1,49 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-android-extensions' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "com.fly.demo" + minSdkVersion 16 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + +// implementation project(path: ':optlib') + implementation "com.github.flyopensource:androidtoken:b62e9e4b8a" +} \ No newline at end of file diff --git a/demo/proguard-rules.pro b/demo/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/demo/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/demo/src/androidTest/java/com/fly/demo/ExampleInstrumentedTest.kt b/demo/src/androidTest/java/com/fly/demo/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..9e9180a --- /dev/null +++ b/demo/src/androidTest/java/com/fly/demo/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.fly.demo + +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.fly.demo", appContext.packageName) + } +} \ No newline at end of file diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7eb5c98 --- /dev/null +++ b/demo/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/java/com/fly/demo/MainActivity.kt b/demo/src/main/java/com/fly/demo/MainActivity.kt new file mode 100644 index 0000000..ddf1469 --- /dev/null +++ b/demo/src/main/java/com/fly/demo/MainActivity.kt @@ -0,0 +1,31 @@ +package com.fly.demo + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.fly.optlib.tokens.HotpToken +import com.fly.optlib.utils.SeedConvertor +import kotlinx.android.synthetic.main.activity_main.* + +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + btCreate.setOnClickListener { createPSWD() } + } + + private fun createPSWD() { + + var hexSeed = randomSeed() + val token = HotpToken("mark", "123456789", hexSeed, 1, 6, "fly") + val otp = token.generateOtp() + tvInfo.text = "password : $otp" + } + + private fun randomSeed(): String? { + val ba = SeedConvertor.ConvertFromEncodingToBA(HotpToken.generateNewSeed(160), + SeedConvertor.HEX_FORMAT) + var hexSeed = SeedConvertor.ConvertFromBA(ba, SeedConvertor.BASE32_FORMAT) + hexSeed = SeedConvertor.ConvertFromBA(SeedConvertor.ConvertFromEncodingToBA(hexSeed, SeedConvertor.BASE32_FORMAT), 0) + return hexSeed + } +} \ No newline at end of file diff --git a/demo/src/main/res/drawable-v24/ic_launcher_foreground.xml b/demo/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/demo/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/ic_launcher_background.xml b/demo/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/demo/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..8b4e466 --- /dev/null +++ b/demo/src/main/res/layout/activity_main.xml @@ -0,0 +1,28 @@ + + + + +