From 6886e015f284fff23a25d6d4c5795b729ec7d0ee Mon Sep 17 00:00:00 2001 From: RichyHBM Date: Tue, 16 Jan 2018 22:54:10 +0000 Subject: [PATCH] Remove OpenPGP receiver as OpenPGP makes use of bindService internally and that cant be called from a broadcast receiver --- README.md | 6 + app/src/main/AndroidManifest.xml | 6 - .../Receivers/BackupBroadcastReceiver.java | 33 +----- .../EncryptedBackupBroadcastReceiver.java | 11 +- .../OpenPGPBackupBroadcasrReceiver.java | 110 ------------------ .../PlainTextBackupBroadcastReceiver.java | 9 +- .../andotp/Utilities/NotificationHelper.java | 41 +++++++ 7 files changed, 61 insertions(+), 155 deletions(-) delete mode 100644 app/src/main/java/org/shadowice/flocke/andotp/Receivers/OpenPGPBackupBroadcasrReceiver.java create mode 100644 app/src/main/java/org/shadowice/flocke/andotp/Utilities/NotificationHelper.java diff --git a/README.md b/README.md index 39138f47..db3b1e29 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7ceb8bcc..c7f9f9e8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,11 +63,5 @@ - - - - - - diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/BackupBroadcastReceiver.java b/app/src/main/java/org/shadowice/flocke/andotp/Receivers/BackupBroadcastReceiver.java index 3dc62aa5..cc18fffe 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/BackupBroadcastReceiver.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Receivers/BackupBroadcastReceiver.java @@ -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()); - } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/EncryptedBackupBroadcastReceiver.java b/app/src/main/java/org/shadowice/flocke/andotp/Receivers/EncryptedBackupBroadcastReceiver.java index 5566d788..377eced9 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/EncryptedBackupBroadcastReceiver.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Receivers/EncryptedBackupBroadcastReceiver.java @@ -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); } } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/OpenPGPBackupBroadcasrReceiver.java b/app/src/main/java/org/shadowice/flocke/andotp/Receivers/OpenPGPBackupBroadcasrReceiver.java deleted file mode 100644 index 694873dd..00000000 --- a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/OpenPGPBackupBroadcasrReceiver.java +++ /dev/null @@ -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 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() ); - } - } -} diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/PlainTextBackupBroadcastReceiver.java b/app/src/main/java/org/shadowice/flocke/andotp/Receivers/PlainTextBackupBroadcastReceiver.java index 329c9d99..6bbe67b0 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Receivers/PlainTextBackupBroadcastReceiver.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Receivers/PlainTextBackupBroadcastReceiver.java @@ -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 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); } } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/NotificationHelper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/NotificationHelper.java new file mode 100644 index 00000000..cb9c37d2 --- /dev/null +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/NotificationHelper.java @@ -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()); + } +}