Variable digits lenths (#30)

Adds support for variable digit lengths
This commit is contained in:
SuperVirus 2017-08-18 11:39:36 +02:00 committed by Jakob Nixdorf
parent f616350aae
commit c17bc0dbfb
5 changed files with 58 additions and 6 deletions

View file

@ -93,11 +93,13 @@ public class MainActivity extends BaseActivity
final EditText labelInput = (EditText) inputView.findViewById(R.id.manual_label);
final EditText secretInput = (EditText) inputView.findViewById(R.id.manual_secret);
final EditText periodInput = (EditText) inputView.findViewById(R.id.manual_period);
final EditText digitsInput = (EditText) inputView.findViewById(R.id.manual_digits);
final Spinner algorithmInput = (Spinner) inputView.findViewById(R.id.manual_algorithm);
typeInput.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_expandable_list_item_1, Entry.OTPType.values()));
algorithmInput.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_expandable_list_item_1, TokenCalculator.HashAlgorithm.values()));
periodInput.setText(Integer.toString(TokenCalculator.TOTP_DEFAULT_PERIOD));
digitsInput.setText(Integer.toString(TokenCalculator.TOTP_DEFAULT_DIGITS));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.dialog_title_manual_entry)
@ -112,8 +114,9 @@ public class MainActivity extends BaseActivity
String label = labelInput.getText().toString();
String secret = secretInput.getText().toString();
int period = Integer.parseInt(periodInput.getText().toString());
int digits = Integer.parseInt(digitsInput.getText().toString());
Entry e = new Entry(type, secret, period, label, algorithm);
Entry e = new Entry(type, secret, period, digits, label, algorithm);
e.updateOTP();
adapter.addEntry(e);
adapter.saveEntries();

View file

@ -42,11 +42,13 @@ public class Entry {
private static final String JSON_SECRET = "secret";
private static final String JSON_LABEL = "label";
private static final String JSON_PERIOD = "period";
private static final String JSON_DIGITS = "digits";
private static final String JSON_TYPE = "type";
private static final String JSON_ALGORITHM = "algorithm";
private OTPType type = OTPType.TOTP;
private int period = TokenCalculator.TOTP_DEFAULT_PERIOD;
private int digits = TokenCalculator.TOTP_DEFAULT_DIGITS;
private TokenCalculator.HashAlgorithm algorithm = TokenCalculator.DEFAULT_ALGORITHM;
private byte[] secret;
private String label;
@ -55,10 +57,11 @@ public class Entry {
public Entry(){}
public Entry(OTPType type, String secret, int period, String label, TokenCalculator.HashAlgorithm algorithm) {
public Entry(OTPType type, String secret, int period, int digits, String label, TokenCalculator.HashAlgorithm algorithm) {
this.type = type;
this.secret = new Base32().decode(secret.toUpperCase());
this.period = period;
this.digits = digits;
this.label = label;
this.algorithm = algorithm;
}
@ -83,6 +86,7 @@ public class Entry {
String issuer = uri.getQueryParameter("issuer");
String period = uri.getQueryParameter("period");
String digits = uri.getQueryParameter("digits");
String algorithm = uri.getQueryParameter("algorithm");
if(issuer != null){
@ -98,6 +102,12 @@ public class Entry {
this.period = TokenCalculator.TOTP_DEFAULT_PERIOD;
}
if (digits != null) {
this.digits = Integer.parseInt(digits);
} else {
this.digits = TokenCalculator.TOTP_DEFAULT_DIGITS;
}
if (algorithm != null) {
this.algorithm = TokenCalculator.HashAlgorithm.valueOf(algorithm.toUpperCase());
} else {
@ -110,6 +120,12 @@ public class Entry {
this.label = jsonObj.getString(JSON_LABEL);
this.period = jsonObj.getInt(JSON_PERIOD);
try {
this.digits = jsonObj.getInt(JSON_DIGITS);
} catch(JSONException e) {
this.digits = TokenCalculator.TOTP_DEFAULT_DIGITS;
}
try {
this.type = OTPType.valueOf(jsonObj.getString(JSON_TYPE));
} catch(JSONException e) {
@ -128,6 +144,7 @@ public class Entry {
jsonObj.put(JSON_SECRET, new String(new Base32().encode(getSecret())));
jsonObj.put(JSON_LABEL, getLabel());
jsonObj.put(JSON_PERIOD, getPeriod());
jsonObj.put(JSON_DIGITS, getDigits());
jsonObj.put(JSON_TYPE, getType().toString());
jsonObj.put(JSON_ALGORITHM, algorithm.toString());
@ -166,6 +183,14 @@ public class Entry {
this.period = period;
}
public int getDigits() {
return digits;
}
public void setDigits(int digits) {
this.digits = digits;
}
public TokenCalculator.HashAlgorithm getAlgorithm() {
return this.algorithm;
}
@ -187,7 +212,7 @@ public class Entry {
long counter = time / this.getPeriod();
if (counter > last_update) {
currentOTP = TokenCalculator.TOTP(secret, period, algorithm);
currentOTP = TokenCalculator.TOTP(secret, period, digits, algorithm);
last_update = counter;
return true;
@ -202,6 +227,7 @@ public class Entry {
if (o == null || getClass() != o.getClass()) return false;
Entry entry = (Entry) o;
return period == entry.period &&
digits == entry.digits &&
type == entry.type &&
algorithm == entry.algorithm &&
Arrays.equals(secret, entry.secret) &&
@ -210,6 +236,6 @@ public class Entry {
@Override
public int hashCode() {
return Objects.hash(type, period, algorithm, secret, label);
return Objects.hash(type, period, digits, algorithm, secret, label);
}
}

View file

@ -33,6 +33,7 @@ import javax.crypto.spec.SecretKeySpec;
public class TokenCalculator {
public static final int TOTP_DEFAULT_PERIOD = 30;
public static final int TOTP_DEFAULT_DIGITS = 6;
public enum HashAlgorithm {
SHA1, SHA256, SHA512
@ -50,8 +51,8 @@ public class TokenCalculator {
return mac.doFinal(data);
}
public static String TOTP(byte[] secret, int period, HashAlgorithm algorithm) {
return String.format("%06d", TOTP(secret, period, System.currentTimeMillis() / 1000, 6, algorithm));
public static String TOTP(byte[] secret, int period, int digits, HashAlgorithm algorithm) {
return String.format("%0" + digits + "d", TOTP(secret, period, System.currentTimeMillis() / 1000, digits, algorithm));
}
public static int TOTP(byte[] key, int period, long time, int digits, HashAlgorithm algorithm)

View file

@ -86,6 +86,27 @@
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/label_digits"/>
<EditText
android:id="@+id/manual_digits"
android:layout_weight="7"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="@string/label_digits"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -21,6 +21,7 @@
<string name="label_type">Type</string>
<string name="label_secret">Secret</string>
<string name="label_period">Period</string>
<string name="label_digits">Digits</string>
<string name="label_label">Label</string>
<string name="label_algorithm">Algorithm</string>