allow setting custom headers in autocrypt setup message
This commit is contained in:
@@ -26,8 +26,10 @@ import java.io.OutputStream;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
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_MASTER_KEY_ID = 0;
|
||||||
private static final int INDEX_HAS_ANY_SECRET = 1;
|
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
|
public BackupOperation(Context context, KeyRepository keyRepository, Progressable
|
||||||
progressable) {
|
progressable) {
|
||||||
super(context, keyRepository, progressable);
|
super(context, keyRepository, progressable);
|
||||||
@@ -130,7 +135,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||||||
|
|
||||||
CountingOutputStream outStream = new CountingOutputStream(new BufferedOutputStream(plainOut));
|
CountingOutputStream outStream = new CountingOutputStream(new BufferedOutputStream(plainOut));
|
||||||
boolean backupSuccess = exportKeysToStream(log, backupInput.getMasterKeyIds(),
|
boolean backupSuccess = exportKeysToStream(log, backupInput.getMasterKeyIds(),
|
||||||
backupInput.getExportSecret(), backupInput.getExportPublic(), outStream);
|
backupInput.getExportSecret(), backupInput.getExportPublic(), outStream, backupInput.getExtraHeaders());
|
||||||
|
|
||||||
if (!backupSuccess) {
|
if (!backupSuccess) {
|
||||||
// if there was an error, it will be in the log so we just have to return
|
// 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,
|
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
|
// noinspection unused TODO use these in a log entry
|
||||||
int okSecret = 0, okPublic = 0;
|
int okSecret = 0, okPublic = 0;
|
||||||
|
|
||||||
@@ -253,9 +258,10 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||||||
boolean hasSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) > 0;
|
boolean hasSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) > 0;
|
||||||
if (exportSecret && hasSecret) {
|
if (exportSecret && hasSecret) {
|
||||||
log.add(LogType.MSG_BACKUP_SECRET, 2, KeyFormattingUtils.beautifyKeyId(masterKeyId));
|
log.add(LogType.MSG_BACKUP_SECRET, 2, KeyFormattingUtils.beautifyKeyId(masterKeyId));
|
||||||
if (writeSecretKeyToStream(masterKeyId, log, outStream)) {
|
if (writeSecretKeyToStream(masterKeyId, log, outStream, extraSecretKeyHeaders)) {
|
||||||
okSecret += 1;
|
okSecret += 1;
|
||||||
}
|
}
|
||||||
|
extraSecretKeyHeaders = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,12 +306,17 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||||||
return true;
|
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 {
|
throws IOException {
|
||||||
ArmoredOutputStream arOutStream = null;
|
ArmoredOutputStream arOutStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
arOutStream = new ArmoredOutputStream(outStream);
|
arOutStream = new ArmoredOutputStream(outStream);
|
||||||
|
if (extraSecretKeyHeaders != null) {
|
||||||
|
addExtraHeadersToStream(arOutStream, extraSecretKeyHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
byte[] data = mKeyRepository.loadSecretKeyRingData(masterKeyId);
|
byte[] data = mKeyRepository.loadSecretKeyRingData(masterKeyId);
|
||||||
UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(data);
|
UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(data);
|
||||||
CanonicalizedSecretKeyRing ring = (CanonicalizedSecretKeyRing) uncachedKeyRing.canonicalize(log, 2, true);
|
CanonicalizedSecretKeyRing ring = (CanonicalizedSecretKeyRing) uncachedKeyRing.canonicalize(log, 2, true);
|
||||||
@@ -320,6 +331,16 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
|
|||||||
return true;
|
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) {
|
private Cursor queryForKeys(long[] masterKeyIds) {
|
||||||
String selection = null, selectionArgs[] = null;
|
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();
|
Passphrase autocryptTransferCode = Numeric9x4PassphraseUtil.generateNumeric9x4Passphrase();
|
||||||
CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel(autocryptTransferCode);
|
CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel(autocryptTransferCode);
|
||||||
|
|
||||||
BackupKeyringParcel input = BackupKeyringParcel.createExportAutocryptSetupMessage(masterKeyIds);
|
BackupKeyringParcel input = BackupKeyringParcel.createExportAutocryptSetupMessage(masterKeyIds, headerLines);
|
||||||
BackupOperation op = new BackupOperation(this, mKeyRepository, null);
|
BackupOperation op = new BackupOperation(this, mKeyRepository, null);
|
||||||
ExportResult pgpResult = op.execute(input, inputParcel, outputStream);
|
ExportResult pgpResult = op.execute(input, inputParcel, outputStream);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
package org.sufficientlysecure.keychain.service;
|
package org.sufficientlysecure.keychain.service;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
@@ -36,15 +38,24 @@ public abstract class BackupKeyringParcel implements Parcelable {
|
|||||||
public abstract boolean getEnableAsciiArmorOutput();
|
public abstract boolean getEnableAsciiArmorOutput();
|
||||||
@Nullable
|
@Nullable
|
||||||
public abstract Uri getOutputUri();
|
public abstract Uri getOutputUri();
|
||||||
|
@Nullable
|
||||||
|
public abstract List<String> getExtraHeaders();
|
||||||
|
|
||||||
public static BackupKeyringParcel create(long[] masterKeyIds, boolean exportSecret,
|
public static BackupKeyringParcel create(long[] masterKeyIds, boolean exportSecret,
|
||||||
boolean isEncrypted, boolean enableAsciiArmorOutput, Uri outputUri) {
|
boolean isEncrypted, boolean enableAsciiArmorOutput, Uri outputUri) {
|
||||||
return new AutoValue_BackupKeyringParcel(
|
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(
|
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.PipedOutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
@@ -157,7 +158,7 @@ public class BackupOperationTest {
|
|||||||
assertTrue("second keyring has local certification", checkForLocal(mStaticRing2));
|
assertTrue("second keyring has local certification", checkForLocal(mStaticRing2));
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
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);
|
assertTrue("export must be a success", result);
|
||||||
|
|
||||||
@@ -194,7 +195,7 @@ public class BackupOperationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out = new ByteArrayOutputStream();
|
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);
|
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
|
@Test
|
||||||
public void testExportUnencrypted() throws Exception {
|
public void testExportUnencrypted() throws Exception {
|
||||||
ContentResolver mockResolver = mock(ContentResolver.class);
|
ContentResolver mockResolver = mock(ContentResolver.class);
|
||||||
|
|||||||
2
extern/openpgp-api-lib
vendored
2
extern/openpgp-api-lib
vendored
Submodule extern/openpgp-api-lib updated: 3631265b34...c2ddaa76bb
Reference in New Issue
Block a user