mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-20 22:09:12 +00:00
Add support for Steam OTP
This commit is contained in:
parent
a46640f43d
commit
1dd5f893da
18 changed files with 234 additions and 56 deletions
|
@ -12,7 +12,7 @@ public class HOTP {
|
|||
private HOTP() {
|
||||
}
|
||||
|
||||
public static String generateOTP(byte[] secret, String algo, int digits, long counter)
|
||||
public static OTP generateOTP(byte[] secret, String algo, int digits, long counter)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
SecretKeySpec key = new SecretKeySpec(secret, "RAW");
|
||||
|
||||
|
@ -30,18 +30,11 @@ public class HOTP {
|
|||
// truncate hash to get the HTOP value
|
||||
// http://tools.ietf.org/html/rfc4226#section-5.4
|
||||
int offset = hash[hash.length - 1] & 0xf;
|
||||
long bin = ((hash[offset] & 0x7f) << 24)
|
||||
int otp = ((hash[offset] & 0x7f) << 24)
|
||||
| ((hash[offset + 1] & 0xff) << 16)
|
||||
| ((hash[offset + 2] & 0xff) << 8)
|
||||
| (hash[offset + 3] & 0xff);
|
||||
long otp = bin % (long) Math.pow(10, digits);
|
||||
|
||||
// prepend zeroes if needed
|
||||
StringBuilder res = new StringBuilder(Long.toString(otp));
|
||||
while (res.length() < digits) {
|
||||
res.insert(0, "0");
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
return new OTP(otp, digits);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package com.beemdevelopment.aegis.crypto.otp;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class OTP {
|
||||
private static final String STEAM_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
|
||||
|
||||
private int _code;
|
||||
private int _digits;
|
||||
|
||||
public OTP(int code, int digits) {
|
||||
_code = code;
|
||||
_digits = digits;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return _code;
|
||||
}
|
||||
|
||||
public int getDigits() {
|
||||
return _digits;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
int code = _code % (int) Math.pow(10, _digits);
|
||||
|
||||
// prepend zeroes if needed
|
||||
StringBuilder res = new StringBuilder(Long.toString(code));
|
||||
while (res.length() < _digits) {
|
||||
res.insert(0, "0");
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
public String toSteamString() {
|
||||
int code = _code;
|
||||
StringBuilder res = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < _digits; i++) {
|
||||
char c = STEAM_ALPHABET.charAt(code % STEAM_ALPHABET.length());
|
||||
res.append(c);
|
||||
code /= STEAM_ALPHABET.length();
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
}
|
|
@ -8,13 +8,13 @@ public class TOTP {
|
|||
private TOTP() {
|
||||
}
|
||||
|
||||
public static String generateOTP(byte[] secret, String algo, int digits, long period, long seconds)
|
||||
public static OTP generateOTP(byte[] secret, String algo, int digits, long period, long seconds)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
long counter = (long) Math.floor((double) seconds / period);
|
||||
return HOTP.generateOTP(secret, algo, digits, counter);
|
||||
}
|
||||
|
||||
public static String generateOTP(byte[] secret, String algo, int digits, long period)
|
||||
public static OTP generateOTP(byte[] secret, String algo, int digits, long period)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
return generateOTP(secret, algo, digits, period, System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ public abstract class DatabaseAppImporter implements DatabaseImporter {
|
|||
// note: keep this list sorted alphabetically
|
||||
LinkedHashMap<String, Class<? extends DatabaseAppImporter>> importers = new LinkedHashMap<>();
|
||||
importers.put("Google Authenticator", GoogleAuthAppImporter.class);
|
||||
importers.put("Steam", SteamAppImporter.class);
|
||||
_importers = Collections.unmodifiableMap(importers);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package com.beemdevelopment.aegis.importers;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
||||
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||
import com.beemdevelopment.aegis.encoding.Base64;
|
||||
import com.beemdevelopment.aegis.encoding.Base64Exception;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfoException;
|
||||
import com.beemdevelopment.aegis.otp.SteamInfo;
|
||||
import com.beemdevelopment.aegis.util.ByteInputStream;
|
||||
import com.topjohnwu.superuser.io.SuFile;
|
||||
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SteamAppImporter extends DatabaseAppImporter {
|
||||
@SuppressLint("SdCardPath")
|
||||
private static final String _path = "/data/data/com.valvesoftware.android.steam.community/files";
|
||||
|
||||
private List<JSONObject> _objs = new ArrayList<>();
|
||||
|
||||
public SteamAppImporter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws DatabaseImporterException {
|
||||
SuFile dir = new SuFile(_path);
|
||||
for (SuFile file : dir.listFiles((d, name) -> name.startsWith("Steamguard-"))) {
|
||||
try (SuFileInputStream in = new SuFileInputStream(file)) {
|
||||
try (ByteInputStream stream = ByteInputStream.create(in)) {
|
||||
JSONObject obj = new JSONObject(new String(stream.getBytes(), StandardCharsets.UTF_8));
|
||||
_objs.add(obj);
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseImporterResult convert() {
|
||||
DatabaseImporterResult result = new DatabaseImporterResult();
|
||||
|
||||
for (JSONObject obj : _objs) {
|
||||
try {
|
||||
DatabaseEntry entry = convertEntry(obj);
|
||||
result.addEntry(entry);
|
||||
} catch (DatabaseImporterEntryException e) {
|
||||
result.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public DatabaseEntry convertEntry(JSONObject obj) throws DatabaseImporterEntryException{
|
||||
try {
|
||||
byte[] secret = Base64.decode(obj.getString("shared_secret"));
|
||||
SteamInfo info = new SteamInfo(secret);
|
||||
|
||||
String account = obj.getString("account_name");
|
||||
return new DatabaseEntry(info, account, "Steam");
|
||||
} catch (JSONException | Base64Exception | OtpInfoException e) {
|
||||
throw new DatabaseImporterEntryException(e, obj.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncrypted() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.beemdevelopment.aegis.otp;
|
||||
|
||||
import com.beemdevelopment.aegis.crypto.otp.HOTP;
|
||||
import com.beemdevelopment.aegis.crypto.otp.OTP;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -9,6 +10,8 @@ import java.security.InvalidKeyException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class HotpInfo extends OtpInfo {
|
||||
public static final String ID = "hotp";
|
||||
|
||||
private long _counter;
|
||||
|
||||
public HotpInfo(byte[] secret, long counter) throws OtpInfoException {
|
||||
|
@ -28,7 +31,8 @@ public class HotpInfo extends OtpInfo {
|
|||
@Override
|
||||
public String getOtp() {
|
||||
try {
|
||||
return HOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getCounter());
|
||||
OTP otp = HOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getCounter());
|
||||
return otp.toString();
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
@ -36,7 +40,7 @@ public class HotpInfo extends OtpInfo {
|
|||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "hotp";
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -98,13 +98,14 @@ public abstract class OtpInfo implements Serializable {
|
|||
int digits = obj.getInt("digits");
|
||||
|
||||
switch (type) {
|
||||
case "totp":
|
||||
int period = obj.getInt("period");
|
||||
info = new TotpInfo(secret, algo, digits, period);
|
||||
case TotpInfo.ID:
|
||||
info = new TotpInfo(secret, algo, digits, obj.getInt("period"));
|
||||
break;
|
||||
case "hotp":
|
||||
long counter = obj.getLong("counter");
|
||||
info = new HotpInfo(secret, algo, digits, counter);
|
||||
case SteamInfo.ID:
|
||||
info = new SteamInfo(secret, algo, digits, obj.getInt("period"));
|
||||
break;
|
||||
case HotpInfo.ID:
|
||||
info = new HotpInfo(secret, algo, digits, obj.getLong("counter"));
|
||||
break;
|
||||
default:
|
||||
throw new OtpInfoException("unsupported otp type: " + type);
|
||||
|
@ -126,7 +127,8 @@ public abstract class OtpInfo implements Serializable {
|
|||
}
|
||||
|
||||
OtpInfo info = (OtpInfo) o;
|
||||
return Arrays.equals(getSecret(), info.getSecret())
|
||||
return getType().equals(info.getType())
|
||||
&& Arrays.equals(getSecret(), info.getSecret())
|
||||
&& getAlgorithm(false).equals(info.getAlgorithm(false))
|
||||
&& getDigits() == info.getDigits();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package com.beemdevelopment.aegis.otp;
|
||||
|
||||
import com.beemdevelopment.aegis.crypto.otp.OTP;
|
||||
import com.beemdevelopment.aegis.crypto.otp.TOTP;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class SteamInfo extends TotpInfo {
|
||||
public static final String ID = "steam";
|
||||
|
||||
public SteamInfo(byte[] secret) throws OtpInfoException {
|
||||
super(secret, "SHA1", 5, 30);
|
||||
}
|
||||
|
||||
public SteamInfo(byte[] secret, String algorithm, int digits, int period) throws OtpInfoException {
|
||||
super(secret, algorithm, digits, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOtp() {
|
||||
try {
|
||||
OTP otp = TOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getPeriod());
|
||||
return otp.toSteamString();
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return ID;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.beemdevelopment.aegis.otp;
|
||||
|
||||
import com.beemdevelopment.aegis.crypto.otp.OTP;
|
||||
import com.beemdevelopment.aegis.crypto.otp.TOTP;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
@ -9,6 +10,8 @@ import java.security.InvalidKeyException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class TotpInfo extends OtpInfo {
|
||||
public static final String ID = "totp";
|
||||
|
||||
private int _period;
|
||||
|
||||
public TotpInfo(byte[] secret) throws OtpInfoException {
|
||||
|
@ -24,7 +27,8 @@ public class TotpInfo extends OtpInfo {
|
|||
@Override
|
||||
public String getOtp() {
|
||||
try {
|
||||
return TOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getPeriod());
|
||||
OTP otp = TOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getPeriod());
|
||||
return otp.toString();
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -32,7 +36,7 @@ public class TotpInfo extends OtpInfo {
|
|||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "totp";
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,7 +30,6 @@ import android.widget.TableRow;
|
|||
|
||||
import com.amulyakhare.textdrawable.TextDrawable;
|
||||
import com.avito.android.krop.KropView;
|
||||
import com.beemdevelopment.aegis.Theme;
|
||||
import com.beemdevelopment.aegis.encoding.Base32;
|
||||
import com.beemdevelopment.aegis.encoding.Base32Exception;
|
||||
import com.beemdevelopment.aegis.helpers.EditTextHelper;
|
||||
|
@ -39,6 +38,7 @@ import com.beemdevelopment.aegis.helpers.TextDrawableHelper;
|
|||
import com.beemdevelopment.aegis.otp.HotpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfoException;
|
||||
import com.beemdevelopment.aegis.otp.SteamInfo;
|
||||
import com.beemdevelopment.aegis.otp.TotpInfo;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -167,7 +167,7 @@ public class EditEntryActivity extends AegisActivity {
|
|||
}
|
||||
|
||||
String type = _origEntry.getInfo().getType();
|
||||
_spinnerType.setSelection(getStringResourceIndex(R.array.otp_types_array, type.toUpperCase()), false);
|
||||
_spinnerType.setSelection(getStringResourceIndex(R.array.otp_types_array, type), false);
|
||||
|
||||
String algo = _origEntry.getInfo().getAlgorithm(false);
|
||||
_spinnerAlgo.setSelection(getStringResourceIndex(R.array.otp_algo_array, algo), false);
|
||||
|
@ -190,11 +190,12 @@ public class EditEntryActivity extends AegisActivity {
|
|||
String type = _spinnerType.getSelectedItem().toString();
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case "totp":
|
||||
case TotpInfo.ID:
|
||||
case SteamInfo.ID:
|
||||
_rowCounter.setVisibility(View.GONE);
|
||||
_rowPeriod.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
case "hotp":
|
||||
case HotpInfo.ID:
|
||||
_rowPeriod.setVisibility(View.GONE);
|
||||
_rowCounter.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
|
@ -418,6 +419,14 @@ public class EditEntryActivity extends AegisActivity {
|
|||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
private int parsePeriod() throws ParseException {
|
||||
try {
|
||||
return Integer.parseInt(_textPeriod.getText().toString());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParseException("Period is not an integer.");
|
||||
}
|
||||
}
|
||||
|
||||
private DatabaseEntry parseEntry() throws ParseException {
|
||||
if (_textSecret.length() == 0) {
|
||||
throw new ParseException("Secret is a required field.");
|
||||
|
@ -444,16 +453,13 @@ public class EditEntryActivity extends AegisActivity {
|
|||
OtpInfo info;
|
||||
try {
|
||||
switch (type.toLowerCase()) {
|
||||
case "totp":
|
||||
int period;
|
||||
try {
|
||||
period = Integer.parseInt(_textPeriod.getText().toString());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParseException("Period is not an integer.");
|
||||
}
|
||||
info = new TotpInfo(secret, algo, digits, period);
|
||||
case TotpInfo.ID:
|
||||
info = new TotpInfo(secret, algo, digits, parsePeriod());
|
||||
break;
|
||||
case "hotp":
|
||||
case SteamInfo.ID:
|
||||
info = new SteamInfo(secret, algo, digits, parsePeriod());
|
||||
break;
|
||||
case HotpInfo.ID:
|
||||
long counter;
|
||||
try {
|
||||
counter = Long.parseLong(_textCounter.getText().toString());
|
||||
|
@ -547,7 +553,7 @@ public class EditEntryActivity extends AegisActivity {
|
|||
private int getStringResourceIndex(@ArrayRes int id, String string) {
|
||||
String[] res = getResources().getStringArray(id);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i].equals(string)) {
|
||||
if (res[i].equalsIgnoreCase(string)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -590,8 +590,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
super.onAnimationEnd(animation);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
else if (dy < 0 && _fabMenu.getVisibility() != View.VISIBLE && !isAnimating) {
|
||||
} else if (dy < 0 && _fabMenu.getVisibility() != View.VISIBLE && !isAnimating) {
|
||||
_fabMenu.setVisibility(View.VISIBLE);
|
||||
_fabMenu.animate()
|
||||
.translationY(0)
|
||||
|
|
|
@ -515,11 +515,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
|
||||
_result.putExtra("needsRecreate", true);
|
||||
Snackbar bar = Snackbar.make(getView(), String.format(Locale.getDefault(), getString(R.string.imported_entries_count), entries.size(), errors.size()), Snackbar.LENGTH_LONG);
|
||||
if (errors.size() == 0) {
|
||||
bar.setAction(R.string.details, v -> {
|
||||
|
||||
});
|
||||
}
|
||||
bar.show();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ import com.amulyakhare.textdrawable.TextDrawable;
|
|||
import com.beemdevelopment.aegis.helpers.TextDrawableHelper;
|
||||
import com.beemdevelopment.aegis.helpers.UiRefresher;
|
||||
import com.beemdevelopment.aegis.otp.HotpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfo;
|
||||
import com.beemdevelopment.aegis.otp.SteamInfo;
|
||||
import com.beemdevelopment.aegis.otp.TotpInfo;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
|
@ -139,10 +141,18 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
|
||||
private void updateCode() {
|
||||
String otp = _entry.getInfo().getOtp();
|
||||
String text = otp.substring(0, (otp.length() / 2)
|
||||
+ (otp.length() % 2)) + " "
|
||||
+ otp.substring(otp.length() / 2);
|
||||
OtpInfo info = _entry.getInfo();
|
||||
|
||||
String text;
|
||||
if (info instanceof SteamInfo) {
|
||||
text = info.getOtp();
|
||||
} else {
|
||||
String otp = info.getOtp();
|
||||
text = otp.substring(0, (otp.length() / 2)
|
||||
+ (otp.length() % 2)) + " "
|
||||
+ otp.substring(otp.length() / 2);
|
||||
}
|
||||
|
||||
_profileCode.setText(text);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.beemdevelopment.aegis.ui.views;
|
|||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -36,8 +35,6 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
|
||||
private UiRefresher _refresher;
|
||||
|
||||
private RecyclerView.OnScrollChangeListener _onScrollListener;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
<string name="file_not_found">Ошибка: файл не найден</string>
|
||||
<string name="reading_file_error">Произошла ошибка при попытке прочитать файл</string>
|
||||
<string name="root_error">Ошибка: невозможно получить root-доступ</string>
|
||||
<string name="imported_entries_count">Импортироаано %d записей</string>
|
||||
<string name="imported_entries_count">Импортироаано %d записей. %d ошибки.</string>
|
||||
<string name="exporting_database_error">Произошла ошибка при попытке экспорта базы данных</string>
|
||||
<string name="export_database_location">База данных была экспортирована в:</string>
|
||||
<string name="export_warning">Это действие экспортирует базу данных из личного хранилища Aegis.</string>
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
<string-array name="otp_types_array">
|
||||
<item>TOTP</item>
|
||||
<item>HOTP</item>
|
||||
<item>Steam</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="otp_algo_array">
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.security.InvalidKeyException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.beemdevelopment.aegis.crypto.otp.HOTP;
|
||||
import com.beemdevelopment.aegis.crypto.otp.OTP;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
@ -27,8 +28,8 @@ public class HOTPTest {
|
|||
@Test
|
||||
public void vectorsMatch() throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
for (int i = 0; i < _vectors.length; i++) {
|
||||
String otp = HOTP.generateOTP(_secret, "HmacSHA1", 6, i);
|
||||
assertEquals(_vectors[i], otp);
|
||||
OTP otp = HOTP.generateOTP(_secret, "HmacSHA1", 6, i);
|
||||
assertEquals(_vectors[i], otp.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.beemdevelopment.aegis;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.beemdevelopment.aegis.crypto.otp.OTP;
|
||||
import com.beemdevelopment.aegis.crypto.otp.TOTP;
|
||||
import com.beemdevelopment.aegis.encoding.Hex;
|
||||
import com.beemdevelopment.aegis.encoding.HexException;
|
||||
|
@ -87,8 +88,8 @@ public class TOTPTest {
|
|||
return;
|
||||
}
|
||||
|
||||
String otp = TOTP.generateOTP(seed, vector.Algo, 8, 30, vector.Time);
|
||||
assertEquals(vector.OTP, otp);
|
||||
OTP otp = TOTP.generateOTP(seed, vector.Algo, 8, 30, vector.Time);
|
||||
assertEquals(vector.OTP, otp.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue