Remove OpenPGP receiver as OpenPGP makes use of bindService internally and that cant be called from a broadcast receiver
This commit is contained in:
parent
155c1481ff
commit
6886e015f2
7 changed files with 61 additions and 155 deletions
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue