Merge pull request #655 from alexbakker/phonefactor-error

Show a clearer error message when encountering phonefactor:// URI's
This commit is contained in:
Michael Schättgen 2021-01-13 20:24:26 +01:00 committed by GitHub
commit 3e40cc9c8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 22 deletions

View file

@ -65,7 +65,7 @@ public class GoogleAuthInfo implements Serializable {
public static GoogleAuthInfo parseUri(String s) throws GoogleAuthInfoException { public static GoogleAuthInfo parseUri(String s) throws GoogleAuthInfoException {
Uri uri = Uri.parse(s); Uri uri = Uri.parse(s);
if (uri == null) { 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); return GoogleAuthInfo.parseUri(uri);
} }
@ -73,27 +73,27 @@ public class GoogleAuthInfo implements Serializable {
public static GoogleAuthInfo parseUri(Uri uri) throws GoogleAuthInfoException { public static GoogleAuthInfo parseUri(Uri uri) throws GoogleAuthInfoException {
String scheme = uri.getScheme(); String scheme = uri.getScheme();
if (scheme == null || !scheme.equals(SCHEME)) { 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 // 'secret' is a required parameter
String encodedSecret = uri.getQueryParameter("secret"); String encodedSecret = uri.getQueryParameter("secret");
if (encodedSecret == null) { if (encodedSecret == null) {
throw new GoogleAuthInfoException("Parameter 'secret' is not present"); throw new GoogleAuthInfoException(uri, "Parameter 'secret' is not present");
} }
byte[] secret; byte[] secret;
try { try {
secret = parseSecret(encodedSecret); secret = parseSecret(encodedSecret);
} catch (EncodingException e) { } catch (EncodingException e) {
throw new GoogleAuthInfoException("Bad secret", e); throw new GoogleAuthInfoException(uri, "Bad secret", e);
} }
OtpInfo info; OtpInfo info;
try { try {
String type = uri.getHost(); String type = uri.getHost();
if (type == null) { 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) { switch (type) {
@ -117,16 +117,16 @@ public class GoogleAuthInfo implements Serializable {
HotpInfo hotpInfo = new HotpInfo(secret); HotpInfo hotpInfo = new HotpInfo(secret);
String counter = uri.getQueryParameter("counter"); String counter = uri.getQueryParameter("counter");
if (counter == null) { 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)); hotpInfo.setCounter(Long.parseLong(counter));
info = hotpInfo; info = hotpInfo;
break; break;
default: 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) { } catch (OtpInfoException | NumberFormatException e) {
throw new GoogleAuthInfoException(e); throw new GoogleAuthInfoException(uri, e);
} }
// provider info used to disambiguate accounts // provider info used to disambiguate accounts
@ -166,7 +166,7 @@ public class GoogleAuthInfo implements Serializable {
info.setDigits(Integer.parseInt(digits)); info.setDigits(Integer.parseInt(digits));
} }
} catch (OtpInfoException | NumberFormatException e) { } catch (OtpInfoException | NumberFormatException e) {
throw new GoogleAuthInfoException(e); throw new GoogleAuthInfoException(uri, e);
} }
return new GoogleAuthInfo(info, accountName, issuer); return new GoogleAuthInfo(info, accountName, issuer);
@ -183,7 +183,7 @@ public class GoogleAuthInfo implements Serializable {
public static Export parseExportUri(String s) throws GoogleAuthInfoException { public static Export parseExportUri(String s) throws GoogleAuthInfoException {
Uri uri = Uri.parse(s); Uri uri = Uri.parse(s);
if (uri == null) { if (uri == null) {
throw new GoogleAuthInfoException("Bad URI format"); throw new GoogleAuthInfoException(uri, "Bad URI format");
} }
return GoogleAuthInfo.parseExportUri(uri); return GoogleAuthInfo.parseExportUri(uri);
} }
@ -191,17 +191,17 @@ public class GoogleAuthInfo implements Serializable {
public static Export parseExportUri(Uri uri) throws GoogleAuthInfoException { public static Export parseExportUri(Uri uri) throws GoogleAuthInfoException {
String scheme = uri.getScheme(); String scheme = uri.getScheme();
if (scheme == null || !scheme.equals(SCHEME_EXPORT)) { if (scheme == null || !scheme.equals(SCHEME_EXPORT)) {
throw new GoogleAuthInfoException("Unsupported protocol"); throw new GoogleAuthInfoException(uri, "Unsupported protocol");
} }
String host = uri.getHost(); String host = uri.getHost();
if (host == null || !host.equals("offline")) { if (host == null || !host.equals("offline")) {
throw new GoogleAuthInfoException("Unsupported host"); throw new GoogleAuthInfoException(uri, "Unsupported host");
} }
String data = uri.getQueryParameter("data"); String data = uri.getQueryParameter("data");
if (data == null) { if (data == null) {
throw new GoogleAuthInfoException("Parameter 'data' is not set"); throw new GoogleAuthInfoException(uri, "Parameter 'data' is not set");
} }
GoogleAuthProtos.MigrationPayload payload; GoogleAuthProtos.MigrationPayload payload;
@ -209,7 +209,7 @@ public class GoogleAuthInfo implements Serializable {
byte[] bytes = Base64.decode(data); byte[] bytes = Base64.decode(data);
payload = GoogleAuthProtos.MigrationPayload.parseFrom(bytes); payload = GoogleAuthProtos.MigrationPayload.parseFrom(bytes);
} catch (EncodingException | InvalidProtocolBufferException e) { } catch (EncodingException | InvalidProtocolBufferException e) {
throw new GoogleAuthInfoException(e); throw new GoogleAuthInfoException(uri, e);
} }
List<GoogleAuthInfo> infos = new ArrayList<>(); List<GoogleAuthInfo> infos = new ArrayList<>();
@ -227,7 +227,7 @@ public class GoogleAuthInfo implements Serializable {
digits = 8; digits = 8;
break; break;
default: 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; String algo;
@ -244,7 +244,7 @@ public class GoogleAuthInfo implements Serializable {
algo = "SHA512"; algo = "SHA512";
break; break;
default: 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(); byte[] secret = params.getSecret().toByteArray();
@ -258,10 +258,10 @@ public class GoogleAuthInfo implements Serializable {
otp = new HotpInfo(secret, algo, digits, params.getCounter()); otp = new HotpInfo(secret, algo, digits, params.getCounter());
break; break;
default: 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){ } catch (OtpInfoException e){
throw new GoogleAuthInfoException(e); throw new GoogleAuthInfoException(uri, e);
} }
String name = params.getName(); String name = params.getName();

View file

@ -1,16 +1,30 @@
package com.beemdevelopment.aegis.otp; package com.beemdevelopment.aegis.otp;
import android.net.Uri;
public class GoogleAuthInfoException extends Exception { public class GoogleAuthInfoException extends Exception {
public GoogleAuthInfoException(Throwable cause) { private final Uri _uri;
public GoogleAuthInfoException(Uri uri, Throwable cause) {
super(cause); super(cause);
_uri = uri;
} }
public GoogleAuthInfoException(String message) { public GoogleAuthInfoException(Uri uri, String message) {
super(message); super(message);
_uri = uri;
} }
public GoogleAuthInfoException(String message, Throwable cause) { public GoogleAuthInfoException(Uri uri, String message, Throwable cause) {
super(message, 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 @Override
@ -19,6 +33,7 @@ public class GoogleAuthInfoException extends Exception {
if (cause == null) { if (cause == null) {
return super.getMessage(); return super.getMessage();
} }
return String.format("%s (%s)", super.getMessage(), cause.getMessage()); return String.format("%s (%s)", super.getMessage(), cause.getMessage());
} }
} }

View file

@ -157,7 +157,9 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
} }
} catch (GoogleAuthInfoException e) { } catch (GoogleAuthInfoException e) {
e.printStackTrace(); 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(); _cameraProvider.unbindAll();
} }
} }

View file

@ -193,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="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="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">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="authentication_method_raw">Raw</string>
<string name="unlocking_vault">Unlocking the vault</string> <string name="unlocking_vault">Unlocking the vault</string>
<string name="unlocking_vault_repair">Unlocking the vault (repairing)</string> <string name="unlocking_vault_repair">Unlocking the vault (repairing)</string>