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 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 {
|
||||
|
||||
@@ -142,6 +142,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
||||
PgpSignEncryptInputParcel inputParcel = new PgpSignEncryptInputParcel();
|
||||
inputParcel.setSymmetricPassphrase(exportInput.mSymmetricPassphrase);
|
||||
inputParcel.setEnableAsciiArmorOutput(true);
|
||||
inputParcel.setAddBackupHeader(true);
|
||||
|
||||
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_ASYM (LogLevel.DEBUG, R.string.msg_dc_asym),
|
||||
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_DECOMPRESS (LogLevel.DEBUG, R.string.msg_dc_clear_decompress),
|
||||
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.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
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. */
|
||||
@NonNull
|
||||
private DecryptVerifyResult decryptVerify(
|
||||
@@ -215,23 +267,12 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
int currentProgress = 0;
|
||||
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
|
||||
// https://tools.ietf.org/html/rfc4880#page56
|
||||
String charset = null;
|
||||
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 && "charset".equalsIgnoreCase(pieces[0])) {
|
||||
charset = pieces[1].trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (charset != null) {
|
||||
log.add(LogType.MSG_DC_CHARSET, indent, charset);
|
||||
}
|
||||
}
|
||||
// parse ASCII Armor headers
|
||||
ArmorHeaders armorHeaders = parseArmorHeaders(in, log, indent);
|
||||
String charset = armorHeaders.charset;
|
||||
boolean useBackupCode = false;
|
||||
if (armorHeaders.backupVersion != null && armorHeaders.backupVersion == 1) {
|
||||
useBackupCode = true;
|
||||
}
|
||||
|
||||
OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
|
||||
@@ -245,7 +286,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
|
||||
if (obj instanceof PGPEncryptedDataList) {
|
||||
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 (esResult.errorResult != null) {
|
||||
@@ -477,7 +519,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -609,8 +651,11 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
|
||||
if (passphrase == null) {
|
||||
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
|
||||
RequiredInputParcel requiredInputParcel = useBackupCode ?
|
||||
RequiredInputParcel.createRequiredBackupCode() :
|
||||
RequiredInputParcel.createRequiredSymmetricPassphrase();
|
||||
return result.with(new DecryptVerifyResult(log,
|
||||
RequiredInputParcel.createRequiredSymmetricPassphrase(),
|
||||
requiredInputParcel,
|
||||
cryptoInput));
|
||||
}
|
||||
|
||||
@@ -653,8 +698,10 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
||||
result.cleartextStream = encryptedDataSymmetric.getDataStream(decryptorFactory);
|
||||
} catch (PGPDataValidationException e) {
|
||||
log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent + 1);
|
||||
return result.with(new DecryptVerifyResult(log,
|
||||
RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput));
|
||||
RequiredInputParcel requiredInputParcel = useBackupCode ?
|
||||
RequiredInputParcel.createRequiredBackupCode() :
|
||||
RequiredInputParcel.createRequiredSymmetricPassphrase();
|
||||
return result.with(new DecryptVerifyResult(log, requiredInputParcel, cryptoInput));
|
||||
}
|
||||
|
||||
result.encryptedData = encryptedDataSymmetric;
|
||||
|
||||
@@ -44,6 +44,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
||||
protected boolean mDetachedSignature = false;
|
||||
protected boolean mHiddenRecipients = false;
|
||||
protected boolean mIntegrityProtected = true;
|
||||
protected boolean mAddBackupHeader = false;
|
||||
|
||||
public PgpSignEncryptInputParcel() {
|
||||
|
||||
@@ -70,6 +71,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
||||
mDetachedSignature = source.readInt() == 1;
|
||||
mHiddenRecipients = source.readInt() == 1;
|
||||
mIntegrityProtected = source.readInt() == 1;
|
||||
mAddBackupHeader = source.readInt() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,6 +102,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
||||
dest.writeInt(mDetachedSignature ? 1 : 0);
|
||||
dest.writeInt(mHiddenRecipients ? 1 : 0);
|
||||
dest.writeInt(mIntegrityProtected ? 1 : 0);
|
||||
dest.writeInt(mAddBackupHeader ? 1 : 0);
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
@@ -244,6 +247,15 @@ public class PgpSignEncryptInputParcel implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PgpSignEncryptInputParcel setAddBackupHeader(boolean addBackupHeader) {
|
||||
this.mAddBackupHeader = addBackupHeader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isAddBackupHeader() {
|
||||
return mAddBackupHeader;
|
||||
}
|
||||
|
||||
public boolean isHiddenRecipients() {
|
||||
return mHiddenRecipients;
|
||||
}
|
||||
|
||||
@@ -149,6 +149,10 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
if (input.getCharset() != null) {
|
||||
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;
|
||||
} else {
|
||||
out = outputStream;
|
||||
|
||||
@@ -14,8 +14,8 @@ import java.util.Date;
|
||||
public class RequiredInputParcel implements Parcelable {
|
||||
|
||||
public enum RequiredInputType {
|
||||
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT,
|
||||
UPLOAD_FAIL_RETRY
|
||||
PASSPHRASE, PASSPHRASE_SYMMETRIC, BACKUP_CODE, NFC_SIGN, NFC_DECRYPT,
|
||||
NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT, UPLOAD_FAIL_RETRY
|
||||
}
|
||||
|
||||
public Date mSignatureTime;
|
||||
@@ -117,6 +117,11 @@ public class RequiredInputParcel implements Parcelable {
|
||||
null, null, null, null, null);
|
||||
}
|
||||
|
||||
public static RequiredInputParcel createRequiredBackupCode() {
|
||||
return new RequiredInputParcel(RequiredInputType.BACKUP_CODE,
|
||||
null, null, null, null, null);
|
||||
}
|
||||
|
||||
public static RequiredInputParcel createRequiredPassphrase(
|
||||
RequiredInputParcel req) {
|
||||
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_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_backup_version">"Found backupVersion header: '%s'"</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_meta_file">"Filename: %s"</string>
|
||||
|
||||
Reference in New Issue
Block a user