allow setting custom headers in autocrypt setup message
This commit is contained in:
parent
a63aca623d
commit
8edd084212
|
@ -26,8 +26,10 @@ import java.io.OutputStream;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
@ -84,6 +86,9 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||
private static final int INDEX_MASTER_KEY_ID = 0;
|
||||
private static final int INDEX_HAS_ANY_SECRET = 1;
|
||||
|
||||
// this is a very simple matcher, we only need basic sanitization
|
||||
private static final Pattern HEADER_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+: [^\\n]+");
|
||||
|
||||
public BackupOperation(Context context, KeyRepository keyRepository, Progressable
|
||||
progressable) {
|
||||
super(context, keyRepository, progressable);
|
||||
|
@ -130,7 +135,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||
|
||||
CountingOutputStream outStream = new CountingOutputStream(new BufferedOutputStream(plainOut));
|
||||
boolean backupSuccess = exportKeysToStream(log, backupInput.getMasterKeyIds(),
|
||||
backupInput.getExportSecret(), backupInput.getExportPublic(), outStream);
|
||||
backupInput.getExportSecret(), backupInput.getExportPublic(), outStream, backupInput.getExtraHeaders());
|
||||
|
||||
if (!backupSuccess) {
|
||||
// if there was an error, it will be in the log so we just have to return
|
||||
|
@ -215,7 +220,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||
}
|
||||
|
||||
boolean exportKeysToStream(OperationLog log, long[] masterKeyIds, boolean exportSecret, boolean exportPublic,
|
||||
OutputStream outStream) {
|
||||
OutputStream outStream, List<String> extraSecretKeyHeaders) {
|
||||
// noinspection unused TODO use these in a log entry
|
||||
int okSecret = 0, okPublic = 0;
|
||||
|
||||
|
@ -253,9 +258,10 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||
boolean hasSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) > 0;
|
||||
if (exportSecret && hasSecret) {
|
||||
log.add(LogType.MSG_BACKUP_SECRET, 2, KeyFormattingUtils.beautifyKeyId(masterKeyId));
|
||||
if (writeSecretKeyToStream(masterKeyId, log, outStream)) {
|
||||
if (writeSecretKeyToStream(masterKeyId, log, outStream, extraSecretKeyHeaders)) {
|
||||
okSecret += 1;
|
||||
}
|
||||
extraSecretKeyHeaders = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,12 +306,17 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean writeSecretKeyToStream(long masterKeyId, OperationLog log, OutputStream outStream)
|
||||
private boolean writeSecretKeyToStream(long masterKeyId, OperationLog log, OutputStream outStream,
|
||||
List<String> extraSecretKeyHeaders)
|
||||
throws IOException {
|
||||
ArmoredOutputStream arOutStream = null;
|
||||
|
||||
try {
|
||||
arOutStream = new ArmoredOutputStream(outStream);
|
||||
if (extraSecretKeyHeaders != null) {
|
||||
addExtraHeadersToStream(arOutStream, extraSecretKeyHeaders);
|
||||
}
|
||||
|
||||
byte[] data = mKeyRepository.loadSecretKeyRingData(masterKeyId);
|
||||
UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(data);
|
||||
CanonicalizedSecretKeyRing ring = (CanonicalizedSecretKeyRing) uncachedKeyRing.canonicalize(log, 2, true);
|
||||
|
@ -320,6 +331,16 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void addExtraHeadersToStream(ArmoredOutputStream arOutStream, List<String> headers) {
|
||||
for (String header : headers) {
|
||||
if (!HEADER_PATTERN.matcher(header).matches()) {
|
||||
throw new IllegalArgumentException("bad header format");
|
||||
}
|
||||
int sep = header.indexOf(':');
|
||||
arOutStream.setHeader(header.substring(0, sep), header.substring(sep + 2));
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor queryForKeys(long[] masterKeyIds) {
|
||||
String selection = null, selectionArgs[] = null;
|
||||
|
||||
|
|
|
@ -829,10 +829,12 @@ public class OpenPgpService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
List<String> headerLines = data.getStringArrayListExtra(OpenPgpApi.EXTRA_CUSTOM_HEADERS);
|
||||
|
||||
Passphrase autocryptTransferCode = Numeric9x4PassphraseUtil.generateNumeric9x4Passphrase();
|
||||
CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel(autocryptTransferCode);
|
||||
|
||||
BackupKeyringParcel input = BackupKeyringParcel.createExportAutocryptSetupMessage(masterKeyIds);
|
||||
BackupKeyringParcel input = BackupKeyringParcel.createExportAutocryptSetupMessage(masterKeyIds, headerLines);
|
||||
BackupOperation op = new BackupOperation(this, mKeyRepository, null);
|
||||
ExportResult pgpResult = op.execute(input, inputParcel, outputStream);
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
package org.sufficientlysecure.keychain.service;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
|
@ -36,15 +38,24 @@ public abstract class BackupKeyringParcel implements Parcelable {
|
|||
public abstract boolean getEnableAsciiArmorOutput();
|
||||
@Nullable
|
||||
public abstract Uri getOutputUri();
|
||||
@Nullable
|
||||
public abstract List<String> getExtraHeaders();
|
||||
|
||||
public static BackupKeyringParcel create(long[] masterKeyIds, boolean exportSecret,
|
||||
boolean isEncrypted, boolean enableAsciiArmorOutput, Uri outputUri) {
|
||||
return new AutoValue_BackupKeyringParcel(
|
||||
masterKeyIds, exportSecret, true, isEncrypted, enableAsciiArmorOutput, outputUri);
|
||||
masterKeyIds, exportSecret, true, isEncrypted, enableAsciiArmorOutput, outputUri, null);
|
||||
}
|
||||
|
||||
public static BackupKeyringParcel createExportAutocryptSetupMessage(long[] masterKeyIds) {
|
||||
public static BackupKeyringParcel create(long[] masterKeyIds, boolean exportSecret,
|
||||
boolean isEncrypted, boolean enableAsciiArmorOutput, Uri outputUri, List<String> extraHeaders) {
|
||||
return new AutoValue_BackupKeyringParcel(
|
||||
masterKeyIds, true, false, true, true, null);
|
||||
masterKeyIds, exportSecret, true, isEncrypted, enableAsciiArmorOutput, outputUri, extraHeaders);
|
||||
}
|
||||
|
||||
public static BackupKeyringParcel createExportAutocryptSetupMessage(long[] masterKeyIds,
|
||||
List<String> extraHeaders) {
|
||||
return new AutoValue_BackupKeyringParcel(
|
||||
masterKeyIds, true, false, true, true, null, extraHeaders);
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import java.io.PipedInputStream;
|
|||
import java.io.PipedOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import android.app.Application;
|
||||
|
@ -157,7 +158,7 @@ public class BackupOperationTest {
|
|||
assertTrue("second keyring has local certification", checkForLocal(mStaticRing2));
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
boolean result = op.exportKeysToStream(new OperationLog(), null, false, true, out);
|
||||
boolean result = op.exportKeysToStream(new OperationLog(), null, false, true, out, null);
|
||||
|
||||
assertTrue("export must be a success", result);
|
||||
|
||||
|
@ -194,7 +195,7 @@ public class BackupOperationTest {
|
|||
}
|
||||
|
||||
out = new ByteArrayOutputStream();
|
||||
result = op.exportKeysToStream(new OperationLog(), null, true, true, out);
|
||||
result = op.exportKeysToStream(new OperationLog(), null, true, true, out, null);
|
||||
|
||||
assertTrue("export must be a success", result);
|
||||
|
||||
|
@ -238,6 +239,22 @@ public class BackupOperationTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportWithExtraHeaders() throws Exception {
|
||||
BackupOperation op = new BackupOperation(RuntimeEnvironment.application,
|
||||
KeyWritableRepository.create(RuntimeEnvironment.application), null);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
boolean result = op.exportKeysToStream(
|
||||
new OperationLog(), new long[] { mStaticRing1.getMasterKeyId() }, true, false,
|
||||
out, Arrays.asList("header: value"));
|
||||
|
||||
assertTrue(result);
|
||||
|
||||
String resultData = new String(out.toByteArray());
|
||||
assertTrue(resultData.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\nheader: value\n\n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportUnencrypted() throws Exception {
|
||||
ContentResolver mockResolver = mock(ContentResolver.class);
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3631265b348a02ea3c79f4f38f6c031a82298ed5
|
||||
Subproject commit c2ddaa76bbb8819dafff55ae4af00ac40c94e6fb
|
Loading…
Reference in New Issue