Implement manual entry of HOTP accounts

This commit is contained in:
Jakob Nixdorf 2018-03-21 07:59:03 +01:00
parent c8d47ca3bf
commit e76b3c3b96
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
4 changed files with 75 additions and 6 deletions

View file

@ -33,6 +33,7 @@ import javax.crypto.spec.SecretKeySpec;
public class TokenCalculator { public class TokenCalculator {
public static final int TOTP_DEFAULT_PERIOD = 30; public static final int TOTP_DEFAULT_PERIOD = 30;
public static final int TOTP_DEFAULT_DIGITS = 6; public static final int TOTP_DEFAULT_DIGITS = 6;
public static final int HOTP_INITIAL_COUNTER = 1;
public static final int STEAM_DEFAULT_DIGITS = 5; public static final int STEAM_DEFAULT_DIGITS = 5;
private static final char[] STEAMCHARS = new char[] { private static final char[] STEAMCHARS = new char[] {

View file

@ -33,6 +33,7 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner; import android.widget.Spinner;
import com.github.aakira.expandablelayout.ExpandableLayoutListenerAdapter; import com.github.aakira.expandablelayout.ExpandableLayoutListenerAdapter;
@ -57,8 +58,11 @@ public class ManualEntryDialog {
final Spinner typeInput = inputView.findViewById(R.id.manual_type); final Spinner typeInput = inputView.findViewById(R.id.manual_type);
final EditText labelInput = inputView.findViewById(R.id.manual_label); final EditText labelInput = inputView.findViewById(R.id.manual_label);
final EditText secretInput = inputView.findViewById(R.id.manual_secret); final EditText secretInput = inputView.findViewById(R.id.manual_secret);
final EditText counterInput = inputView.findViewById(R.id.manual_counter);
final EditText periodInput = inputView.findViewById(R.id.manual_period); final EditText periodInput = inputView.findViewById(R.id.manual_period);
final EditText digitsInput = inputView.findViewById(R.id.manual_digits); final EditText digitsInput = inputView.findViewById(R.id.manual_digits);
final LinearLayout counterLayout = inputView.findViewById(R.id.manual_layout_counter);
final LinearLayout periodLayout = inputView.findViewById(R.id.manual_layout_period);
final Spinner algorithmInput = inputView.findViewById(R.id.manual_algorithm); final Spinner algorithmInput = inputView.findViewById(R.id.manual_algorithm);
final Button tagsInput = inputView.findViewById(R.id.manual_tags); final Button tagsInput = inputView.findViewById(R.id.manual_tags);
@ -75,6 +79,7 @@ public class ManualEntryDialog {
periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD)); periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD));
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS)); digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
counterInput.setText(String.format(Locale.US, "%d", TokenCalculator.HOTP_INITIAL_COUNTER));
typeInput.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { typeInput.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
@ -82,6 +87,9 @@ public class ManualEntryDialog {
Entry.OTPType type = (Entry.OTPType) adapterView.getSelectedItem(); Entry.OTPType type = (Entry.OTPType) adapterView.getSelectedItem();
if (type == Entry.OTPType.STEAM) { if (type == Entry.OTPType.STEAM) {
counterLayout.setVisibility(View.GONE);
periodLayout.setVisibility(View.VISIBLE);
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.STEAM_DEFAULT_DIGITS)); digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.STEAM_DEFAULT_DIGITS));
periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD)); periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD));
algorithmInput.setSelection(algorithmAdapter.getPosition(TokenCalculator.HashAlgorithm.SHA1)); algorithmInput.setSelection(algorithmAdapter.getPosition(TokenCalculator.HashAlgorithm.SHA1));
@ -89,7 +97,18 @@ public class ManualEntryDialog {
digitsInput.setEnabled(false); digitsInput.setEnabled(false);
periodInput.setEnabled(false); periodInput.setEnabled(false);
algorithmInput.setEnabled(false); algorithmInput.setEnabled(false);
} else { } else if (type == Entry.OTPType.TOTP) {
counterLayout.setVisibility(View.GONE);
periodLayout.setVisibility(View.VISIBLE);
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
digitsInput.setEnabled(true);
periodInput.setEnabled(true);
algorithmInput.setEnabled(true);
} else if (type == Entry.OTPType.HOTP) {
counterLayout.setVisibility(View.VISIBLE);
periodLayout.setVisibility(View.GONE);
digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS)); digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS));
digitsInput.setEnabled(true); digitsInput.setEnabled(true);
periodInput.setEnabled(true); periodInput.setEnabled(true);
@ -166,11 +185,12 @@ public class ManualEntryDialog {
Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem(); Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem();
TokenCalculator.HashAlgorithm algorithm = (TokenCalculator.HashAlgorithm) algorithmInput.getSelectedItem(); TokenCalculator.HashAlgorithm algorithm = (TokenCalculator.HashAlgorithm) algorithmInput.getSelectedItem();
String label = labelInput.getText().toString();
String secret = secretInput.getText().toString();
int digits = Integer.parseInt(digitsInput.getText().toString());
if (type == Entry.OTPType.TOTP || type == Entry.OTPType.STEAM) { if (type == Entry.OTPType.TOTP || type == Entry.OTPType.STEAM) {
String label = labelInput.getText().toString();
String secret = secretInput.getText().toString();
int period = Integer.parseInt(periodInput.getText().toString()); int period = Integer.parseInt(periodInput.getText().toString());
int digits = Integer.parseInt(digitsInput.getText().toString());
Entry e = new Entry(type, secret, period, digits, label, algorithm, tagsAdapter.getActiveTags()); Entry e = new Entry(type, secret, period, digits, label, algorithm, tagsAdapter.getActiveTags());
e.updateOTP(); e.updateOTP();
@ -178,6 +198,13 @@ public class ManualEntryDialog {
adapter.saveEntries(); adapter.saveEntries();
callingActivity.refreshTags(); callingActivity.refreshTags();
} else if (type == Entry.OTPType.HOTP) {
long counter = Long.parseLong(counterInput.getText().toString());
Entry e = new Entry(type, secret, counter, digits, label, algorithm, tagsAdapter.getActiveTags());
e.updateOTP();
adapter.addEntry(e);
adapter.saveEntries();
} }
} }
}) })
@ -204,11 +231,25 @@ public class ManualEntryDialog {
@Override @Override
public void afterTextChanged(Editable editable) { public void afterTextChanged(Editable editable) {
if (TextUtils.isEmpty(labelInput.getText()) || TextUtils.isEmpty(secretInput.getText()) if (TextUtils.isEmpty(labelInput.getText()) || TextUtils.isEmpty(secretInput.getText())
|| TextUtils.isEmpty(periodInput.getText()) || Integer.parseInt(periodInput.getText().toString()) == 0
|| TextUtils.isEmpty(digitsInput.getText()) || Integer.parseInt(digitsInput.getText().toString()) == 0) { || TextUtils.isEmpty(digitsInput.getText()) || Integer.parseInt(digitsInput.getText().toString()) == 0) {
positiveButton.setEnabled(false); positiveButton.setEnabled(false);
} else { } else {
positiveButton.setEnabled(true); Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem();
if (type == Entry.OTPType.HOTP) {
if (TextUtils.isEmpty(counterInput.getText())) {
positiveButton.setEnabled(false);
} else {
positiveButton.setEnabled(true);
}
} else if (type == Entry.OTPType.TOTP || type == Entry.OTPType.STEAM) {
if (TextUtils.isEmpty(periodInput.getText()) || Integer.parseInt(periodInput.getText().toString()) == 0) {
positiveButton.setEnabled(false);
} else {
positiveButton.setEnabled(true);
}
} else {
positiveButton.setEnabled(true);
}
} }
} }
}; };
@ -217,5 +258,6 @@ public class ManualEntryDialog {
secretInput.addTextChangedListener(watcher); secretInput.addTextChangedListener(watcher);
periodInput.addTextChangedListener(watcher); periodInput.addTextChangedListener(watcher);
digitsInput.addTextChangedListener(watcher); digitsInput.addTextChangedListener(watcher);
counterInput.addTextChangedListener(watcher);
} }
} }

View file

@ -70,6 +70,30 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/manual_layout_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone" >
<TextView
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/label_counter"/>
<EditText
android:id="@+id/manual_counter"
android:layout_weight="7"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="@string/label_counter"/>
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -111,6 +135,7 @@
app:ael_expanded="false"> app:ael_expanded="false">
<LinearLayout <LinearLayout
android:id="@+id/manual_layout_period"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical"

View file

@ -25,6 +25,7 @@
<string name="label_secret">Secret</string> <string name="label_secret">Secret</string>
<string name="label_period">Period</string> <string name="label_period">Period</string>
<string name="label_digits">Digits</string> <string name="label_digits">Digits</string>
<string name="label_counter">Counter</string>
<string name="label_label">Label</string> <string name="label_label">Label</string>
<string name="label_algorithm">Algorithm</string> <string name="label_algorithm">Algorithm</string>
<string name="label_tags">Tags</string> <string name="label_tags">Tags</string>