Merge pull request #11523 from nextmcloud/nmc/1944-e2eeDialogContextCrash

E2EE dialog crash on clicking "Set as Encrypted" multiple times
This commit is contained in:
Andy Scherzinger 2023-06-20 16:53:25 +02:00 committed by GitHub
commit 94cfe5a482
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -2,8 +2,10 @@
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* @author TSI-mc
* Copyright (C) 2017 Tobias Kaminsky
* Copyright (C) 2017 Nextcloud GmbH.
* Copyright (C) 2023 TSI-mc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -22,6 +24,7 @@ package com.owncloud.android.ui.dialog;
import android.accounts.AccountManager;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
@ -50,6 +53,7 @@ import com.owncloud.android.utils.EncryptionUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.ArrayList;
@ -129,7 +133,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
viewThemeUtils.platform.colorTextButtons(positiveButton, neutralButton);
}
task = new DownloadKeysAsyncTask();
task = new DownloadKeysAsyncTask(requireContext());
task.execute();
}
@ -239,7 +243,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
neutralButton.setVisibility(View.GONE);
getDialog().setTitle(R.string.end_to_end_encryption_storing_keys);
GenerateNewKeysAsyncTask newKeysTask = new GenerateNewKeysAsyncTask();
GenerateNewKeysAsyncTask newKeysTask = new GenerateNewKeysAsyncTask(requireContext());
newKeysTask.execute();
break;
@ -293,6 +297,12 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
}
public class DownloadKeysAsyncTask extends AsyncTask<Void, Void, String> {
private final WeakReference<Context> mWeakContext;
public DownloadKeysAsyncTask(Context context) {
mWeakContext = new WeakReference<>(context);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
@ -303,47 +313,58 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
@Override
protected String doInBackground(Void... voids) {
Context context = mWeakContext.get();
// fetch private/public key
// if available
// - store public key
// - decrypt private key, store unencrypted private key in database
GetPublicKeyOperation publicKeyOperation = new GetPublicKeyOperation();
RemoteOperationResult<String> publicKeyResult = publicKeyOperation.execute(user, getContext());
if (user != null) {
RemoteOperationResult<String> publicKeyResult = publicKeyOperation.execute(user, context);
if (publicKeyResult.isSuccess()) {
Log_OC.d(TAG, "public key successful downloaded for " + user.getAccountName());
if (publicKeyResult.isSuccess()) {
Log_OC.d(TAG, "public key successful downloaded for " + user.getAccountName());
String publicKeyFromServer = publicKeyResult.getResultData();
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
EncryptionUtils.PUBLIC_KEY,
publicKeyFromServer);
} else {
return null;
}
RemoteOperationResult<com.owncloud.android.lib.ocs.responses.PrivateKey> privateKeyResult =
new GetPrivateKeyOperation().execute(user, getContext());
if (privateKeyResult.isSuccess()) {
Log_OC.d(TAG, "private key successful downloaded for " + user.getAccountName());
keyResult = KEY_EXISTING_USED;
return privateKeyResult.getResultData().getKey();
} else {
return null;
String publicKeyFromServer = publicKeyResult.getResultData();
if (arbitraryDataProvider != null) {
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
EncryptionUtils.PUBLIC_KEY,
publicKeyFromServer);
} else {
return null;
}
} else {
return null;
}
RemoteOperationResult<com.owncloud.android.lib.ocs.responses.PrivateKey> privateKeyResult =
new GetPrivateKeyOperation().execute(user, context);
if (privateKeyResult.isSuccess()) {
Log_OC.d(TAG, "private key successful downloaded for " + user.getAccountName());
keyResult = KEY_EXISTING_USED;
return privateKeyResult.getResultData().getKey();
}
}
return null;
}
@Override
protected void onPostExecute(String privateKey) {
super.onPostExecute(privateKey);
Context context = mWeakContext.get();
if (context == null) {
Log_OC.e(TAG, "Context lost after fetching private keys.");
return;
}
if (privateKey == null) {
// first show info
try {
if (keyWords == null || keyWords.isEmpty()) {
keyWords = EncryptionUtils.getRandomWords(12, requireContext());
keyWords = EncryptionUtils.getRandomWords(12, context);
}
showMnemonicInfo();
} catch (IOException e) {
@ -360,6 +381,13 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
}
public class GenerateNewKeysAsyncTask extends AsyncTask<Void, Void, String> {
private final WeakReference<Context> mWeakContext;
public GenerateNewKeysAsyncTask(Context context) {
mWeakContext = new WeakReference<>(context);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
@ -373,18 +401,20 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
// - encrypt private key, push key to server, store unencrypted private key in database
try {
Context context = mWeakContext.get();
String publicKeyString;
// Create public/private key pair
KeyPair keyPair = EncryptionUtils.generateKeyPair();
// create CSR
AccountManager accountManager = AccountManager.get(getContext());
AccountManager accountManager = AccountManager.get(context);
String userId = accountManager.getUserData(user.toPlatformAccount(), AccountUtils.Constants.KEY_USER_ID);
String urlEncoded = CsrHelper.generateCsrPemEncodedString(keyPair, userId);
SendCSROperation operation = new SendCSROperation(urlEncoded);
RemoteOperationResult result = operation.execute(user, getContext());
RemoteOperationResult result = operation.execute(user, context);
if (result.isSuccess()) {
publicKeyString = (String) result.getData().get(0);
@ -407,7 +437,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
// upload encryptedPrivateKey
StorePrivateKeyOperation storePrivateKeyOperation = new StorePrivateKeyOperation(encryptedPrivateKey);
RemoteOperationResult storePrivateKeyResult = storePrivateKeyOperation.execute(user, getContext());
RemoteOperationResult storePrivateKeyResult = storePrivateKeyOperation.execute(user, context);
if (storePrivateKeyResult.isSuccess()) {
Log_OC.d(TAG, "private key success");
@ -426,7 +456,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
return (String) storePrivateKeyResult.getData().get(0);
} else {
DeletePublicKeyOperation deletePublicKeyOperation = new DeletePublicKeyOperation();
deletePublicKeyOperation.execute(user, getContext());
deletePublicKeyOperation.execute(user, context);
}
} catch (Exception e) {
Log_OC.e(TAG, e.getMessage());
@ -440,9 +470,20 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
protected void onPostExecute(String s) {
super.onPostExecute(s);
Context context = mWeakContext.get();
if (context == null) {
Log_OC.e(TAG, "Context lost after generating new private keys.");
return;
}
if (s.isEmpty()) {
errorSavingKeys();
} else {
if (getDialog() == null) {
Log_OC.e(TAG, "Dialog is null cannot proceed further.");
return;
}
requireDialog().dismiss();
notifyResult();
}
@ -464,6 +505,10 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
@VisibleForTesting
public void showMnemonicInfo() {
if (getDialog() == null) {
Log_OC.e(TAG, "Dialog is null cannot proceed further.");
return;
}
requireDialog().setTitle(R.string.end_to_end_encryption_passphrase_title);
binding.encryptionStatus.setText(R.string.end_to_end_encryption_keywords_description);
@ -483,6 +528,11 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
@VisibleForTesting
public void errorSavingKeys() {
if (getDialog() == null) {
Log_OC.e(TAG, "Dialog is null cannot proceed further.");
return;
}
keyResult = KEY_FAILED;
requireDialog().setTitle(R.string.common_error);