Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c0068bea7 | ||
|
|
6dcde38667 | ||
|
|
ec1913c77a | ||
|
|
3e40cc9c8f | ||
|
|
40d1e22079 | ||
|
|
38bbb60414 | ||
|
|
98d39ee399 | ||
|
|
737b9da292 | ||
|
|
dea13f56f5 | ||
|
|
1f0c51b45b | ||
|
|
466e296988 | ||
|
|
fb17f48013 | ||
|
|
f203eab3ef | ||
|
|
0a6c89cd9d | ||
|
|
951241e95d | ||
|
|
4482a21a54 |
+4
-3
@@ -24,8 +24,8 @@ android {
|
||||
applicationId "${packageName}"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 44
|
||||
versionName "1.4.1"
|
||||
versionCode 45
|
||||
versionName "1.4.2"
|
||||
multiDexEnabled true
|
||||
buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\""
|
||||
buildConfigField "String", "GIT_BRANCH", "\"${getGitBranch()}\""
|
||||
@@ -152,7 +152,8 @@ dependencies {
|
||||
implementation 'com.nulab-inc:zxcvbn:1.3.1'
|
||||
implementation 'de.hdodenhof:circleimageview:3.1.0'
|
||||
implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
|
||||
implementation 'net.lingala.zip4j:zip4j:2.6.4'
|
||||
// NOTE: this is kept at an old version on purpose (something in newer versions breaks the Authenticator Plus importer)
|
||||
implementation 'net.lingala.zip4j:zip4j:2.6.0'
|
||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.68'
|
||||
|
||||
|
||||
@@ -31,6 +31,16 @@
|
||||
</head>
|
||||
<body>
|
||||
<div></div>
|
||||
<h3>Version 1.4.2</h3>
|
||||
<h4>Fixes</h4>
|
||||
<ul>
|
||||
<li>The app would crash if DocumentsUI is not present on the device</li>
|
||||
<li>The app would close when selecting an icon if auto lock on minimize was enabled</li>
|
||||
<li>Importing from Authy was flaky for entries that have an icon</li>
|
||||
<li>The dark theme was not properly applied to the QR code scanner view</li>
|
||||
<li>The app would crash on plain text export on some devices</li>
|
||||
<li>Importing from Authenticator Plus stopped working</li>
|
||||
</ul>
|
||||
<h3>Version 1.4.1</h3>
|
||||
<h4>Fixes</h4>
|
||||
<ul>
|
||||
|
||||
@@ -20,4 +20,10 @@ public class ThemeMap {
|
||||
Theme.DARK, R.style.AppTheme_Dark_NoActionBar,
|
||||
Theme.AMOLED, R.style.AppTheme_TrueBlack_NoActionBar
|
||||
);
|
||||
|
||||
public static final Map<Theme, Integer> FULLSCREEN = ImmutableMap.of(
|
||||
Theme.LIGHT, R.style.AppTheme_Fullscreen,
|
||||
Theme.DARK, R.style.AppTheme_Fullscreen_Dark,
|
||||
Theme.AMOLED, R.style.AppTheme_Fullscreen_TrueBlack
|
||||
);
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ public class AuthyImporter extends DatabaseImporter {
|
||||
authyEntryInfo.AccountType = JsonUtils.optString(entry, "accountType");
|
||||
authyEntryInfo.Name = entry.optString("name");
|
||||
|
||||
boolean isAuthy = !entry.optString("accountType", "authy").equals("authenticator");
|
||||
boolean isAuthy = entry.has("secretSeed");
|
||||
sanitizeEntryInfo(authyEntryInfo, isAuthy);
|
||||
|
||||
byte[] secret;
|
||||
@@ -246,7 +246,7 @@ public class AuthyImporter extends DatabaseImporter {
|
||||
}
|
||||
|
||||
int digits = entry.getInt("digits");
|
||||
OtpInfo info = new TotpInfo(secret, "SHA1", digits, digits == 7 ? 10 : 30);
|
||||
OtpInfo info = new TotpInfo(secret, "SHA1", digits, isAuthy ? 10 : 30);
|
||||
return new VaultEntry(info, authyEntryInfo.Name, authyEntryInfo.Issuer);
|
||||
} catch (OtpInfoException | JSONException | EncodingException e) {
|
||||
throw new DatabaseImporterEntryException(e, entry.toString());
|
||||
|
||||
@@ -65,7 +65,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||
public static GoogleAuthInfo parseUri(String s) throws GoogleAuthInfoException {
|
||||
Uri uri = Uri.parse(s);
|
||||
if (uri == null) {
|
||||
throw new GoogleAuthInfoException(String.format("Bad URI format: %s", s));
|
||||
throw new GoogleAuthInfoException(uri, String.format("Bad URI format: %s", s));
|
||||
}
|
||||
return GoogleAuthInfo.parseUri(uri);
|
||||
}
|
||||
@@ -73,27 +73,27 @@ public class GoogleAuthInfo implements Serializable {
|
||||
public static GoogleAuthInfo parseUri(Uri uri) throws GoogleAuthInfoException {
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme == null || !scheme.equals(SCHEME)) {
|
||||
throw new GoogleAuthInfoException("Unsupported protocol");
|
||||
throw new GoogleAuthInfoException(uri, String.format("Unsupported protocol: %s", scheme));
|
||||
}
|
||||
|
||||
// 'secret' is a required parameter
|
||||
String encodedSecret = uri.getQueryParameter("secret");
|
||||
if (encodedSecret == null) {
|
||||
throw new GoogleAuthInfoException("Parameter 'secret' is not present");
|
||||
throw new GoogleAuthInfoException(uri, "Parameter 'secret' is not present");
|
||||
}
|
||||
|
||||
byte[] secret;
|
||||
try {
|
||||
secret = parseSecret(encodedSecret);
|
||||
} catch (EncodingException e) {
|
||||
throw new GoogleAuthInfoException("Bad secret", e);
|
||||
throw new GoogleAuthInfoException(uri, "Bad secret", e);
|
||||
}
|
||||
|
||||
OtpInfo info;
|
||||
try {
|
||||
String type = uri.getHost();
|
||||
if (type == null) {
|
||||
throw new GoogleAuthInfoException(String.format("Host not present in URI: %s", uri.toString()));
|
||||
throw new GoogleAuthInfoException(uri, String.format("Host not present in URI: %s", uri.toString()));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
@@ -117,16 +117,16 @@ public class GoogleAuthInfo implements Serializable {
|
||||
HotpInfo hotpInfo = new HotpInfo(secret);
|
||||
String counter = uri.getQueryParameter("counter");
|
||||
if (counter == null) {
|
||||
throw new GoogleAuthInfoException("Parameter 'counter' is not present");
|
||||
throw new GoogleAuthInfoException(uri, "Parameter 'counter' is not present");
|
||||
}
|
||||
hotpInfo.setCounter(Long.parseLong(counter));
|
||||
info = hotpInfo;
|
||||
break;
|
||||
default:
|
||||
throw new GoogleAuthInfoException(String.format("Unsupported OTP type: %s", type));
|
||||
throw new GoogleAuthInfoException(uri, String.format("Unsupported OTP type: %s", type));
|
||||
}
|
||||
} catch (OtpInfoException | NumberFormatException e) {
|
||||
throw new GoogleAuthInfoException(e);
|
||||
throw new GoogleAuthInfoException(uri, e);
|
||||
}
|
||||
|
||||
// provider info used to disambiguate accounts
|
||||
@@ -166,7 +166,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||
info.setDigits(Integer.parseInt(digits));
|
||||
}
|
||||
} catch (OtpInfoException | NumberFormatException e) {
|
||||
throw new GoogleAuthInfoException(e);
|
||||
throw new GoogleAuthInfoException(uri, e);
|
||||
}
|
||||
|
||||
return new GoogleAuthInfo(info, accountName, issuer);
|
||||
@@ -183,7 +183,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||
public static Export parseExportUri(String s) throws GoogleAuthInfoException {
|
||||
Uri uri = Uri.parse(s);
|
||||
if (uri == null) {
|
||||
throw new GoogleAuthInfoException("Bad URI format");
|
||||
throw new GoogleAuthInfoException(uri, "Bad URI format");
|
||||
}
|
||||
return GoogleAuthInfo.parseExportUri(uri);
|
||||
}
|
||||
@@ -191,17 +191,17 @@ public class GoogleAuthInfo implements Serializable {
|
||||
public static Export parseExportUri(Uri uri) throws GoogleAuthInfoException {
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme == null || !scheme.equals(SCHEME_EXPORT)) {
|
||||
throw new GoogleAuthInfoException("Unsupported protocol");
|
||||
throw new GoogleAuthInfoException(uri, "Unsupported protocol");
|
||||
}
|
||||
|
||||
String host = uri.getHost();
|
||||
if (host == null || !host.equals("offline")) {
|
||||
throw new GoogleAuthInfoException("Unsupported host");
|
||||
throw new GoogleAuthInfoException(uri, "Unsupported host");
|
||||
}
|
||||
|
||||
String data = uri.getQueryParameter("data");
|
||||
if (data == null) {
|
||||
throw new GoogleAuthInfoException("Parameter 'data' is not set");
|
||||
throw new GoogleAuthInfoException(uri, "Parameter 'data' is not set");
|
||||
}
|
||||
|
||||
GoogleAuthProtos.MigrationPayload payload;
|
||||
@@ -209,7 +209,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||
byte[] bytes = Base64.decode(data);
|
||||
payload = GoogleAuthProtos.MigrationPayload.parseFrom(bytes);
|
||||
} catch (EncodingException | InvalidProtocolBufferException e) {
|
||||
throw new GoogleAuthInfoException(e);
|
||||
throw new GoogleAuthInfoException(uri, e);
|
||||
}
|
||||
|
||||
List<GoogleAuthInfo> infos = new ArrayList<>();
|
||||
@@ -227,7 +227,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||
digits = 8;
|
||||
break;
|
||||
default:
|
||||
throw new GoogleAuthInfoException(String.format("Unsupported digits: %d", params.getDigits().ordinal()));
|
||||
throw new GoogleAuthInfoException(uri, String.format("Unsupported digits: %d", params.getDigits().ordinal()));
|
||||
}
|
||||
|
||||
String algo;
|
||||
@@ -244,7 +244,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||
algo = "SHA512";
|
||||
break;
|
||||
default:
|
||||
throw new GoogleAuthInfoException(String.format("Unsupported hash algorithm: %d", params.getAlgorithm().ordinal()));
|
||||
throw new GoogleAuthInfoException(uri, String.format("Unsupported hash algorithm: %d", params.getAlgorithm().ordinal()));
|
||||
}
|
||||
|
||||
byte[] secret = params.getSecret().toByteArray();
|
||||
@@ -258,10 +258,10 @@ public class GoogleAuthInfo implements Serializable {
|
||||
otp = new HotpInfo(secret, algo, digits, params.getCounter());
|
||||
break;
|
||||
default:
|
||||
throw new GoogleAuthInfoException(String.format("Unsupported algorithm: %d", params.getType().ordinal()));
|
||||
throw new GoogleAuthInfoException(uri, String.format("Unsupported algorithm: %d", params.getType().ordinal()));
|
||||
}
|
||||
} catch (OtpInfoException e){
|
||||
throw new GoogleAuthInfoException(e);
|
||||
throw new GoogleAuthInfoException(uri, e);
|
||||
}
|
||||
|
||||
String name = params.getName();
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
package com.beemdevelopment.aegis.otp;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
public class GoogleAuthInfoException extends Exception {
|
||||
public GoogleAuthInfoException(Throwable cause) {
|
||||
private final Uri _uri;
|
||||
|
||||
public GoogleAuthInfoException(Uri uri, Throwable cause) {
|
||||
super(cause);
|
||||
_uri = uri;
|
||||
}
|
||||
|
||||
public GoogleAuthInfoException(String message) {
|
||||
public GoogleAuthInfoException(Uri uri, String message) {
|
||||
super(message);
|
||||
_uri = uri;
|
||||
}
|
||||
|
||||
public GoogleAuthInfoException(String message, Throwable cause) {
|
||||
public GoogleAuthInfoException(Uri uri, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
_uri = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether the scheme of the URI is phonefactor://.
|
||||
*/
|
||||
public boolean isPhoneFactor() {
|
||||
return _uri != null && _uri.getScheme() != null && _uri.getScheme().equals("phonefactor");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -19,6 +33,7 @@ public class GoogleAuthInfoException extends Exception {
|
||||
if (cause == null) {
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
return String.format("%s (%s)", super.getMessage(), cause.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.beemdevelopment.aegis.ui;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.beemdevelopment.aegis.AegisApplication;
|
||||
@@ -55,12 +58,32 @@ public abstract class AegisActivity extends AppCompatActivity implements AegisAp
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
_app.setBlockAutoLock(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode, Bundle bundle) {
|
||||
if (isAutoLockBypassedForAction(intent.getAction())) {
|
||||
_app.setBlockAutoLock(true);
|
||||
}
|
||||
|
||||
try {
|
||||
super.startActivityForResult(intent, requestCode, bundle);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (isDocsAction(intent.getAction())) {
|
||||
Dialogs.showErrorDialog(this, R.string.documentsui_error, e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocked(boolean userInitiated) {
|
||||
setResult(RESULT_CANCELED, null);
|
||||
@@ -135,4 +158,17 @@ public abstract class AegisActivity extends AppCompatActivity implements AegisAp
|
||||
protected boolean isOrphan() {
|
||||
return !(this instanceof MainActivity) && !(this instanceof AuthActivity) && !(this instanceof IntroActivity) && _app.isVaultLocked();
|
||||
}
|
||||
|
||||
private static boolean isDocsAction(@Nullable String action) {
|
||||
return action != null && (action.equals(Intent.ACTION_GET_CONTENT)
|
||||
|| action.equals(Intent.ACTION_CREATE_DOCUMENT)
|
||||
|| action.equals(Intent.ACTION_OPEN_DOCUMENT)
|
||||
|| action.equals(Intent.ACTION_OPEN_DOCUMENT_TREE));
|
||||
}
|
||||
|
||||
private static boolean isAutoLockBypassedForAction(@Nullable String action) {
|
||||
return isDocsAction(action) || (action != null && (action.equals(Intent.ACTION_PICK)
|
||||
|| action.equals(Intent.ACTION_SEND)
|
||||
|| action.equals(Intent.ACTION_CHOOSER)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,8 +407,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
||||
}
|
||||
|
||||
private void startScanImageActivity() {
|
||||
_app.setBlockAutoLock(true);
|
||||
|
||||
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
|
||||
galleryIntent.setDataAndType(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
|
||||
|
||||
|
||||
@@ -191,8 +191,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
|
||||
_app.setBlockAutoLock(true);
|
||||
startActivityForResult(intent, CODE_IMPORT);
|
||||
});
|
||||
return true;
|
||||
@@ -737,8 +735,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.setType(getExportMimeType(requestCode))
|
||||
.putExtra(Intent.EXTRA_TITLE, fileInfo.toString());
|
||||
|
||||
_app.setBlockAutoLock(true);
|
||||
startActivityForResult(intent, requestCode);
|
||||
});
|
||||
|
||||
@@ -775,8 +771,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||
.setType(getExportMimeType(requestCode))
|
||||
.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
Intent chooser = Intent.createChooser(intent, getString(R.string.pref_export_summary));
|
||||
|
||||
_app.setBlockAutoLock(true);
|
||||
startActivity(chooser);
|
||||
});
|
||||
});
|
||||
@@ -1019,8 +1013,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
|
||||
_app.setBlockAutoLock(true);
|
||||
startActivityForResult(intent, CODE_BACKUPS);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.camera.view.PreviewView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ThemeMap;
|
||||
import com.beemdevelopment.aegis.helpers.QrCodeAnalyzer;
|
||||
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
|
||||
import com.beemdevelopment.aegis.otp.GoogleAuthInfoException;
|
||||
@@ -77,7 +78,7 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
|
||||
|
||||
@Override
|
||||
protected void onSetTheme() {
|
||||
setTheme(R.style.AppTheme_Fullscreen);
|
||||
setTheme(ThemeMap.FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +157,9 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
|
||||
}
|
||||
} catch (GoogleAuthInfoException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(this, R.string.read_qr_error, e, ((dialog, which) -> bindPreview(_cameraProvider)));
|
||||
Dialogs.showErrorDialog(this,
|
||||
e.isPhoneFactor() ? R.string.read_qr_error_phonefactor : R.string.read_qr_error,
|
||||
e, ((dialog, which) -> bindPreview(_cameraProvider)));
|
||||
_cameraProvider.unbindAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ public class VaultManager {
|
||||
* Google Authenticator URI's and writing it to the given OutputStream.
|
||||
*/
|
||||
public void exportGoogleUris(OutputStream outStream) throws VaultManagerException {
|
||||
try (PrintStream stream = new PrintStream(outStream, false, StandardCharsets.UTF_8.toString())) {
|
||||
try (PrintStream stream = new PrintStream(outStream, false, StandardCharsets.UTF_8.name())) {
|
||||
for (VaultEntry entry : getEntries()) {
|
||||
GoogleAuthInfo info = new GoogleAuthInfo(entry.getInfo(), entry.getName(), entry.getIssuer());
|
||||
stream.println(info.getUri().toString());
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
<string name="disable_encryption_error">An error occurred while disabling encryption</string>
|
||||
<string name="backup_successful">The backup was scheduled successfully</string>
|
||||
<string name="backup_error">An error occurred while trying to create a backup</string>
|
||||
<string name="documentsui_error">DocumentsUI appears to be missing from your device. This is an important system component necessary for the selection and creation of documents. If you used a tool to "debloat" your device, you may have accidentally deleted it and will have to reinstall it.</string>
|
||||
<string name="permission_denied">Permission denied</string>
|
||||
<string name="andotp_new_format">New format (v0.6.3 or newer) </string>
|
||||
<string name="andotp_old_format">Old format (v0.6.2 or older) </string>
|
||||
@@ -192,6 +193,7 @@
|
||||
<string name="encryption_enable_biometrics_error">An error occurred while trying to enable biometric unlock. Some devices have poor implementations of biometric authentication and it is likely that yours is one of them. Consider switching to a password-only configuration instead.</string>
|
||||
<string name="no_cameras_available">No cameras available</string>
|
||||
<string name="read_qr_error">An error occurred while trying to read the QR code</string>
|
||||
<string name="read_qr_error_phonefactor">Aegis is not compatible with Microsoft\'s proprietary 2FA algorithm. Please make sure to select \"Setup application without notifications\" when configuring 2FA in Office 365.</string>
|
||||
<string name="authentication_method_raw">Raw</string>
|
||||
<string name="unlocking_vault">Unlocking the vault</string>
|
||||
<string name="unlocking_vault_repair">Unlocking the vault (repairing)</string>
|
||||
|
||||
@@ -126,6 +126,30 @@
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Fullscreen.Dark" parent="AppTheme.Dark">
|
||||
<!-- AppTheme.TransparentActionBar-->
|
||||
<item name="android:actionBarStyle">@style/ThemeActionBar</item>
|
||||
<item name="android:windowActionBarOverlay">true</item>
|
||||
<item name="actionBarStyle">@style/ThemeActionBar</item>
|
||||
<item name="windowActionBarOverlay">true</item>
|
||||
|
||||
<!-- AppTheme.Fullscreen-->
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Fullscreen.TrueBlack" parent="AppTheme.TrueBlack">
|
||||
<!-- AppTheme.TransparentActionBar-->
|
||||
<item name="android:actionBarStyle">@style/ThemeActionBar</item>
|
||||
<item name="android:windowActionBarOverlay">true</item>
|
||||
<item name="actionBarStyle">@style/ThemeActionBar</item>
|
||||
<item name="windowActionBarOverlay">true</item>
|
||||
|
||||
<!-- AppTheme.Fullscreen-->
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Dark" parent="Theme.AppCompat">
|
||||
<item name="primaryText">@color/primary_text_dark</item>
|
||||
<item name="secondaryText">@color/secondary_text_dark</item>
|
||||
|
||||
@@ -1,36 +1,52 @@
|
||||
Aegis Authenticator is a free, secure and open source app to manage your
|
||||
2-step verification tokens for your online services.
|
||||
|
||||
<b>Encryption</b>
|
||||
All of your one-time passwords are stored in a vault. If you choose
|
||||
to set a password, which is highly recommended, the vault will be encrypted
|
||||
using AES-256. If someone with malicious intent gets a hold of the vault file,
|
||||
it’s impossible for them to retrieve the contents without knowing the password.
|
||||
|
||||
<b>Biometric unlock</b>
|
||||
Entering your password each time you need access to a
|
||||
one-time password can be cumbersome. Fortunately, you can also enable
|
||||
biometric unlock if your device has a biometrics sensor.
|
||||
Aegis Authenticator is a free, secure and open source app to manage your 2-step verification tokens for your online services.
|
||||
|
||||
<b>Compatibility</b>
|
||||
Aegis supports the HOTP and TOTP algorithms. These two algorithms are
|
||||
industry-standard and widely supported, making Aegis compatible with
|
||||
thousands of services. Some examples are: Google, GitHub, Dropbox, Facebook
|
||||
and Instagram.
|
||||
Aegis supports the HOTP and TOTP algorithms. These two algorithms are industry-standard and widely supported, making Aegis compatible with thousands of services. Any web service that supports Google Authenticator will also work with Aegis Authenticator.
|
||||
|
||||
It is also compatible with Google Authenticator. Any website that shows a QR
|
||||
code for Google Authenticator also works with Aegis.
|
||||
<b>Encryption and biometric unlock</b>
|
||||
All of your one-time passwords are stored in a vault. If you choose to set a password (highly recommended), the vault will be encrypted using strong cryptography. If someone with malicious intent gets a hold of the vault file, it’s impossible for them to retrieve the contents without knowing the password. Entering your password each time you need access to a one-time password can be cumbersome. Fortunately, you can also enable biometric unlock if your device has a biometrics sensor (i.e. fingerprint or face unlock).
|
||||
|
||||
<b>Groups</b>
|
||||
Have a lot of one-time passwords? Add them to custom groups for easier
|
||||
access. Personal, Work and Social can each get their own group.
|
||||
<b>Organization</b>
|
||||
Over time, you'll likely accumulate tens of entries in your vault. Aegis Authenticator has lots of organization options to make finding the one you need at a particular moment easier. Set a custom icon for an entry to make it easier to find. Search by account name or service name. Have a lot of one-time passwords? Add them to custom groups for easier access. Personal, Work and Social can each get their own group.
|
||||
|
||||
<b>Backups</b>
|
||||
To make sure you will never lose access to your online accounts Aegis
|
||||
Authenticator supports exporting your vault which you can import onto a new
|
||||
device. Aegis Authenticator also allows you to import AndOTP and FreeOTP
|
||||
databases so switching to Aegis is made easier for you.
|
||||
To make sure you will never lose access to your online accounts, Aegis Authenticator can create automatic backups of the vault to a location of your choosing. If your cloud provider supports the Storage Access Framework of Android (like Nextcloud does), it can even create automatic backups to the cloud. Creating manual exports of the vault is also supported.
|
||||
|
||||
<b>Making the switch</b>
|
||||
To make the switch easier, Aegis Authenticator can import the entries of lots of other authenticators, including: Authenticator Plus, Authy, andOTP, FreeOTP, FreeOTP+, Google Authenticator, Microsoft Authenticator, Steam, TOTP Authenticator and WinAuth (root access is required for the apps that don't have an option to export).
|
||||
|
||||
<b>Feature overview</b>
|
||||
<ul>
|
||||
<li>Free and open source</li>
|
||||
<li>Secure
|
||||
<ul>
|
||||
<li>Encrypted, can be unlocked with a password or biometrics</li>
|
||||
<li>Screen capture prevention</li>
|
||||
<li>Tap to reveal</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Compatible with Google Authenticator</li>
|
||||
<li>Supports industry standard algorithms: HOTP and TOTP</li>
|
||||
<li>Lots of ways to add new entries
|
||||
<ul>
|
||||
<li>Scan a QR code or an image of one</li>
|
||||
<li>Enter details manually</li>
|
||||
<li>Import from other popular authenticator apps</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Organization
|
||||
<ul>
|
||||
<li>Alphabetic/custom sorting</li>
|
||||
<li>Custom or automatically generated icons</li>
|
||||
<li>Group entries together</li>
|
||||
<li>Advanced entry editing</li>
|
||||
<li>Search by name/issuer</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Material design with multiple themes: Light, Dark, AMOLED</li>
|
||||
<li>Export (plaintext or encrypted)</li>
|
||||
<li>Automatic backups of the vault to a location of your choosing</li>
|
||||
</ul>
|
||||
|
||||
<b>Open source and license</b>
|
||||
Aegis Authenticator is open source (licensed under GPL v3) and the source code
|
||||
can be found here: http://github.com/beemdevelopment/Aegis
|
||||
Aegis Authenticator is open source (licensed under GPL v3) and the source code can be found here: http://github.com/beemdevelopment/Aegis
|
||||
|
||||
Reference in New Issue
Block a user