Skip to content

Commit

Permalink
Bubble up encryption key exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
avazirna committed Jan 24, 2024
1 parent af8194a commit db7ef66
Show file tree
Hide file tree
Showing 15 changed files with 110 additions and 20 deletions.
4 changes: 3 additions & 1 deletion app/assets/locales/android_translatable_strings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,12 @@ post.generic.error=An error occurred while preparing the HTTP request
post.dialog.title=Claiming...
post.dialog.body=Claiming chosen data from server
post.io.error=Error reading server response: ${0}
post.unknown.response=Received unknown resonse code (${0}) from server
post.unknown.response=Received unknown response code (${0}) from server
post.client.error=Client-side error (code ${0}) received from network request.
post.server.error=Server-side error (code ${0}) received from network request.
post.gone.error=Case is not present on server.
post.conflict.error=You have already claimed this case.
post.cache.encryption.key.error=Encryption error while processing server response: ${0}

version.id.long=CommCare Android, version "${0}"(${1}). App v${5}. CommCare Version ${2}. Build ${3}, built on: ${4}
version.id.short=CCDroid:"${0}"(${1}). v${5} CC${2}b[${3}] on ${4}
Expand Down Expand Up @@ -263,6 +264,7 @@ app.handled.error.explanation=CommCare will now restart. You may need to correct
app.key.request.message=Another android application has requested the ability to communicate securely with CommCare. This application will be able to pass information to CommCare and trigger actions (submission, login, etc). Do you want to grant access?
app.key.request.grant=Grant Access
app.key.request.deny=Deny Access
app.key.request.encryption.key.error=Error during encryption Key generation

key.manage.title=Logging in
key.manage.start=Logging in
Expand Down
8 changes: 8 additions & 0 deletions app/src/org/commcare/activities/InstallFromListActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.commcare.preferences.GlobalPrivilegesManager;
import org.commcare.tasks.ModernHttpTask;
import org.commcare.tasks.templates.CommCareTaskConnector;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.util.LogTypes;
import org.commcare.utils.ConnectivityStatus;
import org.commcare.views.UserfacingErrorHandling;
Expand Down Expand Up @@ -355,6 +356,13 @@ public void handleIOException(IOException exception) {
repeatRequestOrShowResults(true, false);
}

@Override
public void handleEncryptionKeyException(EncryptionKeyHelper.EncryptionKeyException exception) {
Logger.log(LogTypes.TYPE_ERROR_ENCRYPTION_KEY,
"An ENcryptionKeyException was encountered when pocessing available apps request: " + exception.getMessage());
repeatRequestOrShowResults(true, false);
}

private void handleRequestError(int responseCode, boolean couldBeUserError) {
Logger.log(LogTypes.TYPE_ERROR_SERVER_COMMS,
"Request to " + urlCurrentlyRequestingFrom + " in get available apps request " +
Expand Down
13 changes: 12 additions & 1 deletion app/src/org/commcare/activities/KeyAccessRequestActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import org.commcare.CommCareApplication;
import org.commcare.android.database.global.models.AndroidSharedKeyRecord;
import org.commcare.dalvik.R;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.views.ManagedUi;
import org.commcare.views.UiElement;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.locale.Localization;

import androidx.appcompat.app.AppCompatActivity;

Expand All @@ -34,7 +38,14 @@ protected void onCreate(Bundle savedInstanceState) {

grantButton.setOnClickListener(v -> {
Intent response = new Intent(getIntent());
AndroidSharedKeyRecord record = AndroidSharedKeyRecord.generateNewSharingKey();
AndroidSharedKeyRecord record = null;
try {
record = AndroidSharedKeyRecord.generateNewSharingKey();
} catch (EncryptionKeyHelper.EncryptionKeyException e) {
Toast.makeText(this, Localization.get("app.key.request.encryption.key.error"), Toast.LENGTH_LONG).show();
Logger.exception("Exception while generating encryption key ", e);
return;
}
CommCareApplication.instance().getGlobalStorage(AndroidSharedKeyRecord.class).write(record);
record.writeResponseToIntent(response);
setResult(AppCompatActivity.RESULT_OK, response);
Expand Down
6 changes: 6 additions & 0 deletions app/src/org/commcare/activities/PostRequestActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.commcare.tasks.DataPullTask;
import org.commcare.tasks.ModernHttpTask;
import org.commcare.tasks.templates.CommCareTaskConnector;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.views.ManagedUi;
import org.commcare.views.UiElement;
import org.commcare.views.dialogs.CustomProgressDialog;
Expand Down Expand Up @@ -214,6 +215,11 @@ public void handleIOException(IOException exception) {
}
}

@Override
public void handleEncryptionKeyException(EncryptionKeyHelper.EncryptionKeyException exception) {
enterErrorState(Localization.get("post.cache.encryption.key.error", exception.getMessage()));
}

@Override
public void onBackPressed() {
if (inErrorState) {
Expand Down
6 changes: 6 additions & 0 deletions app/src/org/commcare/activities/QueryRequestActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.commcare.session.RemoteQuerySessionManager;
import org.commcare.tasks.ModernHttpTask;
import org.commcare.tasks.templates.CommCareTaskConnector;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.utils.SessionRegistrationHelper;
import org.commcare.utils.SessionUnavailableException;
import org.commcare.views.UserfacingErrorHandling;
Expand Down Expand Up @@ -196,6 +197,11 @@ public void handleIOException(IOException exception) {
}
}

@Override
public void handleEncryptionKeyException(EncryptionKeyHelper.EncryptionKeyException exception) {
enterErrorState(Localization.get("post.cache.encryption.key.error", exception.getMessage()));
}

@Override
public CustomProgressDialog generateProgressDialog(int taskId) {
String title, message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.commcare.models.framework.Persisting;
import org.commcare.modern.database.Table;
import org.commcare.modern.models.MetaField;
import org.commcare.util.EncryptionKeyHelper;
import org.javarosa.core.services.Logger;
import org.javarosa.core.util.PropertyUtils;

Expand Down Expand Up @@ -48,7 +49,8 @@ public AndroidSharedKeyRecord(String keyId, byte[] privateKey, byte[] publicKey)
this.publicKey = publicKey;
}

public static AndroidSharedKeyRecord generateNewSharingKey() {
public static AndroidSharedKeyRecord generateNewSharingKey()
throws EncryptionKeyHelper.EncryptionKeyException {
KeyPair pair = CryptUtil.generateRandomKeyPair(512);
byte[] encodedPrivate = pair.getPrivate().getEncoded();
byte[] encodedPublic = pair.getPublic().getEncoded();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.commcare.android.database.app.models.UserKeyRecord;
import org.commcare.core.encryption.CryptUtil;
import org.commcare.models.encryption.ByteEncrypter;
import org.commcare.util.EncryptionKeyHelper;
import org.javarosa.core.model.User;
import org.javarosa.core.util.PropertyUtils;

Expand Down Expand Up @@ -65,8 +66,11 @@ private void createAndWriteKeyRecordAndUser() {
int userCount = keyRecordDB.getIDsForValue(UserKeyRecord.META_USERNAME, username).size();

if (userCount == 0) {
SecretKey secretKey = CryptUtil.generateRandomSecretKey();
if (secretKey == null) {

SecretKey secretKey;
try {
secretKey = CryptUtil.generateRandomSecretKey();
} catch (EncryptionKeyHelper.EncryptionKeyException e) {
throw new RuntimeException("Error setting up user's encrypted storage");
}
randomKey = secretKey.getEncoded();
Expand Down
8 changes: 8 additions & 0 deletions app/src/org/commcare/network/GetAndParseActor.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.commcare.core.network.AuthInfo;
import org.commcare.core.network.AuthenticationInterceptor;
import org.commcare.core.network.ModernHttpRequester;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.util.LogTypes;
import org.javarosa.core.io.StreamsUtil;
import org.javarosa.core.services.Logger;
Expand Down Expand Up @@ -108,6 +109,13 @@ public void handleIOException(IOException exception) {
}
}

@Override
public void handleEncryptionKeyException(EncryptionKeyHelper.EncryptionKeyException exception) {
Logger.log(LogTypes.TYPE_ERROR_ENCRYPTION_KEY,
String.format("Encountered EncryptionKeyException while getting response stream for %s response: %s",
requestName, exception.getMessage()));
}

private void processErrorResponse(int responseCode) {
Logger.log(LogTypes.TYPE_ERROR_SERVER_COMMS,
String.format("Received error response from %s request: %s", requestName, responseCode));
Expand Down
12 changes: 9 additions & 3 deletions app/src/org/commcare/network/HttpCalloutTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.commcare.data.xml.DataModelPullParser;
import org.commcare.data.xml.TransactionParserFactory;
import org.commcare.tasks.templates.CommCareTask;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.util.LogTypes;
import org.commcare.utils.AndroidCacheDirSetup;
import org.javarosa.core.io.StreamsUtil;
Expand Down Expand Up @@ -43,7 +44,7 @@ public enum HttpCalloutOutcomes {
NetworkFailureBadPassword,
IncorrectPin,
AuthOverHttp,
CaptivePortal
ResponseCacheError, CaptivePortal
}

private final Context c;
Expand Down Expand Up @@ -99,6 +100,9 @@ protected HttpCalloutOutcomes doTaskBackground(Object... params) {
//This is probably related to local files, actually
e.printStackTrace();
outcome = HttpCalloutOutcomes.NetworkFailure;
} catch (EncryptionKeyHelper.EncryptionKeyException e) {
e.printStackTrace();
outcome = HttpCalloutOutcomes.ResponseCacheError;
}

//If we needed the callout to succeed and it didn't, return our failure.
Expand Down Expand Up @@ -131,7 +135,8 @@ protected HttpCalloutOutcomes doSetupTaskBeforeRequest() {

protected abstract Response<ResponseBody> doHttpRequest() throws IOException;

protected HttpCalloutOutcomes doResponseSuccess(Response<ResponseBody> response) throws IOException {
protected HttpCalloutOutcomes doResponseSuccess(Response<ResponseBody> response)
throws IOException, EncryptionKeyHelper.EncryptionKeyException {
beginResponseHandling(response);

InputStream input = cacheResponseOpenHandle(response);
Expand Down Expand Up @@ -162,7 +167,8 @@ protected HttpCalloutOutcomes doResponseSuccess(Response<ResponseBody> response)

protected abstract TransactionParserFactory getTransactionParserFactory();

protected InputStream cacheResponseOpenHandle(Response<ResponseBody> response) throws IOException {
protected InputStream cacheResponseOpenHandle(Response<ResponseBody> response)
throws IOException, EncryptionKeyHelper.EncryptionKeyException {
long dataSizeGuess = ModernHttpRequester.getContentLength(response);

BitCache cache = BitCacheFactory.getCache(new AndroidCacheDirSetup(c), dataSizeGuess);
Expand Down
4 changes: 3 additions & 1 deletion app/src/org/commcare/network/RemoteDataPullResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.commcare.tasks.DataPullTask;
import org.commcare.core.network.bitcache.BitCache;
import org.commcare.core.network.bitcache.BitCacheFactory;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.utils.AndroidCacheDirSetup;
import org.javarosa.core.io.StreamsUtil;

Expand Down Expand Up @@ -49,7 +50,8 @@ protected RemoteDataPullResponse(DataPullTask task,
*
* @throws IOException If there is an issue reading or writing the response.
*/
public BitCache writeResponseToCache(Context c) throws IOException {
public BitCache writeResponseToCache(Context c)
throws IOException, EncryptionKeyHelper.EncryptionKeyException {
BitCache cache = null;
try (InputStream input = getInputStream()) {
final long dataSizeGuess = ModernHttpRequester.getContentLength(response);
Expand Down
3 changes: 2 additions & 1 deletion app/src/org/commcare/tasks/AsyncRestoreHelper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.commcare.tasks;

import org.commcare.network.RemoteDataPullResponse;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.util.LogTypes;
import org.javarosa.core.io.StreamsUtil;
import org.javarosa.core.services.Logger;
Expand Down Expand Up @@ -74,7 +75,7 @@ private boolean parseProgressFromRetryResult(RemoteDataPullResponse response) {
}
eventType = parser.next();
} while (eventType != KXmlParser.END_DOCUMENT);
} catch (IOException | XmlPullParserException e) {
} catch (IOException | XmlPullParserException | EncryptionKeyHelper.EncryptionKeyException e) {
Logger.log(LogTypes.TYPE_USER,
"Error while parsing progress values of retry result");
} finally {
Expand Down
21 changes: 16 additions & 5 deletions app/src/org/commcare/tasks/DataPullTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.commcare.services.CommCareSessionService;
import org.commcare.sync.ExternalDataUpdateHelper;
import org.commcare.tasks.templates.CommCareTask;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.util.LogTypes;
import org.commcare.utils.FormSaveUtil;
import org.commcare.utils.SessionUnavailableException;
Expand Down Expand Up @@ -223,8 +224,10 @@ private byte[] getEncryptionKey() {

private void initUKRForLogin() {
if (blockRemoteKeyManagement || shouldGenerateFirstKey()) {
SecretKey newKey = CryptUtil.generateRandomSecretKey();
if (newKey == null) {
SecretKey newKey = null;
try {
newKey = CryptUtil.generateRandomSecretKey();
} catch (EncryptionKeyHelper.EncryptionKeyException e) {
return;
}
String sandboxId = PropertyUtils.genUUID().replace("-", "");
Expand Down Expand Up @@ -294,6 +297,9 @@ private ResultAndError<PullTaskResult> getRequestResultOrRetry(AndroidTransactio
} catch (UnknownSyncError e) {
e.printStackTrace();
Logger.log(LogTypes.TYPE_WARNING_NETWORK, "Couldn't sync due to Unknown Error|" + e.getMessage());
} catch (EncryptionKeyHelper.EncryptionKeyException e) {
e.printStackTrace();
Logger.log(LogTypes.TYPE_WARNING_NETWORK, "Couldn't sync due to Cache encryption Error|" + e.getMessage());
}

wipeLoginIfItOccurred();
Expand All @@ -305,8 +311,9 @@ private ResultAndError<PullTaskResult> getRequestResultOrRetry(AndroidTransactio
* @return the proper result, or null if we have not yet been able to determine the result to
* return
*/
private ResultAndError<PullTaskResult> makeRequestAndHandleResponse(AndroidTransactionParserFactory factory)
throws IOException, UnknownSyncError {
private ResultAndError<PullTaskResult> makeRequestAndHandleResponse(
AndroidTransactionParserFactory factory)
throws IOException, UnknownSyncError, EncryptionKeyHelper.EncryptionKeyException {

RemoteDataPullResponse pullResponse =
dataPullRequester.makeDataPullRequest(this, requestor, server, !loginNeeded, skipFixtures);
Expand Down Expand Up @@ -352,7 +359,7 @@ private ResultAndError<PullTaskResult> handleAuthFailed() {
*/
private ResultAndError<PullTaskResult> handleSuccessResponseCode(
RemoteDataPullResponse pullResponse, AndroidTransactionParserFactory factory)
throws IOException, UnknownSyncError {
throws IOException, UnknownSyncError, EncryptionKeyHelper.EncryptionKeyException {

asyncRestoreHelper.completeServerProgressBarIfShowing();
handleLoginNeededOnSuccess();
Expand Down Expand Up @@ -541,6 +548,10 @@ private Pair<Integer, String> recover(CommcareRequestEndpoints requestor, Androi
//Ok, well, we're bailing here, but we didn't make any changes
Logger.log(LogTypes.TYPE_USER, "Sync Recovery Failed due to IOException|" + e.getMessage());
return new Pair<>(PROGRESS_RECOVERY_FAIL_SAFE, "");
} catch (EncryptionKeyHelper.EncryptionKeyException e) {
e.printStackTrace();
Logger.log(LogTypes.TYPE_USER, "Sync Recovery Failed due to Cache encryption error|" + e.getMessage());
return new Pair<>(PROGRESS_RECOVERY_FAIL_SAFE, "");
}

this.publishProgress(PROGRESS_RECOVERY_STARTED);
Expand Down
12 changes: 9 additions & 3 deletions app/src/org/commcare/tasks/ModernHttpTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import org.commcare.core.network.HTTPMethod;
import org.commcare.core.network.ModernHttpRequester;
import org.commcare.tasks.templates.CommCareTask;
import org.commcare.util.EncryptionKeyHelper;

import java.io.IOException;
import java.io.InputStream;
import java.security.Key;
import java.util.HashMap;

import javax.annotation.Nullable;
Expand All @@ -36,7 +38,7 @@ public class ModernHttpTask

private final ModernHttpRequester requester;
private InputStream responseDataStream;
private IOException mException;
private Exception mException;
private Response<ResponseBody> mResponse;

// Use for GET request
Expand Down Expand Up @@ -72,7 +74,7 @@ protected Void doTaskBackground(Void... params) {
if (mResponse.isSuccessful()) {
responseDataStream = requester.getResponseStream(mResponse);
}
} catch (IOException e) {
} catch (IOException | EncryptionKeyHelper.EncryptionKeyException e) {
mException = e;
}
return null;
Expand All @@ -83,7 +85,11 @@ protected void deliverResult(HttpResponseProcessor httpResponseProcessor,
Void result) {

if (mException != null) {
httpResponseProcessor.handleIOException(mException);
if (mException instanceof IOException ioExcep) {
httpResponseProcessor.handleIOException(ioExcep);
} else if (mException instanceof EncryptionKeyHelper.EncryptionKeyException encryptKeyExcep) {
httpResponseProcessor.handleEncryptionKeyException(encryptKeyExcep);
}
} else {
// route to appropriate callback based on http response code
ModernHttpRequester.processResponse(
Expand Down
14 changes: 13 additions & 1 deletion app/src/org/commcare/utils/TemplatePrinterUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.commcare.android.javarosa.IntentCallout;
import org.commcare.core.encryption.CryptUtil;
import org.commcare.util.EncryptionKeyHelper;
import org.commcare.views.dialogs.StandardAlertDialog;

import java.io.BufferedReader;
Expand Down Expand Up @@ -38,7 +39,18 @@
public abstract class TemplatePrinterUtils {

private static final String FORMAT_REGEX_WITH_DELIMITER = "((?<=%2$s)|(?=%1$s))";
private static final SecretKey KEY = CryptUtil.generateRandomSecretKey();
private static final SecretKey KEY;

static {
SecretKey secretKey = null;
try {
secretKey = CryptUtil.generateRandomSecretKey();
} catch (EncryptionKeyHelper.EncryptionKeyException e) {
secretKey = null;
} finally{
KEY = secretKey;
}
}

/**
* Concatenate all Strings in a String array to one String.
Expand Down
Loading

0 comments on commit db7ef66

Please sign in to comment.