2016-08-15 23:31:26 +02:00
|
|
|
package me.impy.aegis.crypto;
|
|
|
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
2016-08-16 00:08:01 +02:00
|
|
|
import java.io.Serializable;
|
|
|
|
|
2016-08-15 23:31:26 +02:00
|
|
|
import me.impy.aegis.encoding.Base32;
|
|
|
|
|
2016-08-16 00:08:01 +02:00
|
|
|
public class KeyInfo implements Serializable {
|
2016-08-15 23:55:03 +02:00
|
|
|
private String type;
|
|
|
|
private String label;
|
|
|
|
private byte[] secret;
|
|
|
|
private String issuer;
|
2016-08-16 12:28:27 +02:00
|
|
|
private String algorithm = "HmacSHA1";
|
|
|
|
private int digits = 6;
|
2016-08-15 23:55:03 +02:00
|
|
|
private long counter;
|
2016-08-16 12:28:27 +02:00
|
|
|
private int period = 30;
|
2016-08-15 23:55:03 +02:00
|
|
|
|
|
|
|
public String getType() {
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
public String getLabel() {
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
public byte[] getSecret() {
|
|
|
|
return secret;
|
|
|
|
}
|
|
|
|
public String getIssuer() {
|
|
|
|
return issuer;
|
|
|
|
}
|
2016-08-16 12:28:27 +02:00
|
|
|
public String getAlgorithm() {
|
|
|
|
return algorithm;
|
2016-08-15 23:55:03 +02:00
|
|
|
}
|
|
|
|
public int getDigits() {
|
|
|
|
return digits;
|
|
|
|
}
|
|
|
|
public long getCounter() {
|
|
|
|
return counter;
|
|
|
|
}
|
|
|
|
public int getPeriod() {
|
|
|
|
return period;
|
|
|
|
}
|
|
|
|
|
2016-08-16 12:28:27 +02:00
|
|
|
private KeyInfo() { }
|
2016-08-15 23:31:26 +02:00
|
|
|
|
|
|
|
public static KeyInfo FromURL(String s) throws Exception {
|
|
|
|
final Uri url = Uri.parse(s);
|
|
|
|
if (!url.getScheme().equals("otpauth")) {
|
|
|
|
throw new Exception("unsupported protocol");
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyInfo info = new KeyInfo();
|
2016-08-16 12:28:27 +02:00
|
|
|
|
|
|
|
// only 'totp' and 'hotp' are supported
|
2016-08-15 23:55:03 +02:00
|
|
|
info.type = url.getHost();
|
2016-08-16 12:28:27 +02:00
|
|
|
if (info.type.equals("totp") && info.type.equals("hotp")) {
|
|
|
|
throw new Exception("unsupported type");
|
|
|
|
}
|
2016-08-15 23:31:26 +02:00
|
|
|
|
2016-08-16 12:28:27 +02:00
|
|
|
// 'secret' is a required parameter
|
2016-08-15 23:31:26 +02:00
|
|
|
String secret = url.getQueryParameter("secret");
|
2016-08-16 12:28:27 +02:00
|
|
|
if (secret == null) {
|
|
|
|
throw new Exception("'secret' is not set");
|
|
|
|
}
|
2016-08-15 23:55:03 +02:00
|
|
|
info.secret = Base32.decode(secret);
|
2016-08-16 12:28:27 +02:00
|
|
|
|
|
|
|
// provider info used to disambiguate accounts
|
|
|
|
// these parameters are not required but I don't want them to be null either
|
|
|
|
String issuer = url.getQueryParameter("issuer");
|
2016-08-16 20:04:38 +02:00
|
|
|
String label = url.getPath();
|
2016-08-16 12:28:27 +02:00
|
|
|
info.issuer = issuer != null ? issuer : "";
|
2016-08-16 20:54:53 +02:00
|
|
|
info.label = label != null ? label.substring(1) : "";
|
2016-08-16 12:28:27 +02:00
|
|
|
|
|
|
|
// 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'");
|
|
|
|
}
|
2016-08-15 23:31:26 +02:00
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
}
|