Multiple improvements to KeyInfo.java

- Got rid of the setters as those won't be used anyway
- Renamed 'algo' to 'algorithm'
- KeyInfo.FromURL is now guaranteed to produce a valid result
This commit is contained in:
Impyy 2016-08-16 12:28:27 +02:00
parent b3957bb76f
commit d4007ab065
3 changed files with 49 additions and 65 deletions

View file

@ -43,7 +43,7 @@ public class MainActivity extends AppCompatActivity {
KeyInfo info = (KeyInfo)data.getSerializableExtra("Keyinfo"); KeyInfo info = (KeyInfo)data.getSerializableExtra("Keyinfo");
String nowTimeString = (Long.toHexString(System.currentTimeMillis() / 1000 / info.getPeriod())); String nowTimeString = (Long.toHexString(System.currentTimeMillis() / 1000 / info.getPeriod()));
String totp = TOTP.generateTOTP(info.getSecret(), nowTimeString, info.getDigits(), info.getAlgo()); String totp = TOTP.generateTOTP(info.getSecret(), nowTimeString, info.getDigits(), info.getAlgorithm());
tvTotp.setText(totp); tvTotp.setText(totp);
} }

View file

@ -11,105 +11,87 @@ public class KeyInfo implements Serializable {
private String label; private String label;
private byte[] secret; private byte[] secret;
private String issuer; private String issuer;
private String algo; private String algorithm = "HmacSHA1";
private int digits; private int digits = 6;
private long counter; private long counter;
private int period; private int period = 30;
public String getType() { public String getType() {
return type; return type;
} }
public KeyInfo setType(String type) {
this.type = type;
return this;
}
public String getLabel() { public String getLabel() {
return label; return label;
} }
public KeyInfo setLabel(String label) {
this.label = label;
return this;
}
public byte[] getSecret() { public byte[] getSecret() {
return secret; return secret;
} }
public KeyInfo setSecret(byte[] secret) {
this.secret = secret;
return this;
}
public String getIssuer() { public String getIssuer() {
return issuer; return issuer;
} }
public String getAlgorithm() {
public KeyInfo setIssuer(String issuer) { return algorithm;
this.issuer = issuer;
return this;
} }
public String getAlgo() {
return algo;
}
public KeyInfo setAlgo(String algo) {
this.algo = algo;
return this;
}
public int getDigits() { public int getDigits() {
return digits; return digits;
} }
public KeyInfo setDigits(int digits) {
this.digits = digits;
return this;
}
public long getCounter() { public long getCounter() {
return counter; return counter;
} }
public KeyInfo setCounter(long counter) {
this.counter = counter;
return this;
}
public int getPeriod() { public int getPeriod() {
return period; return period;
} }
public KeyInfo setPeriod(int period) { private KeyInfo() { }
this.period = period;
return this;
}
private KeyInfo() {
}
public static KeyInfo FromURL(String s) throws Exception { public static KeyInfo FromURL(String s) throws Exception {
final Uri url = Uri.parse(s); final Uri url = Uri.parse(s);
if (!url.getScheme().equals("otpauth")) { if (!url.getScheme().equals("otpauth")) {
throw new Exception("unsupported protocol"); throw new Exception("unsupported protocol");
} }
KeyInfo info = new KeyInfo(); KeyInfo info = new KeyInfo();
info.type = url.getHost();
// only 'totp' and 'hotp' are supported
info.type = url.getHost();
if (info.type.equals("totp") && info.type.equals("hotp")) {
throw new Exception("unsupported type");
}
// 'secret' is a required parameter
String secret = url.getQueryParameter("secret"); String secret = url.getQueryParameter("secret");
if (secret == null) {
throw new Exception("'secret' is not set");
}
info.secret = Base32.decode(secret); info.secret = Base32.decode(secret);
info.issuer = url.getQueryParameter("issuer");
info.label = url.getPath(); // provider info used to disambiguate accounts
info.algo = url.getQueryParameter("algorithm") != null ? url.getQueryParameter("algorithm") : "HmacSHA1"; // these parameters are not required but I don't want them to be null either
info.period = url.getQueryParameter("period") != null ? Integer.getInteger(url.getQueryParameter("period")) : 30; String issuer = url.getQueryParameter("issuer");
info.digits = url.getQueryParameter("digits") != null ? Integer.getInteger(url.getQueryParameter("digits")) : 6; String label = url.getQueryParameter("label");
info.counter = url.getQueryParameter("counter") != null ? Long.getLong(url.getQueryParameter("counter")) : 0; info.issuer = issuer != null ? issuer : "";
info.label = label != null ? label : "";
// just use the defaults if these parameters aren't set
String algorithm = url.getQueryParameter("algorithm");
if (algorithm != null) {
info.algorithm = "Hmac" + algorithm;
}
String period = url.getQueryParameter("period");
if (period != null) {
info.period = Integer.getInteger(period);
}
String digits = url.getQueryParameter("digits");
if (digits != null) {
info.digits = Integer.getInteger(digits);
}
// 'counter' is required if the type is 'hotp'
String counter = url.getQueryParameter("counter");
if (counter != null) {
info.counter = Long.getLong(counter);
} else if (info.type.equals("hotp")) {
throw new Exception("'counter' was not set which is required for 'hotp'");
}
return info; return info;
} }

View file

@ -26,7 +26,9 @@ public class HOTPTest {
new testVector(){{ Count = 9; OTP = "520489"; }}, new testVector(){{ Count = 9; OTP = "520489"; }},
}; };
private final byte[] _secret = new byte[] {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30}; private final byte[] _secret = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
};
@Test @Test
public void vectors_match() throws Exception { public void vectors_match() throws Exception {