Remove OpenPGP receiver as OpenPGP makes use of bindService internally and that cant be called from a broadcast receiver

This commit is contained in:
RichyHBM 2018-01-16 22:54:10 +00:00
parent 155c1481ff
commit 6886e015f2
7 changed files with 61 additions and 155 deletions

View file

@ -53,6 +53,12 @@ key, which renders them useless.
* [OpenPGP](http://openpgp.org/): OpenPGP can be used to easily decrypt the OpenPGP-encrypted backups on your PC.
* [andOTP-decrypt](https://github.com/asmw/andOTP-decrypt): A python script written by @asmw to decrypt password-protected backups on your PC (needs more testing).
### Automatic backups:
* BroadcastReceivers: AndOTP supports a number of broadcasts to perform automated backups, eg. via Tasker. These will get saved to the defined backup directory. **These only work when KeyStore is used as the encryption mechanism**
- **org.shadowice.flocke.andotp.broadcast.PLAIN_TEXT_BACKUP**: Perform a plain text backup. **WARNING**: This will save your 2FA tokens onto the disk in an unencrypted manner!
- **org.shadowice.flocke.andotp.broadcast.ENCRYPTED_BACKUP**: Perform an encrypted backup of your 2FA database using the selected password in settings.
## Migration:
Check out [this](https://github.com/andOTP/andOTP/wiki/Migration) wiki page to learn about the different ways to migrate to andOTP from other 2FA apps.

View file

@ -63,11 +63,5 @@
<action android:name="org.shadowice.flocke.andotp.broadcast.ENCRYPTED_BACKUP" />
</intent-filter>
</receiver>
<receiver android:name=".Receivers.OpenPGPBackupBroadcasrReceiver">
<intent-filter>
<action android:name="org.shadowice.flocke.andotp.broadcast.OPEN_PGP_BACKUP" />
</intent-filter>
</receiver>
</application>
</manifest>

View file

@ -12,6 +12,7 @@ import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.NotificationHelper;
import org.shadowice.flocke.andotp.Utilities.Settings;
import java.io.File;
@ -22,12 +23,12 @@ public abstract class BackupBroadcastReceiver extends BroadcastReceiver {
protected boolean canSaveBackup(Context context) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
notify(context, R.string.app_name, R.string.backup_receiver_read_permission_failed);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_receiver_read_permission_failed);
return false;
}
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
notify(context, R.string.app_name, R.string.backup_receiver_write_permission_failed);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_receiver_write_permission_failed);
return false;
}
@ -38,32 +39,4 @@ public abstract class BackupBroadcastReceiver extends BroadcastReceiver {
return true;
}
protected void notify(Context context, int resIdTitle, int resIdBody) {
notify(context, resIdTitle, context.getText(resIdBody).toString());
}
protected void notify(Context context, int resIdTitle, String resBody) {
String channelId = "andOTP_channel";
NotificationChannel channel = null;
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, null)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(context.getText(resIdTitle))
.setContentText(resBody);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channel = new NotificationChannel(channelId, context.getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
} else {
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
}
builder.setChannelId(channelId);
int notificationId = 1;
notificationManager.notify(notificationId, builder.build());
}
}

View file

@ -11,6 +11,7 @@ import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import org.shadowice.flocke.andotp.Utilities.FileHelper;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
import org.shadowice.flocke.andotp.Utilities.NotificationHelper;
import org.shadowice.flocke.andotp.Utilities.Settings;
import org.shadowice.flocke.andotp.Utilities.Tools;
@ -31,7 +32,7 @@ public class EncryptedBackupBroadcastReceiver extends BackupBroadcastReceiver {
String password = settings.getBackupPasswordEnc();
if (password.isEmpty()) {
notify(context, R.string.app_name, R.string.backup_toast_crypt_password_not_set);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_toast_crypt_password_not_set);
return;
}
@ -40,7 +41,7 @@ public class EncryptedBackupBroadcastReceiver extends BackupBroadcastReceiver {
if (settings.getEncryption() == Constants.EncryptionType.KEYSTORE) {
encryptionKey = KeyStoreHelper.loadEncryptionKeyFromKeyStore(context, false);
} else {
notify(context, R.string.app_name, R.string.backup_receiver_custom_encryption_failed );
NotificationHelper.notify(context, R.string.app_name, R.string.backup_receiver_custom_encryption_failed );
return;
}
@ -54,13 +55,13 @@ public class EncryptedBackupBroadcastReceiver extends BackupBroadcastReceiver {
SecretKey key = EncryptionHelper.generateSymmetricKeyFromPassword(password);
byte[] encrypted = EncryptionHelper.encrypt(key, plain.getBytes(StandardCharsets.UTF_8));
FileHelper.writeBytesToFile(context, savePath, encrypted);
notify(context, R.string.app_name, context.getText(R.string.backup_receiver_completed) + savePath.getPath());
NotificationHelper.notify(context, R.string.app_name, context.getText(R.string.backup_receiver_completed) + savePath.getPath());
} catch (Exception e) {
e.printStackTrace();
notify(context, R.string.app_name, R.string.backup_toast_export_failed);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_toast_export_failed);
}
} else {
notify(context, R.string.app_name, R.string.backup_toast_storage_not_accessible);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_toast_storage_not_accessible);
}
}
}

View file

@ -1,110 +0,0 @@
package org.shadowice.flocke.andotp.Receivers;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
import org.shadowice.flocke.andotp.Utilities.FileHelper;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
import org.shadowice.flocke.andotp.Utilities.Settings;
import org.shadowice.flocke.andotp.Utilities.Tools;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import javax.crypto.SecretKey;
public class OpenPGPBackupBroadcasrReceiver extends BackupBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(!canSaveBackup(context))
return;
Settings settings = new Settings(context);
Uri savePath = Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_PGP);
SecretKey encryptionKey;
if (settings.getEncryption() == Constants.EncryptionType.KEYSTORE) {
encryptionKey = KeyStoreHelper.loadEncryptionKeyFromKeyStore(context, false);
} else {
notify(context, R.string.app_name, R.string.backup_receiver_custom_encryption_failed);
return;
}
String PGPProvider = settings.getOpenPGPProvider();
if (PGPProvider.isEmpty()) {
notify(context, R.string.app_name, R.string.backup_desc_openpgp_keyid /* need to set pgp provider */);
return;
}
if(settings.getOpenPGPKey() == 0) {
notify(context, R.string.app_name, R.string.backup_desc_openpgp_keyid);
return;
}
OpenPgpServiceConnection pgpServiceConnection = new OpenPgpServiceConnection(context, PGPProvider);
pgpServiceConnection.bindToService();
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(context, encryptionKey);
String plainJSON = DatabaseHelper.entriesToString(entries);
Intent encryptIntent = new Intent();
if (settings.getOpenPGPSign()) {
encryptIntent.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT);
encryptIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, settings.getOpenPGPKey());
} else {
encryptIntent.setAction(OpenPgpApi.ACTION_ENCRYPT);
}
encryptIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[]{settings.getOpenPGPKey()});
encryptIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
InputStream is = new ByteArrayInputStream(plainJSON.getBytes(StandardCharsets.UTF_8));
ByteArrayOutputStream os = new ByteArrayOutputStream();
OpenPgpApi api = new OpenPgpApi(context, pgpServiceConnection.getService());
Intent result = api.executeApi(encryptIntent, is, os);
handleOpenPGPResult(context, result, os, savePath, Constants.INTENT_BACKUP_ENCRYPT_PGP);
pgpServiceConnection.unbindFromService();
}
public void handleOpenPGPResult(Context context, Intent result, ByteArrayOutputStream os, Uri file, int requestCode) {
if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) == OpenPgpApi.RESULT_CODE_SUCCESS) {
if (requestCode == Constants.INTENT_BACKUP_ENCRYPT_PGP) {
if (os != null) {
if (Tools.isExternalStorageWritable()) {
boolean success = FileHelper.writeStringToFile(context, file, new String(os.toByteArray(), StandardCharsets.UTF_8));
if (success) {
notify(context, R.string.app_name, context.getText(R.string.backup_receiver_completed) + file.getPath());
} else {
notify(context, R.string.app_name, R.string.backup_toast_export_failed);
}
} else {
notify(context, R.string.app_name, R.string.backup_toast_storage_not_accessible);
}
}
}
} else {
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
notify(context, R.string.app_name, context.getText(R.string.backup_toast_export_failed) + " " + error.getMessage() );
}
}
}

View file

@ -10,6 +10,7 @@ import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
import org.shadowice.flocke.andotp.Utilities.FileHelper;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
import org.shadowice.flocke.andotp.Utilities.NotificationHelper;
import org.shadowice.flocke.andotp.Utilities.Settings;
import org.shadowice.flocke.andotp.Utilities.Tools;
@ -33,7 +34,7 @@ public class PlainTextBackupBroadcastReceiver extends BackupBroadcastReceiver {
if (settings.getEncryption() == Constants.EncryptionType.KEYSTORE) {
encryptionKey = KeyStoreHelper.loadEncryptionKeyFromKeyStore(context, false);
} else {
notify(context, R.string.app_name, R.string.backup_receiver_custom_encryption_failed);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_receiver_custom_encryption_failed);
return;
}
@ -41,12 +42,12 @@ public class PlainTextBackupBroadcastReceiver extends BackupBroadcastReceiver {
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(context, encryptionKey);
if (FileHelper.writeStringToFile(context, savePath, DatabaseHelper.entriesToString(entries))) {
notify(context, R.string.app_name, context.getText(R.string.backup_receiver_completed) + savePath.getPath());
NotificationHelper.notify(context, R.string.app_name, context.getText(R.string.backup_receiver_completed) + savePath.getPath());
} else {
notify(context, R.string.app_name, R.string.backup_toast_export_failed);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_toast_export_failed);
}
} else {
notify(context, R.string.app_name, R.string.backup_toast_storage_not_accessible);
NotificationHelper.notify(context, R.string.app_name, R.string.backup_toast_storage_not_accessible);
}
}
}

View file

@ -0,0 +1,41 @@
package org.shadowice.flocke.andotp.Utilities;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import org.shadowice.flocke.andotp.R;
import static android.content.Context.NOTIFICATION_SERVICE;
public class NotificationHelper {
public static void notify(Context context, int resIdTitle, int resIdBody) {
notify(context, resIdTitle, context.getText(resIdBody).toString());
}
public static void notify(Context context, int resIdTitle, String resBody) {
String channelId = "andOTP_channel";
NotificationChannel channel = null;
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, null)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(context.getText(resIdTitle))
.setContentText(resBody);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channel = new NotificationChannel(channelId, context.getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
} else {
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
}
builder.setChannelId(channelId);
int notificationId = 1;
notificationManager.notify(notificationId, builder.build());
}
}