Support backupVersion ASCII Armor header
This commit is contained in:
@@ -82,7 +82,6 @@ public final class Constants {
|
|||||||
|
|
||||||
public static final class Path {
|
public static final class Path {
|
||||||
public static final File APP_DIR = new File(Environment.getExternalStorageDirectory(), "OpenKeychain");
|
public static final File APP_DIR = new File(Environment.getExternalStorageDirectory(), "OpenKeychain");
|
||||||
public static final File APP_DIR_FILE = new File(APP_DIR, "export.asc");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Notification {
|
public static final class Notification {
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||||||
PgpSignEncryptInputParcel inputParcel = new PgpSignEncryptInputParcel();
|
PgpSignEncryptInputParcel inputParcel = new PgpSignEncryptInputParcel();
|
||||||
inputParcel.setSymmetricPassphrase(exportInput.mSymmetricPassphrase);
|
inputParcel.setSymmetricPassphrase(exportInput.mSymmetricPassphrase);
|
||||||
inputParcel.setEnableAsciiArmorOutput(true);
|
inputParcel.setEnableAsciiArmorOutput(true);
|
||||||
|
inputParcel.setAddBackupHeader(true);
|
||||||
|
|
||||||
InputStream inStream = mContext.getContentResolver().openInputStream(exportOutputUri);
|
InputStream inStream = mContext.getContentResolver().openInputStream(exportOutputUri);
|
||||||
|
|
||||||
|
|||||||
@@ -639,6 +639,7 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_DC_ASKIP_NOT_ALLOWED (LogLevel.DEBUG, R.string.msg_dc_askip_not_allowed),
|
MSG_DC_ASKIP_NOT_ALLOWED (LogLevel.DEBUG, R.string.msg_dc_askip_not_allowed),
|
||||||
MSG_DC_ASYM (LogLevel.DEBUG, R.string.msg_dc_asym),
|
MSG_DC_ASYM (LogLevel.DEBUG, R.string.msg_dc_asym),
|
||||||
MSG_DC_CHARSET (LogLevel.DEBUG, R.string.msg_dc_charset),
|
MSG_DC_CHARSET (LogLevel.DEBUG, R.string.msg_dc_charset),
|
||||||
|
MSG_DC_BACKUP_VERSION (LogLevel.DEBUG, R.string.msg_dc_backup_version),
|
||||||
MSG_DC_CLEAR_DATA (LogLevel.DEBUG, R.string.msg_dc_clear_data),
|
MSG_DC_CLEAR_DATA (LogLevel.DEBUG, R.string.msg_dc_clear_data),
|
||||||
MSG_DC_CLEAR_DECOMPRESS (LogLevel.DEBUG, R.string.msg_dc_clear_decompress),
|
MSG_DC_CLEAR_DECOMPRESS (LogLevel.DEBUG, R.string.msg_dc_clear_decompress),
|
||||||
MSG_DC_CLEAR_META_FILE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_file),
|
MSG_DC_CLEAR_META_FILE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_file),
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
||||||
@@ -201,6 +202,57 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ArmorHeaders {
|
||||||
|
String charset = null;
|
||||||
|
Integer backupVersion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArmorHeaders parseArmorHeaders(InputStream in, OperationLog log, int indent) {
|
||||||
|
ArmorHeaders armorHeaders = new ArmorHeaders();
|
||||||
|
|
||||||
|
// If the input stream is armored, and there is a charset specified, take a note for later
|
||||||
|
// https://tools.ietf.org/html/rfc4880#page56
|
||||||
|
if (in instanceof ArmoredInputStream) {
|
||||||
|
ArmoredInputStream aIn = (ArmoredInputStream) in;
|
||||||
|
if (aIn.getArmorHeaders() != null) {
|
||||||
|
for (String header : aIn.getArmorHeaders()) {
|
||||||
|
String[] pieces = header.split(":", 2);
|
||||||
|
if (pieces.length != 2
|
||||||
|
|| TextUtils.isEmpty(pieces[0])
|
||||||
|
|| TextUtils.isEmpty(pieces[1])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pieces[0].toLowerCase()) {
|
||||||
|
case "charset": {
|
||||||
|
armorHeaders.charset = pieces[1].trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "backupversion": {
|
||||||
|
try {
|
||||||
|
armorHeaders.backupVersion = Integer.valueOf(pieces[1].trim());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (armorHeaders.charset != null) {
|
||||||
|
log.add(LogType.MSG_DC_CHARSET, indent, armorHeaders.charset);
|
||||||
|
}
|
||||||
|
if (armorHeaders.backupVersion != null) {
|
||||||
|
log.add(LogType.MSG_DC_BACKUP_VERSION, indent, armorHeaders.backupVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return armorHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
/** Decrypt and/or verify binary or ascii armored pgp data. */
|
/** Decrypt and/or verify binary or ascii armored pgp data. */
|
||||||
@NonNull
|
@NonNull
|
||||||
private DecryptVerifyResult decryptVerify(
|
private DecryptVerifyResult decryptVerify(
|
||||||
@@ -215,23 +267,12 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
int currentProgress = 0;
|
int currentProgress = 0;
|
||||||
updateProgress(R.string.progress_reading_data, currentProgress, 100);
|
updateProgress(R.string.progress_reading_data, currentProgress, 100);
|
||||||
|
|
||||||
// If the input stream is armored, and there is a charset specified, take a note for later
|
// parse ASCII Armor headers
|
||||||
// https://tools.ietf.org/html/rfc4880#page56
|
ArmorHeaders armorHeaders = parseArmorHeaders(in, log, indent);
|
||||||
String charset = null;
|
String charset = armorHeaders.charset;
|
||||||
if (in instanceof ArmoredInputStream) {
|
boolean useBackupCode = false;
|
||||||
ArmoredInputStream aIn = (ArmoredInputStream) in;
|
if (armorHeaders.backupVersion != null && armorHeaders.backupVersion == 1) {
|
||||||
if (aIn.getArmorHeaders() != null) {
|
useBackupCode = true;
|
||||||
for (String header : aIn.getArmorHeaders()) {
|
|
||||||
String[] pieces = header.split(":", 2);
|
|
||||||
if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) {
|
|
||||||
charset = pieces[1].trim();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (charset != null) {
|
|
||||||
log.add(LogType.MSG_DC_CHARSET, indent, charset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
|
OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
|
||||||
@@ -245,7 +286,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
if (obj instanceof PGPEncryptedDataList) {
|
if (obj instanceof PGPEncryptedDataList) {
|
||||||
esResult = handleEncryptedPacket(
|
esResult = handleEncryptedPacket(
|
||||||
input, cryptoInput, (PGPEncryptedDataList) obj, log, indent, currentProgress);
|
input, cryptoInput, (PGPEncryptedDataList) obj, log, indent,
|
||||||
|
currentProgress, useBackupCode);
|
||||||
|
|
||||||
// if there is an error, nothing left to do here
|
// if there is an error, nothing left to do here
|
||||||
if (esResult.errorResult != null) {
|
if (esResult.errorResult != null) {
|
||||||
@@ -477,7 +519,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private EncryptStreamResult handleEncryptedPacket(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
|
private EncryptStreamResult handleEncryptedPacket(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
|
||||||
PGPEncryptedDataList enc, OperationLog log, int indent, int currentProgress) throws PGPException {
|
PGPEncryptedDataList enc, OperationLog log, int indent, int currentProgress, boolean useBackupCode) throws PGPException {
|
||||||
|
|
||||||
EncryptStreamResult result = new EncryptStreamResult();
|
EncryptStreamResult result = new EncryptStreamResult();
|
||||||
|
|
||||||
@@ -609,8 +651,11 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
|
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
|
||||||
|
RequiredInputParcel requiredInputParcel = useBackupCode ?
|
||||||
|
RequiredInputParcel.createRequiredBackupCode() :
|
||||||
|
RequiredInputParcel.createRequiredSymmetricPassphrase();
|
||||||
return result.with(new DecryptVerifyResult(log,
|
return result.with(new DecryptVerifyResult(log,
|
||||||
RequiredInputParcel.createRequiredSymmetricPassphrase(),
|
requiredInputParcel,
|
||||||
cryptoInput));
|
cryptoInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,8 +698,10 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
result.cleartextStream = encryptedDataSymmetric.getDataStream(decryptorFactory);
|
result.cleartextStream = encryptedDataSymmetric.getDataStream(decryptorFactory);
|
||||||
} catch (PGPDataValidationException e) {
|
} catch (PGPDataValidationException e) {
|
||||||
log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent + 1);
|
log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent + 1);
|
||||||
return result.with(new DecryptVerifyResult(log,
|
RequiredInputParcel requiredInputParcel = useBackupCode ?
|
||||||
RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput));
|
RequiredInputParcel.createRequiredBackupCode() :
|
||||||
|
RequiredInputParcel.createRequiredSymmetricPassphrase();
|
||||||
|
return result.with(new DecryptVerifyResult(log, requiredInputParcel, cryptoInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
result.encryptedData = encryptedDataSymmetric;
|
result.encryptedData = encryptedDataSymmetric;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
|||||||
protected boolean mDetachedSignature = false;
|
protected boolean mDetachedSignature = false;
|
||||||
protected boolean mHiddenRecipients = false;
|
protected boolean mHiddenRecipients = false;
|
||||||
protected boolean mIntegrityProtected = true;
|
protected boolean mIntegrityProtected = true;
|
||||||
|
protected boolean mAddBackupHeader = false;
|
||||||
|
|
||||||
public PgpSignEncryptInputParcel() {
|
public PgpSignEncryptInputParcel() {
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
|||||||
mDetachedSignature = source.readInt() == 1;
|
mDetachedSignature = source.readInt() == 1;
|
||||||
mHiddenRecipients = source.readInt() == 1;
|
mHiddenRecipients = source.readInt() == 1;
|
||||||
mIntegrityProtected = source.readInt() == 1;
|
mIntegrityProtected = source.readInt() == 1;
|
||||||
|
mAddBackupHeader = source.readInt() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -100,6 +102,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
|||||||
dest.writeInt(mDetachedSignature ? 1 : 0);
|
dest.writeInt(mDetachedSignature ? 1 : 0);
|
||||||
dest.writeInt(mHiddenRecipients ? 1 : 0);
|
dest.writeInt(mHiddenRecipients ? 1 : 0);
|
||||||
dest.writeInt(mIntegrityProtected ? 1 : 0);
|
dest.writeInt(mIntegrityProtected ? 1 : 0);
|
||||||
|
dest.writeInt(mAddBackupHeader ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCharset() {
|
public String getCharset() {
|
||||||
@@ -244,6 +247,15 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PgpSignEncryptInputParcel setAddBackupHeader(boolean addBackupHeader) {
|
||||||
|
this.mAddBackupHeader = addBackupHeader;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddBackupHeader() {
|
||||||
|
return mAddBackupHeader;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isHiddenRecipients() {
|
public boolean isHiddenRecipients() {
|
||||||
return mHiddenRecipients;
|
return mHiddenRecipients;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,10 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
|||||||
if (input.getCharset() != null) {
|
if (input.getCharset() != null) {
|
||||||
armorOut.setHeader("Charset", input.getCharset());
|
armorOut.setHeader("Charset", input.getCharset());
|
||||||
}
|
}
|
||||||
|
// add proprietary header to indicate that this is a key backup
|
||||||
|
if (input.isAddBackupHeader()) {
|
||||||
|
armorOut.setHeader("BackupVersion", "1");
|
||||||
|
}
|
||||||
out = armorOut;
|
out = armorOut;
|
||||||
} else {
|
} else {
|
||||||
out = outputStream;
|
out = outputStream;
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import java.util.Date;
|
|||||||
public class RequiredInputParcel implements Parcelable {
|
public class RequiredInputParcel implements Parcelable {
|
||||||
|
|
||||||
public enum RequiredInputType {
|
public enum RequiredInputType {
|
||||||
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT,
|
PASSPHRASE, PASSPHRASE_SYMMETRIC, BACKUP_CODE, NFC_SIGN, NFC_DECRYPT,
|
||||||
UPLOAD_FAIL_RETRY
|
NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT, UPLOAD_FAIL_RETRY
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date mSignatureTime;
|
public Date mSignatureTime;
|
||||||
@@ -117,6 +117,11 @@ public class RequiredInputParcel implements Parcelable {
|
|||||||
null, null, null, null, null);
|
null, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RequiredInputParcel createRequiredBackupCode() {
|
||||||
|
return new RequiredInputParcel(RequiredInputType.BACKUP_CODE,
|
||||||
|
null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public static RequiredInputParcel createRequiredPassphrase(
|
public static RequiredInputParcel createRequiredPassphrase(
|
||||||
RequiredInputParcel req) {
|
RequiredInputParcel req) {
|
||||||
return new RequiredInputParcel(RequiredInputType.PASSPHRASE,
|
return new RequiredInputParcel(RequiredInputType.PASSPHRASE,
|
||||||
|
|||||||
@@ -1152,6 +1152,7 @@
|
|||||||
<string name="msg_dc_askip_not_allowed">"Data not encrypted with allowed key, skipping…"</string>
|
<string name="msg_dc_askip_not_allowed">"Data not encrypted with allowed key, skipping…"</string>
|
||||||
<string name="msg_dc_asym">"Found block of asymmetrically encrypted data for key %s"</string>
|
<string name="msg_dc_asym">"Found block of asymmetrically encrypted data for key %s"</string>
|
||||||
<string name="msg_dc_charset">"Found charset header: '%s'"</string>
|
<string name="msg_dc_charset">"Found charset header: '%s'"</string>
|
||||||
|
<string name="msg_dc_backup_version">"Found backupVersion header: '%s'"</string>
|
||||||
<string name="msg_dc_clear_data">"Processing literal data"</string>
|
<string name="msg_dc_clear_data">"Processing literal data"</string>
|
||||||
<string name="msg_dc_clear_decompress">"Unpacking compressed data"</string>
|
<string name="msg_dc_clear_decompress">"Unpacking compressed data"</string>
|
||||||
<string name="msg_dc_clear_meta_file">"Filename: %s"</string>
|
<string name="msg_dc_clear_meta_file">"Filename: %s"</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user