From f3fa2d0038bb6bbb1a6558d2bd171c89a8a9ea45 Mon Sep 17 00:00:00 2001 From: M66B Date: Thu, 24 Oct 2019 14:49:59 +0200 Subject: [PATCH] Use temporary files for encryption/decryption --- .../eu/faircode/email/FragmentCompose.java | 233 +++++++++--------- .../eu/faircode/email/FragmentMessages.java | 178 ++++++------- 2 files changed, 217 insertions(+), 194 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index 3d0a8df063..52e7bc3fc3 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -112,9 +112,8 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.io.BufferedOutputStream; import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; @@ -1557,130 +1556,144 @@ public class FragmentCompose extends FragmentBase { MimeMessage imessage = new MimeMessage(isession); MessageHelper.build(context, message, attachments, identity, imessage); + // Create temporary files + File plain = File.createTempFile("plain", "." + id, context.getCacheDir()); + File encrypted = File.createTempFile("encrypted", "." + id, context.getCacheDir()); + // Serialize message - ByteArrayOutputStream os = new ByteArrayOutputStream(); - imessage.writeTo(os); - ByteArrayInputStream decrypted = new ByteArrayInputStream(os.toByteArray()); - ByteArrayOutputStream encrypted = null; - if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction()) || - OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction())) - encrypted = new ByteArrayOutputStream(); - - // Encrypt message - Log.i("Executing " + data.getAction()); - OpenPgpApi api = new OpenPgpApi(context, pgpService.getService()); - Intent result = api.executeApi(data, decrypted, encrypted); - - int resultCode = result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); - Log.i("Result action=" + data.getAction() + " code=" + resultCode); - switch (resultCode) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - // Attach encrypted data / signature - if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction()) || - OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction()) || - OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) - try { - db.beginTransaction(); - - String name; - int encryption; - if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction())) { - name = "keydata.asc"; - encryption = EntityAttachment.PGP_KEY; - } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction())) { - name = "encrypted.asc"; - encryption = EntityAttachment.PGP_MESSAGE; - } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) { - name = "signature.asc"; - encryption = EntityAttachment.PGP_SIGNATURE; - } else - throw new IllegalStateException(data.getAction()); - - EntityAttachment attachment = new EntityAttachment(); - attachment.message = id; - attachment.sequence = db.attachment().getAttachmentSequence(id) + 1; - attachment.name = name; - attachment.type = "application/octet-stream"; - attachment.disposition = Part.INLINE; - attachment.encryption = encryption; - attachment.id = db.attachment().insertAttachment(attachment); - - byte[] bytes; - if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) - bytes = result.getByteArrayExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE); - else - bytes = encrypted.toByteArray(); - - File file = attachment.getFile(context); - Log.i("Writing " + file + " size=" + bytes.length); - try (OutputStream out = new FileOutputStream(file)) { - out.write(bytes); + try (OutputStream out = new FileOutputStream(plain)) { + imessage.writeTo(out); + } + + // Call OpenPGP + Intent result; + try (InputStream in = new FileInputStream(plain)) { + try (OutputStream out = new FileOutputStream(encrypted)) { + Log.i("Executing " + data.getAction()); + OpenPgpApi api = new OpenPgpApi(context, pgpService.getService()); + result = api.executeApi(data, in, out); + } + } + + plain.delete(); + + // Process result + try { + int resultCode = result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + Log.i("Result action=" + data.getAction() + " code=" + resultCode); + switch (resultCode) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + // Attach encrypted data / signature + if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction()) || + OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction()) || + OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) + try { + db.beginTransaction(); + + String name; + int encryption; + if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction())) { + name = "keydata.asc"; + encryption = EntityAttachment.PGP_KEY; + } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction())) { + name = "encrypted.asc"; + encryption = EntityAttachment.PGP_MESSAGE; + } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) { + name = "signature.asc"; + encryption = EntityAttachment.PGP_SIGNATURE; + } else + throw new IllegalStateException(data.getAction()); + + EntityAttachment attachment = new EntityAttachment(); + attachment.message = id; + attachment.sequence = db.attachment().getAttachmentSequence(id) + 1; + attachment.name = name; + attachment.type = "application/octet-stream"; + attachment.disposition = Part.INLINE; + attachment.encryption = encryption; + attachment.id = db.attachment().insertAttachment(attachment); + + File file = attachment.getFile(context); + + if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) { + byte[] bytes = result.getByteArrayExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE); + Log.i("Writing " + file + " size=" + bytes.length); + try (OutputStream out = new FileOutputStream(file)) { + out.write(bytes); + } + db.attachment().setDownloaded(attachment.id, (long) bytes.length); + } else { + Log.i("Writing " + file + " size=" + encrypted.length()); + Helper.copy(encrypted, file); + db.attachment().setDownloaded(attachment.id, file.length()); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } - db.attachment().setDownloaded(attachment.id, (long) bytes.length); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(data.getAction())) { + pgpKeyIds = result.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); + Log.i("Keys=" + pgpKeyIds.length); - if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(data.getAction())) { - pgpKeyIds = result.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); - Log.i("Keys=" + pgpKeyIds.length); + // Send without encryption + if (pgpKeyIds.length == 0) + return null; - // Send without encryption - if (pgpKeyIds.length == 0) - return null; + // Get encrypt key + Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY); + intent.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyIds[0]); + intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + return intent; + } else if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction())) { + if (identity.sign_key != null) { + // Encrypt message + Intent intent = new Intent(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT); + intent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, pgpKeyIds); + intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, identity.sign_key); + intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + return intent; + } else { + // Get sign key + return new Intent(OpenPgpApi.ACTION_GET_SIGN_KEY_ID); + } + } else if (OpenPgpApi.ACTION_GET_SIGN_KEY_ID.equals(data.getAction())) { + pgpSignKeyId = result.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, -1); + db.identity().setIdentitySignKey(identity.id, pgpSignKeyId); - // Get encrypt key - Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY); - intent.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyIds[0]); - intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - return intent; - } else if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction())) { - if (identity.sign_key != null) { // Encrypt message Intent intent = new Intent(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT); intent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, pgpKeyIds); - intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, identity.sign_key); + intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, pgpSignKeyId); intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); return intent; + } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction())) { + // Get signature + Intent intent = new Intent(OpenPgpApi.ACTION_DETACHED_SIGN); + intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, pgpSignKeyId); + return null; } else { - // Get sign key - return new Intent(OpenPgpApi.ACTION_GET_SIGN_KEY_ID); + // send message + return null; } - } else if (OpenPgpApi.ACTION_GET_SIGN_KEY_ID.equals(data.getAction())) { - pgpSignKeyId = result.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, -1); - db.identity().setIdentitySignKey(identity.id, pgpSignKeyId); - - // Encrypt message - Intent intent = new Intent(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT); - intent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, pgpKeyIds); - intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, pgpSignKeyId); - intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - return intent; - } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction())) { - // Get signature - Intent intent = new Intent(OpenPgpApi.ACTION_DETACHED_SIGN); - intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, pgpSignKeyId); - return null; - } else { - // send message - return null; - } - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); - case OpenPgpApi.RESULT_CODE_ERROR: - db.identity().setIdentitySignKey(identity.id, null); - OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - throw new IllegalArgumentException( - "OpenPgp" + - " error " + (error == null ? "?" : error.getErrorId()) + - ": " + (error == null ? "?" : error.getMessage())); + case OpenPgpApi.RESULT_CODE_ERROR: + db.identity().setIdentitySignKey(identity.id, null); + OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); + throw new IllegalArgumentException( + "OpenPgp" + + " error " + (error == null ? "?" : error.getErrorId()) + + ": " + (error == null ? "?" : error.getMessage())); - default: - throw new IllegalArgumentException("Unknown result code=" + resultCode); + default: + throw new IllegalArgumentException("Unknown result code=" + resultCode); + } + } finally { + encrypted.delete(); } } diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index 9e69b045a8..b25e030f34 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -115,7 +115,6 @@ import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -3841,23 +3840,23 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. Intent data = args.getParcelable("data"); DB db = DB.getInstance(context); + List attachments = db.attachment().getAttachments(id); + InputStream in = null; boolean inline = false; - InputStream encrypted = null; // Find encrypted data - List attachments = db.attachment().getAttachments(id); for (EntityAttachment attachment : attachments) if (EntityAttachment.PGP_MESSAGE.equals(attachment.encryption)) { if (!attachment.available) throw new IllegalArgumentException(context.getString(R.string.title_attachments_missing)); File file = attachment.getFile(context); - encrypted = new FileInputStream(file); + in = new FileInputStream(file); break; } - if (encrypted == null) { + if (in == null) { EntityMessage message = db.message().getMessage(id); if (message != null && message.content) { File file = message.getFile(context); @@ -3876,106 +3875,117 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. section = TextUtils.join("\n\r", disarmored); inline = true; - encrypted = new ByteArrayInputStream(section.getBytes()); + in = new ByteArrayInputStream(section.getBytes()); } } } } - if (encrypted == null) + if (in == null) throw new IllegalArgumentException(context.getString(R.string.title_not_encrypted)); - ByteArrayOutputStream decrypted = new ByteArrayOutputStream(); - - // Decrypt message - OpenPgpApi api = new OpenPgpApi(context, pgpService.getService()); - Intent result = api.executeApi(data, encrypted, decrypted); + Intent result; + File plain = File.createTempFile("plain", "." + id, context.getCacheDir()); + try { + // Decrypt message + try { + try (OutputStream out = new FileOutputStream(plain)) { + OpenPgpApi api = new OpenPgpApi(context, pgpService.getService()); + result = api.executeApi(data, in, out); + } + } finally { + in.close(); + } - Log.i("PGP result=" + result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)); - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - EntityMessage message = db.message().getMessage(id); - if (message == null) - return null; + Log.i("PGP result=" + result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)); + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + EntityMessage message = db.message().getMessage(id); + if (message == null) + return null; - if (inline) { - try { - db.beginTransaction(); + if (inline) { + try { + db.beginTransaction(); - // Write decrypted body - Helper.writeText(message.getFile(context), decrypted.toString()); + // Write decrypted body + Helper.copy(plain, message.getFile(context)); + db.message().setMessageStored(id, new Date().getTime()); - db.message().setMessageStored(id, new Date().getTime()); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + } else { + // Decode message + MessageHelper.MessageParts parts; + Properties props = MessageHelper.getSessionProperties(); + Session isession = Session.getInstance(props, null); + try (InputStream fis = new FileInputStream(plain)) { + MimeMessage imessage = new MimeMessage(isession, fis); + MessageHelper helper = new MessageHelper(imessage); + parts = helper.getMessageParts(); + } - } else { - // Decode message - Properties props = MessageHelper.getSessionProperties(); - Session isession = Session.getInstance(props, null); - ByteArrayInputStream is = new ByteArrayInputStream(decrypted.toByteArray()); - MimeMessage imessage = new MimeMessage(isession, is); - MessageHelper helper = new MessageHelper(imessage); - MessageHelper.MessageParts parts = helper.getMessageParts(); - - try { - db.beginTransaction(); - - // Write decrypted body - String html = parts.getHtml(context); - Helper.writeText(message.getFile(context), html); - - // Remove previously decrypted attachments - for (EntityAttachment local : attachments) - if (local.encryption == null) - db.attachment().deleteAttachment(local.id); - - int sequence = db.attachment().getAttachmentSequence(id); - - // Add decrypted attachments - List remotes = parts.getAttachments(); - for (int index = 0; index < remotes.size(); index++) { - EntityAttachment remote = remotes.get(index); - remote.message = id; - remote.sequence = ++sequence; - remote.id = db.attachment().insertAttachment(remote); - try { - parts.downloadAttachment(context, index, remote); - } catch (Throwable ex) { - Log.e(ex); + try { + db.beginTransaction(); + + // Write decrypted body + String html = parts.getHtml(context); + Helper.writeText(message.getFile(context), html); + + // Remove previously decrypted attachments + for (EntityAttachment local : attachments) + if (local.encryption == null) + db.attachment().deleteAttachment(local.id); + + int sequence = db.attachment().getAttachmentSequence(id); + + // Add decrypted attachments + List remotes = parts.getAttachments(); + for (int index = 0; index < remotes.size(); index++) { + EntityAttachment remote = remotes.get(index); + remote.message = id; + remote.sequence = ++sequence; + remote.id = db.attachment().insertAttachment(remote); + try { + parts.downloadAttachment(context, index, remote); + } catch (Throwable ex) { + Log.e(ex); + } } - } - db.message().setMessageStored(id, new Date().getTime()); + db.message().setMessageStored(id, new Date().getTime()); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } } - } - // Check signature status - OpenPgpSignatureResult sigResult = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); - int sresult = (sigResult == null ? RESULT_NO_SIGNATURE : sigResult.getResult()); - if (sresult == RESULT_NO_SIGNATURE) - Snackbar.make(view, R.string.title_signature_none, Snackbar.LENGTH_LONG).show(); - else if (sresult == RESULT_VALID_KEY_CONFIRMED) - Snackbar.make(view, R.string.title_signature_valid, Snackbar.LENGTH_LONG).show(); - else - db.message().setMessageError(id, getString(R.string.title_signature_invalid)); + // Check signature status + OpenPgpSignatureResult sigResult = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); + int sresult = (sigResult == null ? RESULT_NO_SIGNATURE : sigResult.getResult()); + if (sresult == RESULT_NO_SIGNATURE) + Snackbar.make(view, R.string.title_signature_none, Snackbar.LENGTH_LONG).show(); + else if (sresult == RESULT_VALID_KEY_CONFIRMED) + Snackbar.make(view, R.string.title_signature_valid, Snackbar.LENGTH_LONG).show(); + else + db.message().setMessageError(id, getString(R.string.title_signature_invalid)); - break; + break; - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - FragmentMessages.this.message = id; - return result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + FragmentMessages.this.message = id; + return result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); - case OpenPgpApi.RESULT_CODE_ERROR: - OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - throw new IllegalArgumentException(error.getMessage()); + case OpenPgpApi.RESULT_CODE_ERROR: + OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); + throw new IllegalArgumentException(error.getMessage()); + } + } finally { + plain.delete(); } return null;