Merge pull request #1362 from open-keychain/operation-migration

Migrated all operations to KeychainNewService
This commit is contained in:
Dominik Schürmann
2015-06-30 18:49:46 +02:00
46 changed files with 2646 additions and 2196 deletions

View File

@@ -709,9 +709,6 @@
android:name=".remote.CryptoInputParcelCacheService"
android:exported="false"
android:process=":remote_api" />
<service
android:name=".service.KeychainNewService"
android:exported="false" />
<service
android:name=".service.KeychainService"
android:exported="false" />

View File

@@ -58,12 +58,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @see CertifyActionsParcel
*
*/
public class CertifyOperation extends BaseOperation {
public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
@Override
public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
OperationLog log = new OperationLog();
@@ -86,8 +87,10 @@ public class CertifyOperation extends BaseOperation {
case PATTERN:
case PASSPHRASE:
if (!cryptoInput.hasPassphrase()) {
return new CertifyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
certificationKey.getKeyId(), certificationKey.getKeyId(), null));
return new CertifyResult(log,
RequiredInputParcel.createRequiredSignPassphrase(
certificationKey.getKeyId(), certificationKey.getKeyId(), null)
);
}
// certification is always with the master key id, so use that one
passphrase = cryptoInput.getPassphrase();
@@ -185,10 +188,10 @@ public class CertifyOperation extends BaseOperation {
}
HkpKeyserver keyServer = null;
ImportExportOperation importExportOperation = null;
ExportOperation exportOperation = null;
if (parcel.keyServerUri != null) {
keyServer = new HkpKeyserver(parcel.keyServerUri);
importExportOperation = new ImportExportOperation(mContext, mProviderHelper, mProgressable);
exportOperation = new ExportOperation(mContext, mProviderHelper, mProgressable);
}
// Write all certified keys into the database
@@ -206,10 +209,10 @@ public class CertifyOperation extends BaseOperation {
mProviderHelper.clearLog();
SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey);
if (importExportOperation != null) {
if (exportOperation != null) {
// TODO use subresult, get rid of try/catch!
try {
importExportOperation.uploadKeyRingToServer(keyServer, certifiedKey);
exportOperation.uploadKeyRingToServer(keyServer, certifiedKey);
uploadOk += 1;
} catch (AddKeyException e) {
Log.e(Constants.TAG, "error uploading key", e);

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
public class ConsolidateOperation extends BaseOperation<ConsolidateInputParcel> {
public ConsolidateOperation(Context context, ProviderHelper providerHelper, Progressable
progressable) {
super(context, providerHelper, progressable);
}
@Override
public ConsolidateResult execute(ConsolidateInputParcel consolidateInputParcel,
CryptoInputParcel cryptoInputParcel) {
if (consolidateInputParcel.mConsolidateRecovery) {
return mProviderHelper.consolidateDatabaseStep2(mProgressable);
} else {
return mProviderHelper.consolidateDatabaseStep1(mProgressable);
}
}
}

View File

@@ -27,6 +27,8 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
/** An operation which implements a high level keyring delete operation.
@@ -37,13 +39,18 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
* a list.
*
*/
public class DeleteOperation extends BaseOperation {
public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
public DeleteOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
super(context, providerHelper, progressable);
}
public DeleteResult execute(long[] masterKeyIds, boolean isSecret) {
@Override
public DeleteResult execute(DeleteKeyringParcel deleteKeyringParcel,
CryptoInputParcel cryptoInputParcel) {
long[] masterKeyIds = deleteKeyringParcel.mMasterKeyIds;
boolean isSecret = deleteKeyringParcel.mIsSecret;
OperationLog log = new OperationLog();

View File

@@ -0,0 +1,343 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* An operation class which implements high level export
* operations.
* This class receives a source and/or destination of keys as input and performs
* all steps for this export.
*
* @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
* For the export operation, the input consists of a set of key ids and
* either the name of a file or an output uri to write to.
* TODO rework uploadKeyRingToServer
*/
public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
public ExportOperation(Context context, ProviderHelper providerHelper, Progressable
progressable) {
super(context, providerHelper, progressable);
}
public ExportOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring)
throws AddKeyException {
uploadKeyRingToServer(server, keyring.getUncachedKeyRing());
}
public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws
AddKeyException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
aos = new ArmoredOutputStream(bos);
keyring.encode(aos);
aos.close();
String armoredKey = bos.toString("UTF-8");
server.add(armoredKey);
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
throw new AddKeyException();
} finally {
try {
if (aos != null) {
aos.close();
}
bos.close();
} catch (IOException e) {
// this is just a finally thing, no matter if it doesn't work out.
}
}
}
public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) {
OperationLog log = new OperationLog();
if (masterKeyIds != null) {
log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
} else {
log.add(LogType.MSG_EXPORT_ALL, 0);
}
// do we have a file name?
if (outputFile == null) {
log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
// check if storage is ready
if (!FileHelper.isStorageMounted(outputFile)) {
log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
try {
OutputStream outStream = new FileOutputStream(outputFile);
try {
ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream);
if (result.cancelled()) {
//noinspection ResultOfMethodCallIgnored
new File(outputFile).delete();
}
return result;
} finally {
outStream.close();
}
} catch (IOException e) {
log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
}
public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
OperationLog log = new OperationLog();
if (masterKeyIds != null) {
log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
} else {
log.add(LogType.MSG_EXPORT_ALL, 0);
}
// do we have a file name?
if (outputUri == null) {
log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
try {
OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream
(outputUri);
return exportKeyRings(log, masterKeyIds, exportSecret, outStream);
} catch (FileNotFoundException e) {
log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
}
ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
OutputStream outStream) {
/* TODO isn't this checked above, with the isStorageMounted call?
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
*/
if (!BufferedOutputStream.class.isInstance(outStream)) {
outStream = new BufferedOutputStream(outStream);
}
int okSecret = 0, okPublic = 0, progress = 0;
Cursor cursor = null;
try {
String selection = null, ids[] = null;
if (masterKeyIds != null) {
// generate placeholders and string selection args
ids = new String[masterKeyIds.length];
StringBuilder placeholders = new StringBuilder("?");
for (int i = 0; i < masterKeyIds.length; i++) {
ids[i] = Long.toString(masterKeyIds[i]);
if (i != 0) {
placeholders.append(",?");
}
}
// put together selection string
selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID
+ " IN (" + placeholders + ")";
}
cursor = mProviderHelper.getContentResolver().query(
KeyRings.buildUnifiedKeyRingsUri(), new String[]{
KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA,
KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET
}, selection, ids, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
);
if (cursor == null || !cursor.moveToFirst()) {
log.add(LogType.MSG_EXPORT_ERROR_DB, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
}
int numKeys = cursor.getCount();
updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
numKeys), 0, numKeys);
// For each public masterKey id
while (!cursor.isAfterLast()) {
long keyId = cursor.getLong(0);
ArmoredOutputStream arOutStream = null;
// Create an output stream
try {
arOutStream = new ArmoredOutputStream(outStream);
log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
byte[] data = cursor.getBlob(1);
CanonicalizedKeyRing ring =
UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
ring.encode(arOutStream);
okPublic += 1;
} catch (PgpGeneralException e) {
log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
updateProgress(progress++, numKeys);
continue;
} finally {
// make sure this is closed
if (arOutStream != null) {
arOutStream.close();
}
arOutStream = null;
}
if (exportSecret && cursor.getInt(3) > 0) {
try {
arOutStream = new ArmoredOutputStream(outStream);
// export secret key part
log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId
(keyId));
byte[] data = cursor.getBlob(2);
CanonicalizedKeyRing ring =
UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
ring.encode(arOutStream);
okSecret += 1;
} catch (PgpGeneralException e) {
log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
updateProgress(progress++, numKeys);
continue;
} finally {
// make sure this is closed
if (arOutStream != null) {
arOutStream.close();
}
}
}
updateProgress(progress++, numKeys);
cursor.moveToNext();
}
updateProgress(R.string.progress_done, numKeys, numKeys);
} catch (IOException e) {
log.add(LogType.MSG_EXPORT_ERROR_IO, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
} finally {
// Make sure the stream is closed
if (outStream != null) try {
outStream.close();
} catch (Exception e) {
Log.e(Constants.TAG, "error closing stream", e);
}
if (cursor != null) {
cursor.close();
}
}
log.add(LogType.MSG_EXPORT_SUCCESS, 1);
return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret);
}
public ExportResult execute(ExportKeyringParcel exportInput, CryptoInputParcel cryptoInput) {
switch (exportInput.mExportType) {
case UPLOAD_KEYSERVER: {
HkpKeyserver hkpKeyserver = new HkpKeyserver(exportInput.mKeyserver);
try {
CanonicalizedPublicKeyRing keyring
= mProviderHelper.getCanonicalizedPublicKeyRing(
exportInput.mCanonicalizedPublicKeyringUri);
uploadKeyRingToServer(hkpKeyserver, keyring);
// TODO: replace with proper log
return new ExportResult(ExportResult.RESULT_OK, new OperationLog());
} catch (Exception e) {
return new ExportResult(ExportResult.RESULT_ERROR, new OperationLog());
// TODO: Implement better exception handling, replace with log
}
}
case EXPORT_FILE: {
return exportToFile(exportInput.mMasterKeyIds, exportInput.mExportSecret,
exportInput.mOutputFile);
}
case EXPORT_URI: {
return exportToUri(exportInput.mMasterKeyIds, exportInput.mExportSecret,
exportInput.mOutputUri);
}
default: { // can't happen
return null;
}
}
}
}

View File

@@ -19,57 +19,50 @@
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/** An operation class which implements high level import and export
/**
* An operation class which implements high level import
* operations.
*
* This class receives a source and/or destination of keys as input and performs
* all steps for this import or export.
*
* all steps for this import.
* For the import operation, the only valid source is an Iterator of
* ParcelableKeyRing, each of which must contain either a single
* keyring encoded as bytes, or a unique reference to a keyring
@@ -78,72 +71,57 @@ import java.util.concurrent.atomic.AtomicBoolean;
* secret keys, because some implementations (notably Symantec PGP Desktop) do
* not include self certificates for user ids in the secret keyring. The import
* method here will generally import keyrings in the order given by the
* iterator. so this should be ensured beforehand.
* iterator, so this should be ensured beforehand.
*
* @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
*
* For the export operation, the input consists of a set of key ids and
* either the name of a file or an output uri to write to.
*
* TODO rework uploadKeyRingToServer
*
*/
public class ImportExportOperation extends BaseOperation {
public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
public ImportOperation(Context context, ProviderHelper providerHelper, Progressable
progressable) {
super(context, providerHelper, progressable);
}
public ImportExportOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
public ImportOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) throws AddKeyException {
uploadKeyRingToServer(server, keyring.getUncachedKeyRing());
// Overloaded functions for using progressable supplied in constructor during import
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
String keyServerUri) {
return serialKeyRingImport(entries, num, keyServerUri, mProgressable);
}
public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws AddKeyException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
aos = new ArmoredOutputStream(bos);
keyring.encode(aos);
aos.close();
String armoredKey = bos.toString("UTF-8");
server.add(armoredKey);
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
throw new AddKeyException();
} finally {
try {
if (aos != null) {
aos.close();
}
bos.close();
} catch (IOException e) {
// this is just a finally thing, no matter if it doesn't work out.
}
}
}
public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries, String keyServerUri) {
public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries,
String keyServerUri) {
Iterator<ParcelableKeyRing> it = entries.iterator();
int numEntries = entries.size();
return importKeyRings(it, numEntries, keyServerUri);
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
}
public ImportKeyResult importKeyRings(ParcelableFileCache<ParcelableKeyRing> cache, String keyServerUri) {
public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries, String keyServerUri,
Progressable progressable) {
Iterator<ParcelableKeyRing> it = entries.iterator();
int numEntries = entries.size();
return serialKeyRingImport(it, numEntries, keyServerUri, progressable);
}
public ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
String keyServerUri) {
// get entries from cached file
try {
IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
int numEntries = it.getSize();
return importKeyRings(it, numEntries, keyServerUri);
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
} catch (IOException e) {
// Special treatment here, we need a lot
@@ -157,15 +135,18 @@ public class ImportExportOperation extends BaseOperation {
}
/**
* Since the introduction of multithreaded import, we expect calling functions to handle the key sync i,e
* ContactSyncAdapterService.requestSync()
* Since the introduction of multithreaded import, we expect calling functions to handle the
* key sync i,eContactSyncAdapterService.requestSync()
*
* @param entries keys to import
* @param num number of keys to import
* @param entries keys to import
* @param num number of keys to import
* @param keyServerUri contains uri of keyserver to import from, if it is an import from cloud
* @param progressable Allows multi-threaded import to supply a progressable that ignores the
* progress of a single key being imported
* @return
*/
public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num, String keyServerUri) {
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
String keyServerUri, Progressable progressable) {
updateProgress(R.string.progress_importing, 0, 100);
OperationLog log = new OperationLog();
@@ -208,7 +189,8 @@ public class ImportExportOperation extends BaseOperation {
else {
// We fetch from keyservers first, because we tend to get more certificates
// from there, so the number of certificates which are merged in later is smaller.
// from there, so the number of certificates which are merged in later is
// smaller.
// If we have a keyServerUri and a fingerprint or at least a keyId,
// download from HKP
@@ -224,7 +206,8 @@ public class ImportExportOperation extends BaseOperation {
byte[] data;
// Download by fingerprint, or keyId - whichever is available
if (entry.mExpectedFingerprint != null) {
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" + entry.mExpectedFingerprint.substring(24));
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" +
entry.mExpectedFingerprint.substring(24));
data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes();
} else {
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex);
@@ -302,10 +285,12 @@ public class ImportExportOperation extends BaseOperation {
mProviderHelper.clearLog();
if (key.isSecret()) {
result = mProviderHelper.saveSecretKeyRing(key,
new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
new ProgressScaler(progressable, (int) (position * progSteps),
(int) ((position + 1) * progSteps), 100));
} else {
result = mProviderHelper.savePublicKeyRing(key,
new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
new ProgressScaler(progressable, (int) (position * progSteps),
(int) ((position + 1) * progSteps), 100));
}
if (!result.success()) {
badKeys += 1;
@@ -333,7 +318,7 @@ public class ImportExportOperation extends BaseOperation {
// Special: consolidate on secret key import (cannot be cancelled!)
if (secret > 0) {
setPreventCancel();
ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(mProgressable);
ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(progressable);
log.add(result, 1);
}
@@ -376,7 +361,7 @@ public class ImportExportOperation extends BaseOperation {
}
// Final log entry, it's easier to do this individually
if ( (newKeys > 0 || updatedKeys > 0) && badKeys > 0) {
if ((newKeys > 0 || updatedKeys > 0) && badKeys > 0) {
log.add(LogType.MSG_IMPORT_PARTIAL, 1);
} else if (newKeys > 0 || updatedKeys > 0) {
log.add(LogType.MSG_IMPORT_SUCCESS, 1);
@@ -388,206 +373,193 @@ public class ImportExportOperation extends BaseOperation {
importedMasterKeyIdsArray);
}
public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) {
@Override
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
return importKeys(importInput.mKeyList, importInput.mKeyserver);
}
OperationLog log = new OperationLog();
if (masterKeyIds != null) {
log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
public ImportKeyResult importKeys(ArrayList<ParcelableKeyRing> keyList, String keyServer) {
ImportKeyResult result;
if (keyList == null) {// import from file, do serially
ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<>(mContext,
"key_import.pcl");
result = serialKeyRingImport(cache, keyServer);
} else {
log.add(LogType.MSG_EXPORT_ALL, 0);
}
// do we have a file name?
if (outputFile == null) {
log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
// check if storage is ready
if (!FileHelper.isStorageMounted(outputFile)) {
log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
try {
OutputStream outStream = new FileOutputStream(outputFile);
try {
ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream);
if (result.cancelled()) {
//noinspection ResultOfMethodCallIgnored
new File(outputFile).delete();
}
return result;
} finally {
outStream.close();
// if there is more than one key with the same fingerprint, we do a serial import to
// prevent
// https://github.com/open-keychain/open-keychain/issues/1221
HashSet<String> keyFingerprintSet = new HashSet<>();
for (int i = 0; i < keyList.size(); i++) {
keyFingerprintSet.add(keyList.get(i).mExpectedFingerprint);
}
if (keyFingerprintSet.size() == keyList.size()) {
// all keys have unique fingerprints
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer);
} else {
result = serialKeyRingImport(keyList, keyServer);
}
} catch (IOException e) {
log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
ContactSyncAdapterService.requestSync();
return result;
}
public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
private ImportKeyResult multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator,
int totKeys, final String keyServer) {
Log.d(Constants.TAG, "Multi-threaded key import starting");
if (keyListIterator != null) {
KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
OperationLog log = new OperationLog();
if (masterKeyIds != null) {
log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
} else {
log.add(LogType.MSG_EXPORT_ALL, 0);
}
final ProgressScaler ignoreProgressable = new ProgressScaler();
// do we have a file name?
if (outputUri == null) {
log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
final int maxThreads = 200;
ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
30L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
try {
OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream(outputUri);
return exportKeyRings(log, masterKeyIds, exportSecret, outStream);
} catch (FileNotFoundException e) {
log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
ExecutorCompletionService<ImportKeyResult> importCompletionService =
new ExecutorCompletionService(importExecutor);
}
while (keyListIterator.hasNext()) { // submit all key rings to be imported
ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
OutputStream outStream) {
final ParcelableKeyRing pkRing = keyListIterator.next();
/* TODO isn't this checked above, with the isStorageMounted call?
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
}
*/
Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult>
() {
if ( ! BufferedOutputStream.class.isInstance(outStream)) {
outStream = new BufferedOutputStream(outStream);
}
@Override
public ImportKeyResult call() {
int okSecret = 0, okPublic = 0, progress = 0;
ArrayList<ParcelableKeyRing> list = new ArrayList<>();
list.add(pkRing);
Cursor cursor = null;
try {
String selection = null, ids[] = null;
if (masterKeyIds != null) {
// generate placeholders and string selection args
ids = new String[masterKeyIds.length];
StringBuilder placeholders = new StringBuilder("?");
for (int i = 0; i < masterKeyIds.length; i++) {
ids[i] = Long.toString(masterKeyIds[i]);
if (i != 0) {
placeholders.append(",?");
return serialKeyRingImport(list, keyServer, ignoreProgressable);
}
}
};
// put together selection string
selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID
+ " IN (" + placeholders + ")";
importCompletionService.submit(importOperationCallable);
}
cursor = mProviderHelper.getContentResolver().query(
KeyRings.buildUnifiedKeyRingsUri(), new String[]{
KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA,
KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET
}, selection, ids, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
);
if (cursor == null || !cursor.moveToFirst()) {
log.add(LogType.MSG_EXPORT_ERROR_DB, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
}
int numKeys = cursor.getCount();
updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
numKeys), 0, numKeys);
// For each public masterKey id
while (!cursor.isAfterLast()) {
long keyId = cursor.getLong(0);
ArmoredOutputStream arOutStream = null;
// Create an output stream
while (!accumulator.isImportFinished()) { // accumulate the results of each import
try {
arOutStream = new ArmoredOutputStream(outStream);
log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
byte[] data = cursor.getBlob(1);
CanonicalizedKeyRing ring =
UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
ring.encode(arOutStream);
okPublic += 1;
} catch (PgpGeneralException e) {
log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
updateProgress(progress++, numKeys);
continue;
} finally {
// make sure this is closed
if (arOutStream != null) {
arOutStream.close();
}
arOutStream = null;
}
if (exportSecret && cursor.getInt(3) > 0) {
try {
arOutStream = new ArmoredOutputStream(outStream);
// export secret key part
log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyId));
byte[] data = cursor.getBlob(2);
CanonicalizedKeyRing ring =
UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
ring.encode(arOutStream);
okSecret += 1;
} catch (PgpGeneralException e) {
log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
updateProgress(progress++, numKeys);
continue;
} finally {
// make sure this is closed
if (arOutStream != null) {
arOutStream.close();
}
accumulator.accumulateKeyImport(importCompletionService.take().get());
} catch (InterruptedException | ExecutionException e) {
Log.e(Constants.TAG, "A key could not be imported during multi-threaded " +
"import", e);
// do nothing?
if (e instanceof ExecutionException) {
// Since serialKeyRingImport does not throw any exceptions, this is what
// would have happened if
// we were importing the key on this thread
throw new RuntimeException();
}
}
updateProgress(progress++, numKeys);
cursor.moveToNext();
}
updateProgress(R.string.progress_done, numKeys, numKeys);
} catch (IOException e) {
log.add(LogType.MSG_EXPORT_ERROR_IO, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
} finally {
// Make sure the stream is closed
if (outStream != null) try {
outStream.close();
} catch (Exception e) {
Log.e(Constants.TAG, "error closing stream", e);
}
if (cursor != null) {
cursor.close();
}
return accumulator.getConsolidatedResult();
}
log.add(LogType.MSG_EXPORT_SUCCESS, 1);
return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret);
return null; // TODO: Decide if we should just crash instead of returning null
}
}
/**
* Used to accumulate the results of individual key imports
*/
private class KeyImportAccumulator {
private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
Progressable mProgressable;
private int mTotalKeys;
private int mImportedKeys = 0;
ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
private int mBadKeys = 0;
private int mNewKeys = 0;
private int mUpdatedKeys = 0;
private int mSecret = 0;
private int mResultType = 0;
/**
* Accumulates keyring imports and updates the progressable whenever a new key is imported.
* Also sets the progress to 0 on instantiation.
*
* @param totalKeys total number of keys to be imported
* @param externalProgressable the external progressable to be updated every time a key
* is imported
*/
public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) {
mTotalKeys = totalKeys;
mProgressable = externalProgressable;
mProgressable.setProgress(0, totalKeys);
}
public int getTotalKeys() {
return mTotalKeys;
}
public int getImportedKeys() {
return mImportedKeys;
}
public synchronized void accumulateKeyImport(ImportKeyResult result) {
mImportedKeys++;
mProgressable.setProgress(mImportedKeys, mTotalKeys);
mImportLog.addAll(result.getLog().toList());//accumulates log
mBadKeys += result.mBadKeys;
mNewKeys += result.mNewKeys;
mUpdatedKeys += result.mUpdatedKeys;
mSecret += result.mSecret;
long[] masterKeyIds = result.getImportedMasterKeyIds();
for (long masterKeyId : masterKeyIds) {
mImportedMasterKeyIds.add(masterKeyId);
}
// if any key import has been cancelled, set result type to cancelled
// resultType is added to in getConsolidatedKayImport to account for remaining factors
mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
}
/**
* returns accumulated result of all imports so far
*/
public ImportKeyResult getConsolidatedResult() {
// adding required information to mResultType
// special case,no keys requested for import
if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
} else {
if (mNewKeys > 0) {
mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
}
if (mUpdatedKeys > 0) {
mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
}
if (mBadKeys > 0) {
mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
if (mNewKeys == 0 && mUpdatedKeys == 0) {
mResultType |= ImportKeyResult.RESULT_ERROR;
}
}
if (mImportLog.containsWarnings()) {
mResultType |= ImportKeyResult.RESULT_WARNINGS;
}
}
long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
for (int i = 0; i < masterKeyIds.length; i++) {
masterKeyIds[i] = mImportedMasterKeyIds.get(i);
}
return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
mSecret, masterKeyIds);
}
public boolean isImportFinished() {
return mTotalKeys == mImportedKeys;
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
import com.textuality.keybase.lib.Proof;
import com.textuality.keybase.lib.prover.Prover;
import de.measite.minidns.Client;
import de.measite.minidns.DNSMessage;
import de.measite.minidns.Question;
import de.measite.minidns.Record;
import de.measite.minidns.record.Data;
import de.measite.minidns.record.TXT;
import org.json.JSONObject;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificationParcel> {
public KeybaseVerificationOperation(Context context, ProviderHelper providerHelper,
Progressable progressable) {
super(context, providerHelper, progressable);
}
@Override
public KeybaseVerificationResult execute(KeybaseVerificationParcel keybaseInput,
CryptoInputParcel cryptoInput) {
String requiredFingerprint = keybaseInput.mRequiredFingerprint;
OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(OperationResult.LogType.MSG_KEYBASE_VERIFICATION, 0, requiredFingerprint);
try {
String keybaseProof = keybaseInput.mKeybaseProof;
Proof proof = new Proof(new JSONObject(keybaseProof));
mProgressable.setProgress(R.string.keybase_message_fetching_data, 0, 100);
Prover prover = Prover.findProverFor(proof);
if (prover == null) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_NO_PROVER, 1,
proof.getPrettyName());
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
if (!prover.fetchProofData()) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FETCH_PROOF, 1);
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
if (!prover.checkFingerprint(requiredFingerprint)) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH, 1);
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
String domain = prover.dnsTxtCheckRequired();
if (domain != null) {
DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
if (dnsQuery == null) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_DNS_FAIL, 1);
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 2,
getFlattenedProverLog(prover));
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
Record[] records = dnsQuery.getAnswers();
List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
for (Record r : records) {
Data d = r.getPayload();
if (d instanceof TXT) {
extents.add(((TXT) d).getExtents());
}
}
if (!prover.checkDnsTxt(extents)) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
getFlattenedProverLog(prover));
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
}
byte[] messageBytes = prover.getPgpMessage().getBytes();
if (prover.rawMessageCheckRequired()) {
InputStream messageByteStream = PGPUtil.getDecoderStream(new
ByteArrayInputStream
(messageBytes));
if (!prover.checkRawMessageBytes(messageByteStream)) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
getFlattenedProverLog(prover));
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
}
PgpDecryptVerify op = new PgpDecryptVerify(mContext, mProviderHelper, mProgressable);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
.setSignedLiteralData(true)
.setRequiredSignerFingerprint(requiredFingerprint);
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
if (!decryptVerifyResult.success()) {
log.add(decryptVerifyResult, 1);
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH, 1);
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
return new KeybaseVerificationResult(OperationResult.RESULT_OK, log, prover);
} catch (Exception e) {
// just adds the passed parameter, in this case e.getMessage()
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1, e.getMessage());
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
}
private String getFlattenedProverLog(Prover prover) {
String log = "";
for (String line : prover.getLog()) {
log += line + "\n";
}
return log;
}
}

View File

@@ -32,6 +32,8 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -45,13 +47,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
* without secret key material, using a GNU_DUMMY s2k type.
*
*/
public class PromoteKeyOperation extends BaseOperation {
public class PromoteKeyOperation extends BaseOperation<PromoteKeyringParcel> {
public PromoteKeyOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
@Override
public PromoteKeyResult execute(PromoteKeyringParcel promoteKeyringParcel,
CryptoInputParcel cryptoInputParcel) {
// Input
long masterKeyId = promoteKeyringParcel.mKeyRingId;
byte[] cardAid = promoteKeyringParcel.mCardAid;
long[] subKeyIds = promoteKeyringParcel.mSubKeyIds;
return execute(masterKeyId, cardAid, subKeyIds);
}
public PromoteKeyResult execute(long masterKeyId, byte[] cardAid, long[] subKeyIds) {
OperationLog log = new OperationLog();

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
import android.os.Parcelable;
import com.textuality.keybase.lib.KeybaseException;
import com.textuality.keybase.lib.prover.Prover;
public class KeybaseVerificationResult extends OperationResult implements Parcelable {
public final String mProofUrl;
public final String mPresenceUrl;
public final String mPresenceLabel;
public KeybaseVerificationResult(int result, OperationLog log) {
super(result, log);
mProofUrl = null;
mPresenceLabel = null;
mPresenceUrl = null;
}
public KeybaseVerificationResult(int result, OperationLog log, Prover prover)
throws KeybaseException {
super(result, log);
mProofUrl = prover.getProofUrl();
mPresenceUrl = prover.getPresenceUrl();
mPresenceLabel = prover.getPresenceLabel();
}
protected KeybaseVerificationResult(Parcel in) {
super(in);
mProofUrl = in.readString();
mPresenceUrl = in.readString();
mPresenceLabel = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(mProofUrl);
dest.writeString(mPresenceUrl);
dest.writeString(mPresenceLabel);
}
public static final Parcelable.Creator<KeybaseVerificationResult> CREATOR = new Parcelable.Creator<KeybaseVerificationResult>() {
@Override
public KeybaseVerificationResult createFromParcel(Parcel in) {
return new KeybaseVerificationResult(in);
}
@Override
public KeybaseVerificationResult[] newArray(int size) {
return new KeybaseVerificationResult[size];
}
};
}

View File

@@ -750,7 +750,19 @@ public abstract class OperationResult implements Parcelable {
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
//export log
// keybase verification
MSG_KEYBASE_VERIFICATION(LogLevel.START, R.string.msg_keybase_verification),
MSG_KEYBASE_ERROR_NO_PROVER(LogLevel.ERROR, R.string.msg_keybase_error_no_prover),
MSG_KEYBASE_ERROR_FETCH_PROOF(LogLevel.ERROR, R.string.msg_keybase_error_fetching_evidence),
MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH(LogLevel.ERROR,
R.string.msg_keybase_error_key_mismatch),
MSG_KEYBASE_ERROR_DNS_FAIL(LogLevel.ERROR, R.string.msg_keybase_error_dns_fail),
MSG_KEYBASE_ERROR_SPECIFIC(LogLevel.ERROR, R.string.msg_keybase_error_specific),
MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH(LogLevel.ERROR,
R.string.msg_keybase_error_msg_payload_mismatch),
// export log
MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start),
MSG_EXPORT_LOG_EXPORT_ERROR_NO_FILE(LogLevel.ERROR,R.string.msg_export_log_error_no_file),
MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen),

View File

@@ -32,7 +32,7 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.ImportExportOperation;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
@@ -1248,9 +1248,9 @@ public class ProviderHelper {
// 3. Re-Import secret keyrings from cache
if (numSecrets > 0) {
ImportKeyResult result = new ImportExportOperation(mContext, this,
ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
.importKeyRings(itSecrets, numSecrets, null);
.serialKeyRingImport(itSecrets, numSecrets, null);
log.add(result, indent);
} else {
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
@@ -1276,9 +1276,9 @@ public class ProviderHelper {
// 4. Re-Import public keyrings from cache
if (numPublics > 0) {
ImportKeyResult result = new ImportExportOperation(mContext, this,
ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
.importKeyRings(itPublics, numPublics, null);
.serialKeyRingImport(itPublics, numPublics, null);
log.add(result, indent);
} else {
log.add(LogType.MSG_CON_REIMPORT_PUBLIC_SKIP, indent);

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
public class ConsolidateInputParcel implements Parcelable {
public boolean mConsolidateRecovery;
public ConsolidateInputParcel(boolean consolidateRecovery) {
mConsolidateRecovery = consolidateRecovery;
}
protected ConsolidateInputParcel(Parcel in) {
mConsolidateRecovery = in.readByte() != 0x00;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (mConsolidateRecovery ? 0x01 : 0x00));
}
public static final Parcelable.Creator<ConsolidateInputParcel> CREATOR = new Parcelable.Creator<ConsolidateInputParcel>() {
@Override
public ConsolidateInputParcel createFromParcel(Parcel in) {
return new ConsolidateInputParcel(in);
}
@Override
public ConsolidateInputParcel[] newArray(int size) {
return new ConsolidateInputParcel[size];
}
};
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
public class DeleteKeyringParcel implements Parcelable {
public long[] mMasterKeyIds;
public boolean mIsSecret;
public DeleteKeyringParcel(long[] masterKeyIds, boolean isSecret) {
mMasterKeyIds = masterKeyIds;
mIsSecret = isSecret;
}
protected DeleteKeyringParcel(Parcel in) {
mIsSecret = in.readByte() != 0x00;
mMasterKeyIds = in.createLongArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (mIsSecret ? 0x01 : 0x00));
dest.writeLongArray(mMasterKeyIds);
}
public static final Parcelable.Creator<DeleteKeyringParcel> CREATOR = new Parcelable.Creator<DeleteKeyringParcel>() {
@Override
public DeleteKeyringParcel createFromParcel(Parcel in) {
return new DeleteKeyringParcel(in);
}
@Override
public DeleteKeyringParcel[] newArray(int size) {
return new DeleteKeyringParcel[size];
}
};
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
public class ExportKeyringParcel implements Parcelable {
public String mKeyserver;
public Uri mCanonicalizedPublicKeyringUri;
public boolean mExportSecret;
public long mMasterKeyIds[];
public String mOutputFile;
public Uri mOutputUri;
public ExportType mExportType;
public enum ExportType {
UPLOAD_KEYSERVER,
EXPORT_FILE,
EXPORT_URI
}
public ExportKeyringParcel(String keyserver, Uri keyringUri) {
mExportType = ExportType.UPLOAD_KEYSERVER;
mKeyserver = keyserver;
mCanonicalizedPublicKeyringUri = keyringUri;
}
public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, String outputFile) {
mExportType = ExportType.EXPORT_FILE;
mMasterKeyIds = masterKeyIds;
mExportSecret = exportSecret;
mOutputFile = outputFile;
}
public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
mExportType = ExportType.EXPORT_URI;
mMasterKeyIds = masterKeyIds;
mExportSecret = exportSecret;
mOutputUri = outputUri;
}
protected ExportKeyringParcel(Parcel in) {
mKeyserver = in.readString();
mCanonicalizedPublicKeyringUri = (Uri) in.readValue(Uri.class.getClassLoader());
mExportSecret = in.readByte() != 0x00;
mOutputFile = in.readString();
mOutputUri = (Uri) in.readValue(Uri.class.getClassLoader());
mExportType = (ExportType) in.readValue(ExportType.class.getClassLoader());
mMasterKeyIds = in.createLongArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mKeyserver);
dest.writeValue(mCanonicalizedPublicKeyringUri);
dest.writeByte((byte) (mExportSecret ? 0x01 : 0x00));
dest.writeString(mOutputFile);
dest.writeValue(mOutputUri);
dest.writeValue(mExportType);
dest.writeLongArray(mMasterKeyIds);
}
public static final Parcelable.Creator<ExportKeyringParcel> CREATOR = new Parcelable.Creator<ExportKeyringParcel>() {
@Override
public ExportKeyringParcel createFromParcel(Parcel in) {
return new ExportKeyringParcel(in);
}
@Override
public ExportKeyringParcel[] newArray(int size) {
return new ExportKeyringParcel[size];
}
};
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import java.util.ArrayList;
public class ImportKeyringParcel implements Parcelable {
// if null, keys are expected to be read from a cache file in ImportExportOperations
public ArrayList<ParcelableKeyRing> mKeyList;
public String mKeyserver; // must be set if keys are to be imported from a keyserver
public ImportKeyringParcel (ArrayList<ParcelableKeyRing> keyList, String keyserver) {
mKeyList = keyList;
mKeyserver = keyserver;
}
protected ImportKeyringParcel(Parcel in) {
if (in.readByte() == 0x01) {
mKeyList = new ArrayList<>();
in.readList(mKeyList, ParcelableKeyRing.class.getClassLoader());
} else {
mKeyList = null;
}
mKeyserver = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (mKeyList == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(mKeyList);
}
dest.writeString(mKeyserver);
}
public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
@Override
public ImportKeyringParcel createFromParcel(Parcel in) {
return new ImportKeyringParcel(in);
}
@Override
public ImportKeyringParcel[] newArray(int size) {
return new ImportKeyringParcel[size];
}
};
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
public class KeybaseVerificationParcel implements Parcelable {
public String mKeybaseProof;
public String mRequiredFingerprint;
public KeybaseVerificationParcel(String keybaseProof, String requiredFingerprint) {
mKeybaseProof = keybaseProof;
mRequiredFingerprint = requiredFingerprint;
}
protected KeybaseVerificationParcel(Parcel in) {
mKeybaseProof = in.readString();
mRequiredFingerprint = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mKeybaseProof);
dest.writeString(mRequiredFingerprint);
}
public static final Parcelable.Creator<KeybaseVerificationParcel> CREATOR = new Parcelable.Creator<KeybaseVerificationParcel>() {
@Override
public KeybaseVerificationParcel createFromParcel(Parcel in) {
return new KeybaseVerificationParcel(in);
}
@Override
public KeybaseVerificationParcel[] newArray(int size) {
return new KeybaseVerificationParcel[size];
}
};
}

View File

@@ -1,188 +0,0 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
import android.os.RemoteException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.operations.CertifyOperation;
import org.sufficientlysecure.keychain.operations.EditKeyOperation;
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Log;
/**
* This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
* data from the activities or other apps, executes them, and stops itself after doing them.
*/
public class KeychainNewService extends Service implements Progressable {
// messenger for communication (hack)
public static final String EXTRA_MESSENGER = "messenger";
// extras for operation
public static final String EXTRA_OPERATION_INPUT = "op_input";
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
// this attribute can possibly merged with the one above? not sure...
private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* This is run on the main thread, we need to spawn a runnable which runs on another thread for the actual operation
*/
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
Runnable actionRunnable = new Runnable() {
@Override
public void run() {
// We have not been cancelled! (yet)
mActionCanceled.set(false);
Bundle extras = intent.getExtras();
// Set messenger for communication (for this particular thread)
mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
// Input
Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
// Operation
BaseOperation op;
// just for brevity
KeychainNewService outerThis = KeychainNewService.this;
if (inputParcel instanceof SignEncryptParcel) {
op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
} else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis);
} else if (inputParcel instanceof SaveKeyringParcel) {
op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
} else if (inputParcel instanceof CertifyAction) {
op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
} else {
return;
}
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
OperationResult result = op.execute(inputParcel, cryptoInput);
sendMessageToHandler(MessageStatus.OKAY, result);
}
};
Thread actionThread = new Thread(actionRunnable);
actionThread.start();
return START_NOT_STICKY;
}
private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
Message msg = Message.obtain();
assert msg != null;
msg.arg1 = status.ordinal();
if (arg2 != null) {
msg.arg2 = arg2;
}
if (data != null) {
msg.setData(data);
}
try {
mMessenger.get().send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
private void sendMessageToHandler(MessageStatus status, OperationResult data) {
Bundle bundle = new Bundle();
bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
sendMessageToHandler(status, null, bundle);
}
private void sendMessageToHandler(MessageStatus status) {
sendMessageToHandler(status, null, null);
}
/**
* Set progress of ProgressDialog by sending message to handler on UI thread
*/
@Override
public void setProgress(String message, int progress, int max) {
Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
+ max);
Bundle data = new Bundle();
if (message != null) {
data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
}
data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
}
@Override
public void setProgress(int resourceId, int progress, int max) {
setProgress(getString(resourceId), progress, max);
}
@Override
public void setProgress(int progress, int max) {
setProgress(null, progress, max);
}
@Override
public void setPreventCancel() {
sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
}
}

View File

@@ -20,146 +20,55 @@ package org.sufficientlysecure.keychain.service;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
import android.os.RemoteException;
import com.textuality.keybase.lib.Proof;
import com.textuality.keybase.lib.prover.Prover;
import org.json.JSONObject;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.operations.CertifyOperation;
import org.sufficientlysecure.keychain.operations.ConsolidateOperation;
import org.sufficientlysecure.keychain.operations.DeleteOperation;
import org.sufficientlysecure.keychain.operations.EditKeyOperation;
import org.sufficientlysecure.keychain.operations.ImportExportOperation;
import org.sufficientlysecure.keychain.operations.ExportOperation;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import de.measite.minidns.Client;
import de.measite.minidns.DNSMessage;
import de.measite.minidns.Question;
import de.measite.minidns.Record;
import de.measite.minidns.record.Data;
import de.measite.minidns.record.TXT;
/**
* This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
* data from the activities or other apps, executes them, and stops itself after doing them.
*/
public class KeychainService extends Service implements Progressable {
/* extras that can be given by intent */
// messenger for communication (hack)
public static final String EXTRA_MESSENGER = "messenger";
public static final String EXTRA_DATA = "data";
/* possible actions */
public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING";
public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING";
public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING";
public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING";
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE";
public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
/* keys for data bundle */
// keybase proof
public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
public static final String KEYBASE_PROOF = "keybase_proof";
// save keyring
public static final String EDIT_KEYRING_PARCEL = "save_parcel";
public static final String EDIT_KEYRING_PASSPHRASE = "passphrase";
// extras for operation
public static final String EXTRA_OPERATION_INPUT = "op_input";
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
// delete keyring(s)
public static final String DELETE_KEY_LIST = "delete_list";
public static final String DELETE_IS_SECRET = "delete_is_secret";
// import key
public static final String IMPORT_KEY_LIST = "import_key_list";
public static final String IMPORT_KEY_SERVER = "import_key_server";
// export key
public static final String EXPORT_FILENAME = "export_filename";
public static final String EXPORT_URI = "export_uri";
public static final String EXPORT_SECRET = "export_secret";
public static final String EXPORT_ALL = "export_all";
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
// upload key
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
// promote key
public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
public static final String PROMOTE_CARD_AID = "promote_card_aid";
public static final String PROMOTE_SUBKEY_IDS = "promote_fingerprints";
// consolidate
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
Messenger mMessenger;
public static final String ACTION_CANCEL = "action_cancel";
// this attribute can possibly merged with the one above? not sure...
private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
private KeyImportAccumulator mKeyImportAccumulator;
private KeychainService mKeychainService;
ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
@Override
public IBinder onBind(Intent intent) {
@@ -171,9 +80,8 @@ public class KeychainService extends Service implements Progressable {
*/
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
mKeychainService = this;
if (ACTION_CANCEL.equals(intent.getAction())) {
if (intent.getAction() != null && intent.getAction().equals(ACTION_CANCEL)) {
mActionCanceled.set(true);
return START_NOT_STICKY;
}
@@ -185,317 +93,64 @@ public class KeychainService extends Service implements Progressable {
mActionCanceled.set(false);
Bundle extras = intent.getExtras();
if (extras == null) {
Log.e(Constants.TAG, "Extras bundle is null!");
// Set messenger for communication (for this particular thread)
mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
// Input
Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
// Operation
BaseOperation op;
// just for brevity
KeychainService outerThis = KeychainService.this;
if (inputParcel instanceof SignEncryptParcel) {
op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis),
outerThis, mActionCanceled);
} else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis);
} else if (inputParcel instanceof SaveKeyringParcel) {
op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
mActionCanceled);
} else if (inputParcel instanceof CertifyActionsParcel) {
op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
mActionCanceled);
} else if (inputParcel instanceof DeleteKeyringParcel) {
op = new DeleteOperation(outerThis, new ProviderHelper(outerThis), outerThis);
} else if (inputParcel instanceof PromoteKeyringParcel) {
op = new PromoteKeyOperation(outerThis, new ProviderHelper(outerThis),
outerThis, mActionCanceled);
} else if (inputParcel instanceof ImportKeyringParcel) {
op = new ImportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
mActionCanceled);
} else if (inputParcel instanceof ExportKeyringParcel) {
op = new ExportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
mActionCanceled);
} else if (inputParcel instanceof ConsolidateInputParcel) {
op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis),
outerThis);
} else if (inputParcel instanceof KeybaseVerificationParcel) {
op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
outerThis);
} else {
return;
}
if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || (intent
.getAction() == null))) {
Log.e(Constants.TAG,
"Extra bundle must contain a messenger, a data bundle, and an action!");
return;
}
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
OperationResult result = op.execute(inputParcel, cryptoInput);
sendMessageToHandler(MessageStatus.OKAY, result);
Uri dataUri = intent.getData();
mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
Bundle data = extras.getBundle(EXTRA_DATA);
if (data == null) {
Log.e(Constants.TAG, "data extra is null!");
return;
}
Log.logDebugBundle(data, "EXTRA_DATA");
ProviderHelper providerHelper = new ProviderHelper(mKeychainService);
String action = intent.getAction();
// executeServiceMethod action from extra bundle
switch (action) {
case ACTION_CONSOLIDATE: {
// Operation
ConsolidateResult result;
if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
result = providerHelper.consolidateDatabaseStep2(mKeychainService);
} else {
result = providerHelper.consolidateDatabaseStep1(mKeychainService);
}
// Result
sendMessageToHandler(MessageStatus.OKAY, result);
break;
}
case ACTION_VERIFY_KEYBASE_PROOF: {
try {
Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
setProgress(R.string.keybase_message_fetching_data, 0, 100);
Prover prover = Prover.findProverFor(proof);
if (prover == null) {
sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof
.getPrettyName());
return;
}
if (!prover.fetchProofData()) {
sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
return;
}
String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
if (!prover.checkFingerprint(requiredFingerprint)) {
sendProofError(getString(R.string.keybase_key_mismatch));
return;
}
String domain = prover.dnsTxtCheckRequired();
if (domain != null) {
DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
if (dnsQuery == null) {
sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure));
return;
}
Record[] records = dnsQuery.getAnswers();
List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
for (Record r : records) {
Data d = r.getPayload();
if (d instanceof TXT) {
extents.add(((TXT) d).getExtents());
}
}
if (!prover.checkDnsTxt(extents)) {
sendProofError(prover.getLog(), null);
return;
}
}
byte[] messageBytes = prover.getPgpMessage().getBytes();
if (prover.rawMessageCheckRequired()) {
InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream
(messageBytes));
if (!prover.checkRawMessageBytes(messageByteStream)) {
sendProofError(prover.getLog(), null);
return;
}
}
PgpDecryptVerify op = new PgpDecryptVerify(mKeychainService, providerHelper,
mKeychainService);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
.setSignedLiteralData(true)
.setRequiredSignerFingerprint(requiredFingerprint);
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
if (!decryptVerifyResult.success()) {
OperationLog log = decryptVerifyResult.getLog();
OperationResult.LogEntryParcel lastEntry = null;
for (OperationResult.LogEntryParcel entry : log) {
lastEntry = entry;
}
sendProofError(getString(lastEntry.mType.getMsgId()));
return;
}
if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
sendProofError(getString(R.string.keybase_message_payload_mismatch));
return;
}
Bundle resultData = new Bundle();
resultData.putString(ServiceProgressHandler.DATA_MESSAGE, "OK");
// these help the handler construct a useful human-readable message
resultData.putString(ServiceProgressHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL, prover
.getPresenceLabel());
sendMessageToHandler(MessageStatus.OKAY, resultData);
} catch (Exception e) {
sendErrorToHandler(e);
}
break;
}
case ACTION_DELETE: {
// Input
long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
// Operation
DeleteOperation op = new DeleteOperation(mKeychainService, providerHelper, mKeychainService);
DeleteResult result = op.execute(masterKeyIds, isSecret);
// Result
sendMessageToHandler(MessageStatus.OKAY, result);
break;
}
case ACTION_EDIT_KEYRING: {
// Input
SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
// Operation
EditKeyOperation op = new EditKeyOperation(mKeychainService, providerHelper,
mKeychainService, mActionCanceled);
OperationResult result = op.execute(saveParcel, cryptoInput);
// Result
sendMessageToHandler(MessageStatus.OKAY, result);
break;
}
case ACTION_PROMOTE_KEYRING: {
// Input
long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
long[] subKeyIds = data.getLongArray(PROMOTE_SUBKEY_IDS);
// Operation
PromoteKeyOperation op = new PromoteKeyOperation(
mKeychainService, providerHelper, mKeychainService,
mActionCanceled);
PromoteKeyResult result = op.execute(keyRingId, cardAid, subKeyIds);
// Result
sendMessageToHandler(MessageStatus.OKAY, result);
break;
}
case ACTION_EXPORT_KEYRING: {
// Input
boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
String outputFile = data.getString(EXPORT_FILENAME);
Uri outputUri = data.getParcelable(EXPORT_URI);
boolean exportAll = data.getBoolean(EXPORT_ALL);
long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
// Operation
ImportExportOperation importExportOperation = new ImportExportOperation(
mKeychainService, providerHelper, mKeychainService);
ExportResult result;
if (outputFile != null) {
result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile);
} else {
result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri);
}
// Result
sendMessageToHandler(MessageStatus.OKAY, result);
break;
}
case ACTION_IMPORT_KEYRING: {
// Input
String keyServer = data.getString(IMPORT_KEY_SERVER);
ArrayList<ParcelableKeyRing> keyList = data.getParcelableArrayList(IMPORT_KEY_LIST);
// either keyList or cache must be null, no guarantees otherwise
if (keyList == null) {// import from file, do serially
serialKeyImport(null, keyServer, providerHelper);
} else {
// if there is more than one key with the same fingerprint, we do a serial import to prevent
// https://github.com/open-keychain/open-keychain/issues/1221
HashSet<String> keyFingerprintSet = new HashSet<>();
for (int i = 0; i < keyList.size(); i++) {
keyFingerprintSet.add(keyList.get(i).mExpectedFingerprint);
}
if (keyFingerprintSet.size() == keyList.size()) {
// all keys have unique fingerprints
multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer);
} else {
serialKeyImport(keyList, keyServer, providerHelper);
}
}
break;
}
case ACTION_UPLOAD_KEYRING: {
try {
// Input
String keyServer = data.getString(UPLOAD_KEY_SERVER);
// and dataUri!
// Operation
HkpKeyserver server = new HkpKeyserver(keyServer);
CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
ImportExportOperation importExportOperation = new ImportExportOperation(mKeychainService,
providerHelper, mKeychainService);
try {
importExportOperation.uploadKeyRingToServer(server, keyring);
} catch (Keyserver.AddKeyException e) {
throw new PgpGeneralException("Unable to export key to selected server");
}
sendMessageToHandler(MessageStatus.OKAY);
} catch (Exception e) {
sendErrorToHandler(e);
}
break;
}
}
if (!intent.getAction().equals(ACTION_IMPORT_KEYRING)) {
// import keyring handles stopping service on its own
stopSelf();
}
}
};
Thread actionThread = new Thread(actionRunnable);
actionThread.start();
return START_NOT_STICKY;
}
private void sendProofError(List<String> log, String label) {
String msg = null;
label = (label == null) ? "" : label + ": ";
for (String m : log) {
Log.e(Constants.TAG, label + m);
msg = m;
}
sendProofError(label + msg);
}
private void sendProofError(String msg) {
Bundle bundle = new Bundle();
bundle.putString(ServiceProgressHandler.DATA_ERROR, msg);
sendMessageToHandler(MessageStatus.OKAY, bundle);
}
private void sendErrorToHandler(Exception e) {
// TODO: Implement a better exception handling here
// contextualize the exception, if necessary
String message;
if (e instanceof PgpGeneralMsgIdException) {
e = ((PgpGeneralMsgIdException) e).getContextualized(mKeychainService);
message = e.getMessage();
} else {
message = e.getMessage();
}
Log.d(Constants.TAG, "KeychainService Exception: ", e);
Bundle data = new Bundle();
data.putString(ServiceProgressHandler.DATA_ERROR, message);
sendMessageToHandler(MessageStatus.EXCEPTION, null, data);
}
private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
Message msg = Message.obtain();
@@ -509,7 +164,7 @@ public class KeychainService extends Service implements Progressable {
}
try {
mMessenger.send(msg);
mMessenger.get().send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
@@ -523,10 +178,6 @@ public class KeychainService extends Service implements Progressable {
sendMessageToHandler(status, null, bundle);
}
private void sendMessageToHandler(MessageStatus status, Bundle data) {
sendMessageToHandler(status, null, data);
}
private void sendMessageToHandler(MessageStatus status) {
sendMessageToHandler(status, null, null);
}
@@ -564,214 +215,4 @@ public class KeychainService extends Service implements Progressable {
sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
}
public void serialKeyImport(ArrayList<ParcelableKeyRing> keyList, final String keyServer,
ProviderHelper providerHelper) {
Log.d(Constants.TAG, "serial key import starting");
ParcelableFileCache<ParcelableKeyRing> cache =
new ParcelableFileCache<>(mKeychainService, "key_import.pcl");
// Operation
ImportExportOperation importExportOperation = new ImportExportOperation(
mKeychainService, providerHelper, mKeychainService,
mActionCanceled);
// Either list or cache must be null, no guarantees otherwise.
ImportKeyResult result = keyList != null
? importExportOperation.importKeyRings(keyList, keyServer)
: importExportOperation.importKeyRings(cache, keyServer);
ContactSyncAdapterService.requestSync();
// Result
sendMessageToHandler(MessageStatus.OKAY, result);
stopSelf();
}
public void multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator, int totKeys, final String
keyServer) {
Log.d(Constants.TAG, "Multi-threaded key import starting");
if (keyListIterator != null) {
mKeyImportAccumulator = new KeyImportAccumulator(totKeys, mKeychainService);
setProgress(0, totKeys);
final int maxThreads = 200;
ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
30L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
while (keyListIterator.hasNext()) {
final ParcelableKeyRing pkRing = keyListIterator.next();
Runnable importOperationRunnable = new Runnable() {
@Override
public void run() {
ImportKeyResult result = null;
try {
ImportExportOperation importExportOperation = new ImportExportOperation(
mKeychainService,
new ProviderHelper(mKeychainService),
mKeyImportAccumulator.getImportProgressable(),
mActionCanceled);
ArrayList<ParcelableKeyRing> list = new ArrayList<>();
list.add(pkRing);
result = importExportOperation.importKeyRings(list,
keyServer);
} finally {
// in the off-chance that importKeyRings does something to crash the
// thread before it can call singleKeyRingImportCompleted, our imported
// key count will go wrong. This will cause the service to never die,
// and the progress dialog to stay displayed. The finally block was
// originally meant to ensure singleKeyRingImportCompleted was called,
// and checks for null were to be introduced, but in such a scenario,
// knowing an uncaught error exists in importKeyRings is more important.
// if a null gets passed, something wrong is happening. We want a crash.
mKeyImportAccumulator.singleKeyRingImportCompleted(result);
}
}
};
importExecutor.execute(importOperationRunnable);
}
}
}
/**
* Used to accumulate the results of individual key imports
*/
private class KeyImportAccumulator {
private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
private int mTotalKeys;
private int mImportedKeys = 0;
private Progressable mInternalProgressable;
ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
private int mBadKeys = 0;
private int mNewKeys = 0;
private int mUpdatedKeys = 0;
private int mSecret = 0;
private int mResultType = 0;
/**
* meant to be used with a service due to stopSelf() in singleKeyRingImportCompleted. Remove this if
* generalising.
*
* @param totalKeys total number of keys to be imported
* @param externalProgressable the external progressable to be updated every time a key is imported
*/
public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) {
mTotalKeys = totalKeys;
// ignore updates from ImportExportOperation for now
mInternalProgressable = new Progressable() {
@Override
public void setProgress(String message, int current, int total) {
}
@Override
public void setProgress(int resourceId, int current, int total) {
}
@Override
public void setProgress(int current, int total) {
}
@Override
public void setPreventCancel() {
}
};
}
private synchronized void singleKeyRingImportCompleted(ImportKeyResult result) {
// increase imported key count and accumulate log and bad, new etc. key counts from result
mKeyImportAccumulator.accumulateKeyImport(result);
setProgress(mKeyImportAccumulator.getImportedKeys(), mKeyImportAccumulator.getTotalKeys());
if (mKeyImportAccumulator.isImportFinished()) {
ContactSyncAdapterService.requestSync();
sendMessageToHandler(ServiceProgressHandler.MessageStatus.OKAY,
mKeyImportAccumulator.getConsolidatedImportKeyResult());
stopSelf();//we're done here
}
}
public Progressable getImportProgressable() {
return mInternalProgressable;
}
public int getTotalKeys() {
return mTotalKeys;
}
public int getImportedKeys() {
return mImportedKeys;
}
public synchronized void accumulateKeyImport(ImportKeyResult result) {
mImportedKeys++;
mImportLog.addAll(result.getLog().toList());//accumulates log
mBadKeys += result.mBadKeys;
mNewKeys += result.mNewKeys;
mUpdatedKeys += result.mUpdatedKeys;
mSecret += result.mSecret;
long[] masterKeyIds = result.getImportedMasterKeyIds();
for (long masterKeyId : masterKeyIds) {
mImportedMasterKeyIds.add(masterKeyId);
}
// if any key import has been cancelled, set result type to cancelled
// resultType is added to in getConsolidatedKayImport to account for remaining factors
mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
}
/**
* returns accumulated result of all imports so far
*/
public ImportKeyResult getConsolidatedImportKeyResult() {
// adding required information to mResultType
// special case,no keys requested for import
if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
} else {
if (mNewKeys > 0) {
mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
}
if (mUpdatedKeys > 0) {
mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
}
if (mBadKeys > 0) {
mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
if (mNewKeys == 0 && mUpdatedKeys == 0) {
mResultType |= ImportKeyResult.RESULT_ERROR;
}
}
if (mImportLog.containsWarnings()) {
mResultType |= ImportKeyResult.RESULT_WARNINGS;
}
}
long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
for (int i = 0; i < masterKeyIds.length; i++) {
masterKeyIds[i] = mImportedMasterKeyIds.get(i);
}
return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
mSecret, masterKeyIds);
}
public boolean isImportFinished() {
return mTotalKeys == mImportedKeys;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
public class PromoteKeyringParcel implements Parcelable {
public long mKeyRingId;
public byte[] mCardAid;
public long[] mSubKeyIds;
public PromoteKeyringParcel(long keyRingId, byte[] cardAid, long[] subKeyIds) {
mKeyRingId = keyRingId;
mCardAid = cardAid;
mSubKeyIds = subKeyIds;
}
protected PromoteKeyringParcel(Parcel in) {
mKeyRingId = in.readLong();
mCardAid = in.createByteArray();
mSubKeyIds = in.createLongArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mKeyRingId);
dest.writeByteArray(mCardAid);
dest.writeLongArray(mSubKeyIds);
}
public static final Parcelable.Creator<PromoteKeyringParcel> CREATOR = new Parcelable.Creator<PromoteKeyringParcel>() {
@Override
public PromoteKeyringParcel createFromParcel(Parcel in) {
return new PromoteKeyringParcel(in);
}
@Override
public PromoteKeyringParcel[] newArray(int size) {
return new PromoteKeyringParcel[size];
}
};
}

View File

@@ -64,6 +64,8 @@ public class ServiceProgressHandler extends Handler {
public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
public static final String TAG_PROGRESS_DIALOG = "progressDialog";
FragmentActivity mActivity;
public ServiceProgressHandler(FragmentActivity activity) {
@@ -88,7 +90,7 @@ public class ServiceProgressHandler extends Handler {
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
frag.show(manager, "progressDialog");
frag.show(manager, TAG_PROGRESS_DIALOG);
}
});

View File

@@ -19,15 +19,12 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcel;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -52,15 +49,11 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
@@ -302,7 +295,7 @@ public class CertifyKeyFragment
}
@Override
protected CertifyActionsParcel createOperationInput() {
public CertifyActionsParcel createOperationInput() {
// Bail out if there is not at least one user id selected
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
@@ -325,7 +318,7 @@ public class CertifyKeyFragment
}
@Override
protected void onCryptoOperationSuccess(CertifyResult result) {
public void onCryptoOperationSuccess(CertifyResult result) {
Intent intent = new Intent();
intent.putExtra(CertifyResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, intent);
@@ -333,7 +326,7 @@ public class CertifyKeyFragment
}
@Override
protected void onCryptoOperationCancelled() {
public void onCryptoOperationCancelled() {
super.onCryptoOperationCancelled();
}

View File

@@ -17,25 +17,27 @@
package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
/**
* We can not directly create a dialog on the application context.
* This activity encapsulates a DialogFragment to emulate a dialog.
*/
public class ConsolidateDialogActivity extends FragmentActivity {
public class ConsolidateDialogActivity extends FragmentActivity
implements CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> {
public static final String EXTRA_CONSOLIDATE_RECOVERY = "consolidate_recovery";
private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
private boolean mRecovery;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -48,55 +50,45 @@ public class ConsolidateDialogActivity extends FragmentActivity {
}
private void consolidateRecovery(boolean recovery) {
// Message is received after importing is done in KeychainService
ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
/* don't care about the results (for now?)
mRecovery = recovery;
// get returned data bundle
Bundle returnData = message.getInputData();
if (returnData == null) {
return;
}
final ConsolidateResult result =
returnData.getParcelable(KeychainService.RESULT_CONSOLIDATE);
if (result == null) {
return;
}
result.createNotify(ConsolidateDialogActivity.this).show();
*/
ConsolidateDialogActivity.this.finish();
}
}
};
// Send all information needed to service to import key in other thread
Intent intent = new Intent(this, KeychainService.class);
intent.setAction(KeychainService.ACTION_CONSOLIDATE);
// fill values for this action
Bundle data = new Bundle();
data.putBoolean(KeychainService.CONSOLIDATE_RECOVERY, recovery);
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL, false
);
// start service with intent
startService(intent);
mConsolidateOpHelper = new CryptoOperationHelper<>(this, this, R.string.progress_importing);
mConsolidateOpHelper.cryptoOperation();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mConsolidateOpHelper != null) {
mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
}
}
@Override
public ConsolidateInputParcel createOperationInput() {
return new ConsolidateInputParcel(mRecovery);
}
@Override
public void onCryptoOperationSuccess(ConsolidateResult result) {
// don't care about result (for now?)
ConsolidateDialogActivity.this.finish();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ConsolidateResult result) {
// don't care about result (for now?)
ConsolidateDialogActivity.this.finish();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
}

View File

@@ -35,12 +35,14 @@ import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
@@ -49,6 +51,7 @@ import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -68,6 +71,9 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
SaveKeyringParcel mSaveKeyringParcel;
private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mCreateOpHelper;
public static CreateKeyFinalFragment newInstance() {
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
@@ -206,66 +212,61 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
private void createKey() {
final CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
Intent intent = new Intent(getActivity(), KeychainService.class);
intent.setAction(KeychainService.ACTION_EDIT_KEYRING);
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> createKeyCallback
= new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
public SaveKeyringParcel createOperationInput() {
return mSaveKeyringParcel;
}
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final EditKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
Log.e(Constants.TAG, "result == null");
return;
}
@Override
public void onCryptoOperationSuccess(EditKeyResult result) {
if (createKeyActivity.mUseSmartCardSettings) {
// save key id in between
mSaveKeyringParcel.mMasterKeyId = result.mMasterKeyId;
cryptoOperation(new CryptoInputParcel());
return;
}
if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
// result will be displayed after upload
uploadKey(result);
return;
}
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
if (createKeyActivity.mUseSmartCardSettings) {
// save key id in between
mSaveKeyringParcel.mMasterKeyId = result.mMasterKeyId;
// calls cryptoOperation corresponding to moveToCard
cryptoOperation(new CryptoInputParcel());
return;
}
if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
// result will be displayed after upload
uploadKey(result);
return;
}
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(EditKeyResult result) {
result.createNotify(getActivity()).show();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
};
Bundle data = new Bundle();
data.putParcelable(KeychainService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
intent.putExtra(KeychainService.EXTRA_DATA, data);
mCreateOpHelper = new CryptoOperationHelper<>(this, createKeyCallback,
R.string.progress_building_key);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
saveHandler.showProgressDialog(getString(R.string.progress_building_key),
ProgressDialog.STYLE_HORIZONTAL, false);
getActivity().startService(intent);
mCreateOpHelper.cryptoOperation();
}
// currently only used for moveToCard
@Override
protected SaveKeyringParcel createOperationInput() {
public SaveKeyringParcel createOperationInput() {
CachedPublicKeyRing key = (new ProviderHelper(getActivity()))
.getCachedPublicKeyRing(mSaveKeyringParcel.mMasterKeyId);
@@ -297,7 +298,7 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
// currently only used for moveToCard
@Override
protected void onCryptoOperationSuccess(OperationResult result) {
public void onCryptoOperationSuccess(OperationResult result) {
EditKeyResult editResult = (EditKeyResult) result;
if (editResult.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
@@ -314,56 +315,57 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
// TODO move into EditKeyOperation
private void uploadKey(final EditKeyResult saveKeyResult) {
// Send all information needed to service to upload key in other thread
final Intent intent = new Intent(getActivity(), KeychainService.class);
intent.setAction(KeychainService.ACTION_UPLOAD_KEYRING);
// set data uri as path to keyring
Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
final Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
saveKeyResult.mMasterKeyId);
intent.setData(blobUri);
Bundle data = new Bundle();
// upload to favorite keyserver
String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
data.putString(KeychainService.UPLOAD_KEY_SERVER, keyserver);
final String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
intent.putExtra(KeychainService.EXTRA_DATA, data);
CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> callback
= new CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult>() {
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
public ExportKeyringParcel createOperationInput() {
return new ExportKeyringParcel(keyserver, blobUri);
}
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// TODO: upload operation needs a result!
// TODO: then combine these results
//if (result.getResult() == OperationResultParcel.RESULT_OK) {
//Notify.create(getActivity(), R.string.key_send_success,
//Notify.Style.OK).show();
@Override
public void onCryptoOperationSuccess(ExportResult result) {
handleResult(result);
}
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ExportResult result) {
handleResult(result);
}
public void handleResult(ExportResult result) {
// TODO: upload operation needs a result! "result" is not currenlty used
// TODO: then combine these results (saveKeyResult and update op result)
//if (result.getResult() == OperationResultParcel.RESULT_OK) {
//Notify.create(getActivity(), R.string.key_send_success,
//Notify.Style.OK).show();
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(
getString(R.string.progress_uploading),
ProgressDialog.STYLE_HORIZONTAL, false);
// start service with intent
getActivity().startService(intent);
mUploadOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_uploading);
mUploadOpHelper.cryptoOperation();
}
}

View File

@@ -22,11 +22,8 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -37,18 +34,19 @@ import android.widget.TextView;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Preferences;
public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListenerFragment {
public class CreateKeyYubiKeyImportFragment
extends CryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
implements NfcListenerFragment {
private static final String ARG_FINGERPRINT = "fingerprint";
public static final String ARG_AID = "aid";
@@ -64,6 +62,10 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
private TextView vSerNo;
private TextView vUserId;
// for CryptoOperationFragment key import
private String mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList;
public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
CreateKeyYubiKeyImportFragment frag = new CreateKeyYubiKeyImportFragment();
@@ -175,77 +177,20 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
public void importKey() {
// Message is received after decrypting is done in KeychainService
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
ImportKeyResult result =
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
long[] masterKeyIds = result.getImportedMasterKeyIds();
// TODO handle masterKeyIds.length != 1...? sorta outlandish scenario
if (!result.success() || masterKeyIds.length == 0) {
result.createNotify(getActivity()).show();
return;
}
Intent intent = new Intent(getActivity(), ViewKeyActivity.class);
// use the imported masterKeyId, not the one from the yubikey, because
// that one might* just have been a subkey of the imported key
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
startActivity(intent);
getActivity().finish();
}
}
};
// Send all information needed to service to decrypt in other thread
Intent intent = new Intent(getActivity(), KeychainService.class);
// fill values for this action
Bundle data = new Bundle();
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
keyList.add(new ParcelableKeyRing(mNfcFingerprint, null, null));
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyList);
mKeyList = keyList;
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
mKeyserver = cloudPrefs.keyserver;
}
intent.putExtra(KeychainService.EXTRA_DATA, data);
super.setProgressMessageResource(R.string.progress_importing);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
saveHandler.showProgressDialog(
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL, false
);
// start service with intent
getActivity().startService(intent);
super.cryptoOperation();
}
@@ -264,4 +209,29 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
refreshSearch();
}
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver);
}
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
long[] masterKeyIds = result.getImportedMasterKeyIds();
if (masterKeyIds.length == 0) {
super.onCryptoOperationError(result);
return;
}
Intent intent = new Intent(getActivity(), ViewKeyActivity.class);
// use the imported masterKeyId, not the one from the yubikey, because
// that one might* just have been a subkey of the imported key
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
startActivity(intent);
getActivity().finish();
}
}

View File

@@ -27,6 +27,7 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -45,14 +46,14 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -78,6 +79,8 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
private DecryptVerifyResult mDecryptVerifyResult;
private ViewAnimator mOverlayAnimator;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -135,43 +138,15 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
private void lookupUnknownKey(long unknownKeyId) {
// Message is received after importing is done in KeychainService
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final ImportKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
Activity activity = getActivity();
if (result != null && activity != null) {
result.createNotify(activity).show();
}
getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
}
}
};
// fill values for this action
Bundle data = new Bundle();
final ArrayList<ParcelableKeyRing> keyList;
final String keyserver;
// search config
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
keyserver = cloudPrefs.keyserver;
}
{
@@ -180,19 +155,43 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
selectedEntries.add(keyEntry);
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, selectedEntries);
keyList = selectedEntries;
}
// Send all information needed to service to query keys in other thread
Intent intent = new Intent(getActivity(), KeychainService.class);
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
intent.putExtra(KeychainService.EXTRA_DATA, data);
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> callback
= new CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult>() {
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(keyList, keyserver);
}
getActivity().startService(intent);
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
result.createNotify(getActivity()).show();
getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
}
@Override
public void onCryptoOperationCancelled() {
// do nothing
}
@Override
public void onCryptoOperationError(ImportKeyResult result) {
result.createNotify(getActivity()).show();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
};
mImportOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_importing);
mImportOpHelper.cryptoOperation();
}
private void showKey(long keyId) {
@@ -457,4 +456,11 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
protected abstract void onVerifyLoaded(boolean hideErrorOverlay);
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mImportOpHelper != null) {
mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
}
super.onActivityResult(requestCode, resultCode, data);
}
}

View File

@@ -254,18 +254,12 @@ public class DecryptListFragment
}
@Override
protected boolean onCryptoSetProgress(String msg, int progress, int max) {
public boolean onCryptoSetProgress(String msg, int progress, int max) {
mAdapter.setProgress(mCurrentInputUri, progress, max, msg);
return true;
}
@Override
protected void dismissProgress() {
// progress shown inline, so never mind
}
@Override
protected void onCryptoOperationError(DecryptVerifyResult result) {
public void onCryptoOperationError(DecryptVerifyResult result) {
final Uri uri = mCurrentInputUri;
mCurrentInputUri = null;
@@ -275,7 +269,7 @@ public class DecryptListFragment
}
@Override
protected void onCryptoOperationSuccess(DecryptVerifyResult result) {
public void onCryptoOperationSuccess(DecryptVerifyResult result) {
Uri uri = mCurrentInputUri;
mCurrentInputUri = null;
@@ -439,7 +433,7 @@ public class DecryptListFragment
}
@Override
protected PgpDecryptVerifyInputParcel createOperationInput() {
public PgpDecryptVerifyInputParcel createOperationInput() {
if (mCurrentInputUri == null) {
if (mPendingInputUris.isEmpty()) {

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -26,7 +25,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -40,9 +38,6 @@ import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
@@ -54,8 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
@@ -65,7 +58,12 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.dialog.*;
import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -609,12 +607,12 @@ public class EditKeyFragment extends CryptoOperationFragment<SaveKeyringParcel,
}
@Override
protected SaveKeyringParcel createOperationInput() {
public SaveKeyringParcel createOperationInput() {
return mSaveKeyringParcel;
}
@Override
protected void onCryptoOperationSuccess(OperationResult result) {
public void onCryptoOperationSuccess(OperationResult result) {
// if good -> finish, return result to showkey and display there!
Intent intent = new Intent();

View File

@@ -386,7 +386,7 @@ public class EncryptFilesFragment
}
@Override
protected void onCryptoOperationSuccess(final SignEncryptResult result) {
public void onCryptoOperationSuccess(final SignEncryptResult result) {
if (mDeleteAfterEncrypt) {
// TODO make behavior coherent here
@@ -485,7 +485,7 @@ public class EncryptFilesFragment
}
protected SignEncryptParcel createOperationInput() {
public SignEncryptParcel createOperationInput() {
SignEncryptParcel actionsParcel = getCachedActionsParcel();

View File

@@ -202,7 +202,7 @@ public class EncryptTextFragment
}
protected SignEncryptParcel createOperationInput() {
public SignEncryptParcel createOperationInput() {
if (mMessage == null || mMessage.isEmpty()) {
Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR)
@@ -316,7 +316,7 @@ public class EncryptTextFragment
}
@Override
protected void onCryptoOperationSuccess(SignEncryptResult result) {
public void onCryptoOperationSuccess(SignEncryptResult result) {
if (mShareAfterEncrypt) {
// Share encrypted message/file

View File

@@ -17,12 +17,10 @@
package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.View.OnClickListener;
@@ -35,9 +33,10 @@ import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -47,7 +46,8 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize
import java.io.IOException;
import java.util.ArrayList;
public class ImportKeysActivity extends BaseNfcActivity {
public class ImportKeysActivity extends BaseNfcActivity
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
@@ -82,6 +82,12 @@ public class ImportKeysActivity extends BaseNfcActivity {
private Fragment mTopFragment;
private View mImportButton;
// for CryptoOperationHelper.Callback
private String mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -388,23 +394,9 @@ public class ImportKeysActivity extends BaseNfcActivity {
return;
}
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
ImportKeysActivity.this.handleMessage(message);
}
};
// Send all information needed to service to import key in other thread
Intent intent = new Intent(this, KeychainService.class);
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
// fill values for this action
Bundle data = new Bundle();
mOperationHelper = new CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult>(
this, this, R.string.progress_importing
);
ImportKeysListFragment.LoaderState ls = mListFragment.getLoaderState();
if (ls instanceof ImportKeysListFragment.BytesLoaderState) {
@@ -423,30 +415,18 @@ public class ImportKeysActivity extends BaseNfcActivity {
new ParcelableFileCache<>(this, "key_import.pcl");
cache.writeCache(selectedEntries);
intent.putExtra(KeychainService.EXTRA_DATA, data);
mKeyList = null;
mKeyserver = null;
mOperationHelper.cryptoOperation();
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
serviceHandler.showProgressDialog(
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL,
true
);
// start service with intent
startService(intent);
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR)
.show((ViewGroup) findViewById(R.id.import_snackbar));
}
} else if (ls instanceof ImportKeysListFragment.CloudLoaderState) {
ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls;
data.putString(KeychainService.IMPORT_KEY_SERVER, sls.mCloudPrefs.keyserver);
ImportKeysListFragment.CloudLoaderState sls =
(ImportKeysListFragment.CloudLoaderState) ls;
// get selected key entries
ArrayList<ParcelableKeyRing> keys = new ArrayList<>();
@@ -459,22 +439,11 @@ public class ImportKeysActivity extends BaseNfcActivity {
);
}
}
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keys);
intent.putExtra(KeychainService.EXTRA_DATA, data);
mKeyList = keys;
mKeyserver = sls.mCloudPrefs.keyserver;
mOperationHelper.cryptoOperation();
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
serviceHandler.showProgressDialog(
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL, true
);
// start service with intent
startService(intent);
}
}
@@ -485,4 +454,57 @@ public class ImportKeysActivity extends BaseNfcActivity {
// either way, finish afterwards
finish();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mOperationHelper == null ||
!mOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
public void handleResult (ImportKeyResult result) {
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())
|| ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) {
Intent intent = new Intent();
intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
ImportKeysActivity.this.setResult(RESULT_OK, intent);
ImportKeysActivity.this.finish();
return;
}
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(getIntent().getAction())) {
ImportKeysActivity.this.setResult(RESULT_OK, mPendingIntentData);
ImportKeysActivity.this.finish();
return;
}
result.createNotify(ImportKeysActivity.this)
.show((ViewGroup) findViewById(R.id.import_snackbar));
}
// methods from CryptoOperationHelper.Callback
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver);
}
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
handleResult(result);
}
@Override
public void onCryptoOperationCancelled() {
// do nothing
}
@Override
public void onCryptoOperationError(ImportKeyResult result) {
handleResult(result);
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
}

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
@@ -26,8 +25,6 @@ import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
@@ -43,8 +40,8 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -55,7 +52,8 @@ import java.util.Locale;
/**
* Proxy activity (just a transparent content view) to scan QR Codes using the Barcode Scanner app
*/
public class ImportKeysProxyActivity extends FragmentActivity {
public class ImportKeysProxyActivity extends FragmentActivity
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
// implies activity returns scanned fingerprint as extra and does not import
@@ -64,6 +62,11 @@ public class ImportKeysProxyActivity extends FragmentActivity {
public static final String EXTRA_FINGERPRINT = "fingerprint";
// for CryptoOperationHelper
private String mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -106,6 +109,10 @@ public class ImportKeysProxyActivity extends FragmentActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mImportOpHelper != null) {
mImportOpHelper.cryptoOperation();
}
if (requestCode == IntentIntegratorSupportV4.REQUEST_CODE) {
IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
resultCode, data);
@@ -205,75 +212,56 @@ public class ImportKeysProxyActivity extends FragmentActivity {
private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {
// Message is received after importing is done in KeychainService
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
finish();
return;
}
final ImportKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
Log.e(Constants.TAG, "result == null");
finish();
return;
}
if (!result.success()) {
// only return if no success...
Intent data = new Intent();
data.putExtras(returnData);
returnResult(data);
return;
}
Intent certifyIntent = new Intent(ImportKeysProxyActivity.this,
CertifyKeyActivity.class);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
result.getImportedMasterKeyIds());
startActivityForResult(certifyIntent, 0);
}
}
};
// fill values for this action
Bundle data = new Bundle();
// search config
{
Preferences prefs = Preferences.getPreferences(this);
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
mKeyserver = cloudPrefs.keyserver;
}
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyRings);
mKeyList = keyRings;
// Send all information needed to service to query keys in other thread
Intent intent = new Intent(this, KeychainService.class);
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
intent.putExtra(KeychainService.EXTRA_DATA, data);
mImportOpHelper = new CryptoOperationHelper<>(this, this, R.string.progress_importing);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
mImportOpHelper.cryptoOperation();
}
// show progress dialog
serviceHandler.showProgressDialog(
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL, true);
// start service with intent
startService(intent);
// CryptoOperationHelper.Callback methods
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver);
}
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
result.getImportedMasterKeyIds());
startActivityForResult(certifyIntent, 0);
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ImportKeyResult result) {
Bundle returnData = new Bundle();
returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
Intent data = new Intent();
data.putExtras(returnData);
returnResult(data);
return;
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
/**

View File

@@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@@ -64,9 +63,10 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
@@ -90,7 +90,8 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
*/
public class KeyListFragment extends LoaderFragment
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
LoaderManager.LoaderCallbacks<Cursor>, FabContainer {
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
static final int REQUEST_REPEAT_PASSPHRASE = 1;
static final int REQUEST_ACTION = 2;
@@ -107,6 +108,14 @@ public class KeyListFragment extends LoaderFragment
private FloatingActionsMenu mFab;
// for CryptoOperationHelper import
private ArrayList<ParcelableKeyRing> mKeyList;
private String mKeyserver;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
// for ConsolidateOperation
private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
// This ids for multiple key export.
private ArrayList<Long> mIdsForRepeatAskPassphrase;
// This index for remembering the number of master key.
@@ -580,112 +589,59 @@ public class KeyListFragment extends LoaderFragment
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
keyList.add(keyEntry);
}
mKeyList = keyList;
} finally {
cursor.close();
}
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final ImportKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
Log.e(Constants.TAG, "result == null");
return;
}
result.createNotify(getActivity()).show();
}
}
};
// Send all information needed to service to query keys in other thread
Intent intent = new Intent(getActivity(), KeychainService.class);
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
// fill values for this action
Bundle data = new Bundle();
// search config
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
mKeyserver = cloudPrefs.keyserver;
}
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyList);
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
serviceHandler.showProgressDialog(
getString(R.string.progress_updating),
ProgressDialog.STYLE_HORIZONTAL, true);
// start service with intent
getActivity().startService(intent);
mImportOpHelper = new CryptoOperationHelper<>(this,
this, R.string.progress_updating);
mImportOpHelper.cryptoOperation();
}
private void consolidate() {
// Message is received after importing is done in KeychainService
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> callback
= new CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult>() {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
public ConsolidateInputParcel createOperationInput() {
return new ConsolidateInputParcel(false); // we want to perform a full consolidate
}
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final ConsolidateResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
return;
}
@Override
public void onCryptoOperationSuccess(ConsolidateResult result) {
result.createNotify(getActivity()).show();
}
result.createNotify(getActivity()).show();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ConsolidateResult result) {
result.createNotify(getActivity()).show();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
};
// Send all information needed to service to import key in other thread
Intent intent = new Intent(getActivity(), KeychainService.class);
mConsolidateOpHelper =
new CryptoOperationHelper<>(this, callback, R.string.progress_importing);
intent.setAction(KeychainService.ACTION_CONSOLIDATE);
// fill values for this action
Bundle data = new Bundle();
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL, false);
// start service with intent
getActivity().startService(intent);
mConsolidateOpHelper.cryptoOperation();
}
private void showMultiExportDialog(long[] masterKeyIds) {
@@ -724,6 +680,14 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mImportOpHelper != null) {
mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
}
if (mConsolidateOpHelper != null) {
mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
}
if (requestCode == REQUEST_REPEAT_PASSPHRASE) {
if (resultCode != Activity.RESULT_OK) {
return;
@@ -769,6 +733,32 @@ public class KeyListFragment extends LoaderFragment
anim.start();
}
// CryptoOperationHelper.Callback methods
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver);
}
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
result.createNotify(getActivity()).show();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ImportKeyResult result) {
result.createNotify(getActivity()).show();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
private HashMap<Integer, Boolean> mSelection = new HashMap<>();

View File

@@ -18,15 +18,11 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.NumberPicker;
@@ -38,9 +34,9 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
@@ -52,7 +48,8 @@ import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class SafeSlingerActivity extends BaseActivity {
public class SafeSlingerActivity extends BaseActivity
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
private static final int REQUEST_CODE_SAFE_SLINGER = 211;
@@ -61,6 +58,12 @@ public class SafeSlingerActivity extends BaseActivity {
private long mMasterKeyId;
private int mSelectedNumber = 2;
// for CryptoOperationHelper
private ArrayList<ParcelableKeyRing> mKeyList;
private String mKeyserver;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -116,65 +119,17 @@ public class SafeSlingerActivity extends BaseActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mOperationHelper != null) {
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
}
if (requestCode == REQUEST_CODE_SAFE_SLINGER) {
if (resultCode == ExchangeActivity.RESULT_EXCHANGE_CANCELED) {
return;
}
final FragmentActivity activity = SafeSlingerActivity.this;
// Message is received after importing is done in KeychainService
ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final ImportKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
Log.e(Constants.TAG, "result == null");
return;
}
if (!result.success()) {
// result.createNotify(activity).show();
// only return if no success...
Intent data = new Intent();
data.putExtras(returnData);
setResult(RESULT_OK, data);
finish();
return;
}
// if (mExchangeMasterKeyId == null) {
// return;
// }
Intent certifyIntent = new Intent(activity, CertifyKeyActivity.class);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
startActivityForResult(certifyIntent, 0);
// mExchangeMasterKeyId = null;
}
}
};
Log.d(Constants.TAG, "importKeys started");
// Send all information needed to service to import key in other thread
Intent intent = new Intent(activity, KeychainService.class);
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
// instead of giving the entries by Intent extra, cache them into a
// file to prevent Java Binder problems on heavy imports
// read FileImportCache for more info.
@@ -185,28 +140,18 @@ public class SafeSlingerActivity extends BaseActivity {
// We parcel this iteratively into a file - anything we can
// display here, we should be able to import.
ParcelableFileCache<ParcelableKeyRing> cache =
new ParcelableFileCache<>(activity, "key_import.pcl");
new ParcelableFileCache<>(this, "key_import.pcl");
cache.writeCache(it.size(), it.iterator());
// fill values for this action
Bundle bundle = new Bundle();
intent.putExtra(KeychainService.EXTRA_DATA, bundle);
mOperationHelper =
new CryptoOperationHelper(this, this, R.string.progress_importing);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL, true
);
// start service with intent
activity.startService(intent);
mKeyList = null;
mKeyserver = null;
mOperationHelper.cryptoOperation();
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
Notify.create(activity, "Problem writing cache file!", Notify.Style.ERROR).show();
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR).show();
}
} else {
// give everything else down to KeyListActivity!
@@ -233,4 +178,39 @@ public class SafeSlingerActivity extends BaseActivity {
return list;
}
// CryptoOperationHelper.Callback functions
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver);
}
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
startActivityForResult(certifyIntent, 0);
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ImportKeyResult result) {
Bundle returnData = new Bundle();
returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
Intent data = new Intent();
data.putExtras(returnData);
setResult(RESULT_OK, data);
finish();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
}

View File

@@ -17,12 +17,9 @@
package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.NavUtils;
import android.view.MenuItem;
import android.view.View;
@@ -33,23 +30,30 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
/**
* Sends the selected public key to a keyserver
*/
public class UploadKeyActivity extends BaseActivity {
public class UploadKeyActivity extends BaseActivity
implements CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> {
private View mUploadButton;
private Spinner mKeyServerSpinner;
private Uri mDataUri;
// CryptoOperationHelper.Callback vars
private String mKeyserver;
private Uri mUnifiedKeyringUri;
private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -89,51 +93,23 @@ public class UploadKeyActivity extends BaseActivity {
setContentView(R.layout.upload_key_activity);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mUploadOpHelper != null) {
mUploadOpHelper.handleActivityResult(requestCode, resultCode, data);
}
super.onActivityResult(requestCode, resultCode, data);
}
private void uploadKey() {
// Send all information needed to service to upload key in other thread
Intent intent = new Intent(this, KeychainService.class);
intent.setAction(KeychainService.ACTION_UPLOAD_KEYRING);
// set data uri as path to keyring
Uri blobUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
intent.setData(blobUri);
// fill values for this action
Bundle data = new Bundle();
mUnifiedKeyringUri = blobUri;
String server = (String) mKeyServerSpinner.getSelectedItem();
data.putString(KeychainService.UPLOAD_KEY_SERVER, server);
mKeyserver = server;
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Message is received after uploading is done in KeychainService
ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
Toast.LENGTH_SHORT).show();
finish();
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(
getString(R.string.progress_uploading),
ProgressDialog.STYLE_HORIZONTAL, false);
// start service with intent
startService(intent);
mUploadOpHelper = new CryptoOperationHelper(this, this, R.string.progress_uploading);
mUploadOpHelper.cryptoOperation();
}
@Override
@@ -148,4 +124,30 @@ public class UploadKeyActivity extends BaseActivity {
}
return super.onOptionsItemSelected(item);
}
@Override
public ExportKeyringParcel createOperationInput() {
return new ExportKeyringParcel(mKeyserver, mUnifiedKeyringUri);
}
@Override
public void onCryptoOperationSuccess(ExportResult result) {
Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
Toast.LENGTH_SHORT).show();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ExportResult result) {
// TODO: Implement proper log for key upload then show error
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
}

View File

@@ -65,11 +65,13 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -89,7 +91,8 @@ import java.util.ArrayList;
import java.util.HashMap;
public class ViewKeyActivity extends BaseNfcActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
LoaderManager.LoaderCallbacks<Cursor>,
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
public static final String EXTRA_NFC_AID = "nfc_aid";
@@ -105,6 +108,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements
protected Uri mDataUri;
// For CryptoOperationHelper.Callback
private String mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
private TextView mName;
private TextView mStatusText;
private ImageView mStatusImage;
@@ -396,7 +404,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
private void certifyImmediate() {
Intent intent = new Intent(this, CertifyKeyActivity.class);
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId});
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId});
startCertifyIntent(intent);
}
@@ -486,6 +494,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mOperationHelper != null) {
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
}
if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) {
// If there is an EXTRA_RESULT, that's an error. Just show it.
@@ -651,56 +663,20 @@ public class ViewKeyActivity extends BaseNfcActivity implements
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
entries.add(keyEntry);
// Message is received after importing is done in KeychainService
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
mIsRefreshing = false;
if (returnData == null) {
finish();
return;
}
final ImportKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
result.createNotify(ViewKeyActivity.this).show();
}
}
};
// fill values for this action
Bundle data = new Bundle();
mKeyList = entries;
// search config
{
Preferences prefs = Preferences.getPreferences(this);
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
mKeyserver = cloudPrefs.keyserver;
}
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, entries);
// Send all information needed to service to query keys in other thread
Intent intent = new Intent(this, KeychainService.class);
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// start service with intent
startService(intent);
mOperationHelper = new CryptoOperationHelper<>(
this, this, R.string.progress_importing);
mOperationHelper.cryptoOperation();
}
private void editKey(Uri dataUri) {
@@ -986,4 +962,33 @@ public class ViewKeyActivity extends BaseNfcActivity implements
public void onLoaderReset(Loader<Cursor> loader) {
}
// CryptoOperationHelper.Callback functions
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver);
}
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
mIsRefreshing = false;
result.createNotify(this).show();
}
@Override
public void onCryptoOperationCancelled() {
mIsRefreshing = false;
}
@Override
public void onCryptoOperationError(ImportKeyResult result) {
mIsRefreshing = false;
result.createNotify(this).show();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
}

View File

@@ -17,15 +17,12 @@
package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -48,9 +45,10 @@ import com.textuality.keybase.lib.User;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
@@ -59,7 +57,8 @@ import java.util.Hashtable;
import java.util.List;
public class ViewKeyTrustFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
LoaderManager.LoaderCallbacks<Cursor>,
CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> {
public static final String ARG_DATA_URI = "uri";
@@ -76,6 +75,14 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
// for retrieving the key were working on
private Uri mDataUri;
private Proof mProof;
// for CryptoOperationHelper,Callback
private String mKeybaseProof;
private String mKeybaseFingerprint;
private CryptoOperationHelper<KeybaseVerificationParcel, KeybaseVerificationResult>
mKeybaseOpHelper;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
@@ -349,112 +356,130 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
}
private void verify(final Proof proof, final String fingerprint) {
Intent intent = new Intent(getActivity(), KeychainService.class);
Bundle data = new Bundle();
intent.setAction(KeychainService.ACTION_VERIFY_KEYBASE_PROOF);
data.putString(KeychainService.KEYBASE_PROOF, proof.toString());
data.putString(KeychainService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint);
intent.putExtra(KeychainService.EXTRA_DATA, data);
mProof = proof;
mKeybaseProof = proof.toString();
mKeybaseFingerprint = fingerprint;
mProofVerifyDetail.setVisibility(View.GONE);
// Create a new Messenger for the communication back after proof work is done
ServiceProgressHandler handler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
mKeybaseOpHelper = new CryptoOperationHelper<>(this, this,
R.string.progress_verifying_signature);
mKeybaseOpHelper.cryptoOperation();
}
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
Bundle returnData = message.getData();
String msg = returnData.getString(ServiceProgressHandler.DATA_MESSAGE);
SpannableStringBuilder ssb = new SpannableStringBuilder();
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mKeybaseOpHelper != null) {
mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data);
}
}
if ((msg != null) && msg.equals("OK")) {
// CryptoOperationHelper.Callback methods
@Override
public KeybaseVerificationParcel createOperationInput() {
return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint);
}
//yay
String proofUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PROOF_URL);
String presenceUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_URL);
String presenceLabel = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL);
@Override
public void onCryptoOperationSuccess(KeybaseVerificationResult result) {
String proofLabel;
switch (proof.getType()) {
case Proof.PROOF_TYPE_TWITTER:
proofLabel = getString(R.string.keybase_twitter_proof);
break;
case Proof.PROOF_TYPE_DNS:
proofLabel = getString(R.string.keybase_dns_proof);
break;
case Proof.PROOF_TYPE_WEB_SITE:
proofLabel = getString(R.string.keybase_web_site_proof);
break;
case Proof.PROOF_TYPE_GITHUB:
proofLabel = getString(R.string.keybase_github_proof);
break;
case Proof.PROOF_TYPE_REDDIT:
proofLabel = getString(R.string.keybase_reddit_proof);
break;
default:
proofLabel = getString(R.string.keybase_a_post);
break;
}
result.createNotify(getActivity()).show();
ssb.append(getString(R.string.keybase_proof_succeeded));
StyleSpan bold = new StyleSpan(Typeface.BOLD);
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append("\n\n");
int length = ssb.length();
ssb.append(proofLabel);
if (proofUrl != null) {
URLSpan postLink = new URLSpan(proofUrl);
ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (Proof.PROOF_TYPE_DNS == proof.getType()) {
ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
} else {
ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
}
length = ssb.length();
URLSpan presenceLink = new URLSpan(presenceUrl);
ssb.append(presenceLabel);
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
ssb.append(", ").
append(getString(R.string.keybase_reddit_attribution)).
append("").append(proof.getHandle()).append("”, ");
}
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
} else {
// verification failed!
msg = returnData.getString(ServiceProgressHandler.DATA_ERROR);
ssb.append(getString(R.string.keybase_proof_failure));
if (msg == null) {
msg = getString(R.string.keybase_unknown_proof_failure);
}
StyleSpan bold = new StyleSpan(Typeface.BOLD);
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append("\n\n").append(msg);
}
mProofVerifyHeader.setVisibility(View.VISIBLE);
mProofVerifyDetail.setVisibility(View.VISIBLE);
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
mProofVerifyDetail.setText(ssb);
}
}
};
String proofUrl = result.mProofUrl;
String presenceUrl = result.mPresenceUrl;
String presenceLabel = result.mPresenceLabel;
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(handler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
Proof proof = mProof; // TODO: should ideally be contained in result
// show progress dialog
handler.showProgressDialog(
getString(R.string.progress_verifying_signature),
ProgressDialog.STYLE_HORIZONTAL, false
);
String proofLabel;
switch (proof.getType()) {
case Proof.PROOF_TYPE_TWITTER:
proofLabel = getString(R.string.keybase_twitter_proof);
break;
case Proof.PROOF_TYPE_DNS:
proofLabel = getString(R.string.keybase_dns_proof);
break;
case Proof.PROOF_TYPE_WEB_SITE:
proofLabel = getString(R.string.keybase_web_site_proof);
break;
case Proof.PROOF_TYPE_GITHUB:
proofLabel = getString(R.string.keybase_github_proof);
break;
case Proof.PROOF_TYPE_REDDIT:
proofLabel = getString(R.string.keybase_reddit_proof);
break;
default:
proofLabel = getString(R.string.keybase_a_post);
break;
}
// start service with intent
getActivity().startService(intent);
SpannableStringBuilder ssb = new SpannableStringBuilder();
ssb.append(getString(R.string.keybase_proof_succeeded));
StyleSpan bold = new StyleSpan(Typeface.BOLD);
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append("\n\n");
int length = ssb.length();
ssb.append(proofLabel);
if (proofUrl != null) {
URLSpan postLink = new URLSpan(proofUrl);
ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (Proof.PROOF_TYPE_DNS == proof.getType()) {
ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
} else {
ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
}
length = ssb.length();
URLSpan presenceLink = new URLSpan(presenceUrl);
ssb.append(presenceLabel);
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned
.SPAN_EXCLUSIVE_EXCLUSIVE);
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
ssb.append(", ").
append(getString(R.string.keybase_reddit_attribution)).
append("").append(proof.getHandle()).append("”, ");
}
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
displaySpannableResult(ssb);
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(KeybaseVerificationResult result) {
result.createNotify(getActivity()).show();
SpannableStringBuilder ssb = new SpannableStringBuilder();
ssb.append(getString(R.string.keybase_proof_failure));
String msg = getString(result.getLog().getLast().mType.mMsgId);
if (msg == null) {
msg = getString(R.string.keybase_unknown_proof_failure);
}
StyleSpan bold = new StyleSpan(Typeface.BOLD);
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append("\n\n").append(msg);
displaySpannableResult(ssb);
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
private void displaySpannableResult(SpannableStringBuilder ssb) {
mProofVerifyHeader.setVisibility(View.VISIBLE);
mProofVerifyDetail.setVisibility(View.VISIBLE);
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
mProofVerifyDetail.setText(ssb);
}
}

View File

@@ -21,12 +21,8 @@ package org.sufficientlysecure.keychain.ui;
import java.nio.ByteBuffer;
import java.util.Arrays;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -39,16 +35,16 @@ import android.widget.TextView;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class ViewKeyYubiKeyFragment extends Fragment
public class ViewKeyYubiKeyFragment
extends CryptoOperationFragment<PromoteKeyringParcel, PromoteKeyResult>
implements LoaderCallbacks<Cursor> {
public static final String ARG_MASTER_KEY_ID = "master_key_id";
@@ -60,6 +56,8 @@ public class ViewKeyYubiKeyFragment extends Fragment
private String mUserId;
private byte[] mCardAid;
private long mMasterKeyId;
private long[] mSubKeyIds;
private Button vButton;
private TextView vStatus;
@@ -127,50 +125,15 @@ public class ViewKeyYubiKeyFragment extends Fragment
}
public void promoteToSecretKey() {
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
PromoteKeyResult result =
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
result.createNotify(getActivity()).show();
}
}
};
// Send all information needed to service to decrypt in other thread
Intent intent = new Intent(getActivity(), KeychainService.class);
// fill values for this action
intent.setAction(KeychainService.ACTION_PROMOTE_KEYRING);
Bundle data = new Bundle();
data.putLong(KeychainService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
data.putByteArray(KeychainService.PROMOTE_CARD_AID, mCardAid);
long[] subKeyIds = new long[mFingerprints.length];
for (int i = 0; i < subKeyIds.length; i++) {
subKeyIds[i] = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[i]);
}
data.putLongArray(KeychainService.PROMOTE_SUBKEY_IDS, subKeyIds);
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// start service with intent
getActivity().startService(intent);
// mMasterKeyId and mCardAid are already set
mSubKeyIds = subKeyIds;
cryptoOperation();
}
public static final String[] PROJECTION = new String[]{
@@ -240,4 +203,14 @@ public class ViewKeyYubiKeyFragment extends Fragment
public void onLoaderReset(Loader<Cursor> loader) {
}
@Override
public PromoteKeyringParcel createOperationInput() {
return new PromoteKeyringParcel(mMasterKeyId, mCardAid, mSubKeyIds);
}
@Override
protected void onCryptoOperationResult(PromoteKeyResult result) {
result.createNotify(getActivity()).show();
}
}

View File

@@ -33,6 +33,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Highlighter;
@@ -92,8 +93,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
/** This method returns a list of all selected entries, with public keys sorted
* before secret keys, see ImportExportOperation for specifics.
* @see org.sufficientlysecure.keychain.operations.ImportExportOperation
* before secret keys, see ImportOperation for specifics.
* @see ImportOperation
*/
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
ArrayList<ImportKeysListEntry> result = new ArrayList<>();

View File

@@ -36,7 +36,8 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
mCachedActionsParcel = null;
}
protected abstract T createOperationInput();
@Override
public abstract T createOperationInput();
protected T getCachedActionsParcel() {
return mCachedActionsParcel;
@@ -46,7 +47,7 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
mCachedActionsParcel = cachedActionsParcel;
}
protected void onCryptoOperationCancelled() {
public void onCryptoOperationCancelled() {
mCachedActionsParcel = null;
}

View File

@@ -18,217 +18,78 @@
package org.sufficientlysecure.keychain.ui.base;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.service.KeychainNewService;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
/**
* All fragments executing crypto operations need to extend this class.
*/
public abstract class CryptoOperationFragment <T extends Parcelable, S extends OperationResult>
extends Fragment {
public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult>
extends Fragment implements CryptoOperationHelper.Callback<T, S> {
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
public static final int REQUEST_CODE_NFC = 0x00008002;
private CryptoOperationHelper<T, S> mOperationHelper;
private void initiateInputActivity(RequiredInputParcel requiredInput) {
public CryptoOperationFragment() {
switch (requiredInput.mType) {
case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
Intent intent = new Intent(getActivity(), NfcOperationActivity.class);
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
startActivityForResult(intent, REQUEST_CODE_NFC);
return;
}
case PASSPHRASE:
case PASSPHRASE_SYMMETRIC: {
Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
return;
}
}
throw new RuntimeException("Unhandled pending result!");
mOperationHelper = new CryptoOperationHelper<>(this, this);
}
public void setProgressMessageResource(int id) {
mOperationHelper.setProgressMessageResource(id);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_CANCELED) {
onCryptoOperationCancelled();
return;
}
switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) {
CryptoInputParcel cryptoInput =
data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
cryptoOperation(cryptoInput);
return;
}
break;
}
case REQUEST_CODE_NFC: {
if (resultCode == Activity.RESULT_OK && data != null) {
CryptoInputParcel cryptoInput =
data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
cryptoOperation(cryptoInput);
return;
}
break;
}
default: {
super.onActivityResult(requestCode, resultCode, data);
}
}
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
}
protected void dismissProgress() {
ProgressDialogFragment progressDialogFragment =
(ProgressDialogFragment) getFragmentManager().findFragmentByTag("progressDialog");
if (progressDialogFragment == null) {
return;
}
progressDialogFragment.dismissAllowingStateLoss();
}
protected abstract T createOperationInput();
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
cryptoOperation(cryptoInput, true);
}
@Override
public abstract T createOperationInput();
protected void cryptoOperation() {
cryptoOperation(new CryptoInputParcel());
}
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
cryptoOperation(cryptoInput);
}
protected void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
T operationInput = createOperationInput();
if (operationInput == null) {
return;
}
// Send all information needed to service to edit key in other thread
Intent intent = new Intent(getActivity(), KeychainNewService.class);
intent.putExtra(KeychainNewService.EXTRA_OPERATION_INPUT, operationInput);
intent.putExtra(KeychainNewService.EXTRA_CRYPTO_INPUT, cryptoInput);
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final OperationResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
onHandleResult(result);
}
}
@Override
protected void onSetProgress(String msg, int progress, int max) {
// allow handling of progress in fragment, or delegate upwards
if ( ! onCryptoSetProgress(msg, progress, max)) {
super.onSetProgress(msg, progress, max);
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
if (showProgress) {
saveHandler.showProgressDialog(
getString(R.string.progress_building_key),
ProgressDialog.STYLE_HORIZONTAL, false);
}
getActivity().startService(intent);
mOperationHelper.cryptoOperation(cryptoInput, showProgress);
}
protected void onCryptoOperationResult(S result) {
if (result.success()) {
onCryptoOperationSuccess(result);
} else {
onCryptoOperationError(result);
}
}
abstract protected void onCryptoOperationSuccess(S result);
protected void onCryptoOperationError(S result) {
result.createNotify(getActivity()).show();
}
protected void onCryptoOperationCancelled() {
}
public void onHandleResult(OperationResult result) {
if (result instanceof InputPendingResult) {
InputPendingResult pendingResult = (InputPendingResult) result;
if (pendingResult.isPending()) {
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
initiateInputActivity(requiredInput);
return;
}
}
dismissProgress();
try {
// noinspection unchecked, because type erasure :(
onCryptoOperationResult((S) result);
} catch (ClassCastException e) {
throw new AssertionError("bad return class ("
+ result.getClass().getSimpleName() + "), this is a programming error!");
}
}
protected boolean onCryptoSetProgress(String msg, int progress, int max) {
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
@Override
public void onCryptoOperationError(S result) {
onCryptoOperationResult(result);
result.createNotify(getActivity()).show();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationSuccess(S result) {
onCryptoOperationResult(result);
}
/**
*
* To be overriden by subclasses, if desired. Provides a way to access the method by the
* same name in CryptoOperationHelper, if super.onCryptoOperationSuccess and
* super.onCryptoOperationError are called at the start of the respective functions in the
* subclass overriding them
* @param result
*/
protected void onCryptoOperationResult(S result) {
}
}

View File

@@ -0,0 +1,326 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.base;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
/**
* Designed to be integrated into activities or fragments used for CryptoOperations.
* Encapsulates the execution of a crypto operation and handling of input pending cases.s
*
* @param <T> The type of input parcel sent to the operation
* @param <S> The type of result returned by the operation
*/
public class CryptoOperationHelper<T extends Parcelable, S extends OperationResult> {
public interface Callback <T extends Parcelable, S extends OperationResult> {
T createOperationInput();
void onCryptoOperationSuccess(S result);
void onCryptoOperationCancelled();
void onCryptoOperationError(S result);
boolean onCryptoSetProgress(String msg, int progress, int max);
}
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
public static final int REQUEST_CODE_NFC = 0x00008002;
// keeps track of request code used to start an activity from this CryptoOperationHelper.
// this is necessary when multiple CryptoOperationHelpers are used in the same fragment/activity
// otherwise all CryptoOperationHandlers may respond to the same onActivityResult
private int mRequestedCode = -1;
private int mProgressMessageResource;
private FragmentActivity mActivity;
private Fragment mFragment;
private Callback<T, S> mCallback;
private boolean mUseFragment; // short hand for mActivity == null
/**
* If OperationHelper is being integrated into an activity
*
* @param activity
*/
public CryptoOperationHelper(FragmentActivity activity, Callback<T, S> callback, int progressMessageString) {
mActivity = activity;
mUseFragment = false;
mCallback = callback;
mProgressMessageResource = progressMessageString;
}
/**
* if OperationHelper is being integrated into a fragment
*
* @param fragment
*/
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback, int progressMessageString) {
mFragment = fragment;
mUseFragment = true;
mProgressMessageResource = progressMessageString;
mCallback = callback;
}
/**
* if OperationHelper is being integrated into a fragment with default message for the progress dialog
*
* @param fragment
*/
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback) {
mFragment = fragment;
mUseFragment = true;
mProgressMessageResource = R.string.progress_building_key;
mCallback = callback;
}
public void setProgressMessageResource(int id) {
mProgressMessageResource = id;
}
private void initiateInputActivity(RequiredInputParcel requiredInput) {
Activity activity = mUseFragment ? mFragment.getActivity() : mActivity;
switch (requiredInput.mType) {
case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
Intent intent = new Intent(activity, NfcOperationActivity.class);
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
mRequestedCode = REQUEST_CODE_NFC;
if (mUseFragment) {
mFragment.startActivityForResult(intent, mRequestedCode);
} else {
mActivity.startActivityForResult(intent, mRequestedCode);
}
return;
}
case PASSPHRASE:
case PASSPHRASE_SYMMETRIC: {
Intent intent = new Intent(activity, PassphraseDialogActivity.class);
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
mRequestedCode = REQUEST_CODE_PASSPHRASE;
if (mUseFragment) {
mFragment.startActivityForResult(intent, mRequestedCode);
} else {
mActivity.startActivityForResult(intent, mRequestedCode);
}
return;
}
}
throw new RuntimeException("Unhandled pending result!");
}
/**
* Attempts the result of an activity started by this helper. Returns true if requestCode is recognized,
* false otherwise.
*
* @param requestCode
* @param resultCode
* @param data
* @return true if requestCode was recognized, false otherwise
*/
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(Constants.TAG, "received activity result in OperationHelper");
if (mRequestedCode != requestCode) {
// this wasn't meant for us to handle
return false;
} else {
mRequestedCode = -1;
}
if (resultCode == Activity.RESULT_CANCELED) {
mCallback.onCryptoOperationCancelled();
return true;
}
switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) {
CryptoInputParcel cryptoInput =
data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
cryptoOperation(cryptoInput);
return true;
}
break;
}
case REQUEST_CODE_NFC: {
if (resultCode == Activity.RESULT_OK && data != null) {
CryptoInputParcel cryptoInput =
data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
cryptoOperation(cryptoInput);
return true;
}
break;
}
default: {
return false;
}
}
return true;
}
protected void dismissProgress() {
FragmentManager fragmentManager =
mUseFragment ? mFragment.getFragmentManager() :
mActivity.getSupportFragmentManager();
if (fragmentManager == null) { // the fragment holding us has died
// fragmentManager was null when used with DialogFragments. (they close on click?)
return;
}
ProgressDialogFragment progressDialogFragment =
(ProgressDialogFragment) fragmentManager.findFragmentByTag(
ServiceProgressHandler.TAG_PROGRESS_DIALOG);
if (progressDialogFragment == null) {
return;
}
progressDialogFragment.dismissAllowingStateLoss();
}
public void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
FragmentActivity activity = mUseFragment ? mFragment.getActivity() : mActivity;
T operationInput = mCallback.createOperationInput();
if (operationInput == null) {
return;
}
// Send all information needed to service to edit key in other thread
Intent intent = new Intent(activity, KeychainService.class);
intent.putExtra(KeychainService.EXTRA_OPERATION_INPUT, operationInput);
intent.putExtra(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final OperationResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
onHandleResult(result);
}
}
@Override
protected void onSetProgress(String msg, int progress, int max) {
// allow handling of progress in fragment, or delegate upwards
if ( ! mCallback.onCryptoSetProgress(msg, progress, max)) {
super.onSetProgress(msg, progress, max);
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
if (showProgress) {
saveHandler.showProgressDialog(
activity.getString(mProgressMessageResource),
ProgressDialog.STYLE_HORIZONTAL, false);
}
activity.startService(intent);
}
public void cryptoOperation(CryptoInputParcel cryptoInputParcel) {
cryptoOperation(cryptoInputParcel, true);
}
public void cryptoOperation() {
cryptoOperation(new CryptoInputParcel());
}
protected void onCryptoOperationResult(S result) {
if (result.success()) {
mCallback.onCryptoOperationSuccess(result);
} else {
mCallback.onCryptoOperationError(result);
}
}
public void onHandleResult(OperationResult result) {
Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
if (result instanceof InputPendingResult) {
InputPendingResult pendingResult = (InputPendingResult) result;
if (pendingResult.isPending()) {
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
initiateInputActivity(requiredInput);
return;
}
}
dismissProgress();
try {
// noinspection unchecked, because type erasure :(
onCryptoOperationResult((S) result);
} catch (ClassCastException e) {
throw new AssertionError("bad return class ("
+ result.getClass().getSimpleName() + "), this is a programming error!");
}
}
}

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
@@ -33,16 +32,20 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.HashMap;
public class DeleteKeyDialogFragment extends DialogFragment {
public class DeleteKeyDialogFragment extends DialogFragment
implements CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult> {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
@@ -52,6 +55,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
private TextView mMainMessage;
private View mInflateView;
private Messenger mMessenger;
// for CryptoOperationHelper.Callback
private long[] mMasterKeyIds;
private boolean mHasSecret;
private CryptoOperationHelper<DeleteKeyringParcel, DeleteResult> mDeleteOpHelper;
/**
* Creates new instance of this delete file dialog fragment
*/
@@ -67,10 +77,18 @@ public class DeleteKeyDialogFragment extends DialogFragment {
return frag;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mDeleteOpHelper != null) {
mDeleteOpHelper.handleActivityResult(requestCode, resultCode, data);
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
@@ -129,47 +147,16 @@ public class DeleteKeyDialogFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
// Send all information needed to service to import key in other thread
Intent intent = new Intent(getActivity(), KeychainService.class);
mMasterKeyIds = masterKeyIds;
mHasSecret = hasSecret;
intent.setAction(KeychainService.ACTION_DELETE);
// Message is received after importing is done in KeychainService
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
super.handleMessage(message);
// handle messages by standard KeychainIntentServiceHandler first
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
try {
Message msg = Message.obtain();
msg.copyFrom(message);
messenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "messenger error", e);
}
}
}
};
// fill values for this action
Bundle data = new Bundle();
data.putLongArray(KeychainService.DELETE_KEY_LIST, masterKeyIds);
data.putBoolean(KeychainService.DELETE_IS_SECRET, hasSecret);
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(getString(R.string.progress_deleting),
ProgressDialog.STYLE_HORIZONTAL, true);
// start service with intent
getActivity().startService(intent);
dismiss();
mDeleteOpHelper = new CryptoOperationHelper<>
(DeleteKeyDialogFragment.this, DeleteKeyDialogFragment.this,
R.string.progress_deleting);
mDeleteOpHelper.cryptoOperation();
// do NOT dismiss here, it'll give
// OperationHelper a null fragmentManager
// dismiss();
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -183,4 +170,42 @@ public class DeleteKeyDialogFragment extends DialogFragment {
return builder.show();
}
@Override
public DeleteKeyringParcel createOperationInput() {
return new DeleteKeyringParcel(mMasterKeyIds, mHasSecret);
}
@Override
public void onCryptoOperationSuccess(DeleteResult result) {
handleResult(result);
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(DeleteResult result) {
handleResult(result);
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
public void handleResult(DeleteResult result) {
try {
Bundle data = new Bundle();
data.putParcelable(OperationResult.EXTRA_RESULT, result);
Message msg = Message.obtain();
msg.arg1 = ServiceProgressHandler.MessageStatus.OKAY.ordinal();
msg.setData(data);
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "messenger error", e);
}
dismiss();
}
}

View File

@@ -18,15 +18,14 @@
package org.sufficientlysecure.keychain.util;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Messenger;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import java.util.ArrayList;
import java.util.HashSet;
@@ -35,24 +34,35 @@ import java.util.Locale;
import java.util.Set;
public class EmailKeyHelper {
// to import keys, simply use CryptoOperationHelper with this callback
public abstract class ImportContactKeysCallback
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static void importContacts(Context context, Messenger messenger) {
importAll(context, messenger, ContactHelper.getContactMails(context));
}
private ArrayList<ParcelableKeyRing> mKeyList;
private String mKeyserver;
public static void importAll(Context context, Messenger messenger, List<String> mails) {
// Collect all candidates as ImportKeysListEntry (set for deduplication)
Set<ImportKeysListEntry> entries = new HashSet<>();
for (String mail : mails) {
entries.addAll(getEmailKeys(context, mail));
public ImportContactKeysCallback(Context context, String keyserver) {
this(context, ContactHelper.getContactMails(context), keyserver);
}
// Put them in a list and import
ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
for (ImportKeysListEntry entry : entries) {
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
public ImportContactKeysCallback(Context context, List<String> mails, String keyserver) {
Set<ImportKeysListEntry> entries = new HashSet<>();
for (String mail : mails) {
entries.addAll(getEmailKeys(context, mail));
}
// Put them in a list and import
ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
for (ImportKeysListEntry entry : entries) {
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
}
mKeyList = keys;
mKeyserver = keyserver;
}
@Override
public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver);
}
importKeys(context, messenger, keys);
}
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail) {
@@ -78,17 +88,6 @@ public class EmailKeyHelper {
return keys;
}
private static void importKeys(Context context, Messenger messenger, ArrayList<ParcelableKeyRing> keys) {
Intent importIntent = new Intent(context, KeychainService.class);
importIntent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
importData.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keys);
importIntent.putExtra(KeychainService.EXTRA_DATA, importData);
importIntent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
context.startService(importIntent);
}
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) {
Set<ImportKeysListEntry> keys = new HashSet<>();
try {

View File

@@ -17,26 +17,26 @@
package org.sufficientlysecure.keychain.util;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import java.io.File;
public class ExportHelper {
public class ExportHelper
implements CryptoOperationHelper.Callback <ExportKeyringParcel, ExportResult> {
protected File mExportFile;
FragmentActivity mActivity;
private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mExportOpHelper;
private boolean mExportSecret;
private long[] mMasterKeyIds;
public ExportHelper(FragmentActivity activity) {
super();
this.mActivity = activity;
@@ -71,60 +71,43 @@ public class ExportHelper {
}, mActivity.getSupportFragmentManager() ,title, message, exportFile, checkMsg);
}
// TODO: If ExportHelper requires pending data (see CryptoOPerationHelper), activities using
// TODO: this class should be able to call mExportOpHelper.handleActivity
/**
* Export keys
*/
public void exportKeys(long[] masterKeyIds, boolean exportSecret) {
Log.d(Constants.TAG, "exportKeys started");
mExportSecret = exportSecret;
mMasterKeyIds = masterKeyIds; // if masterKeyIds is null it means export all
// Send all information needed to service to export key in other thread
final Intent intent = new Intent(mActivity, KeychainService.class);
intent.setAction(KeychainService.ACTION_EXPORT_KEYRING);
// fill values for this action
Bundle data = new Bundle();
data.putString(KeychainService.EXPORT_FILENAME, mExportFile.getAbsolutePath());
data.putBoolean(KeychainService.EXPORT_SECRET, exportSecret);
if (masterKeyIds == null) {
data.putBoolean(KeychainService.EXPORT_ALL, true);
} else {
data.putLongArray(KeychainService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
}
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Message is received after exporting is done in KeychainService
ServiceProgressHandler exportHandler = new ServiceProgressHandler(mActivity) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
// get returned data bundle
Bundle data = message.getData();
ExportResult result = data.getParcelable(ExportResult.EXTRA_RESULT);
result.createNotify(mActivity).show();
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(exportHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
exportHandler.showProgressDialog(
mActivity.getString(R.string.progress_exporting),
ProgressDialog.STYLE_HORIZONTAL, false
);
// start service with intent
mActivity.startService(intent);
mExportOpHelper = new CryptoOperationHelper(mActivity, this, R.string.progress_exporting);
mExportOpHelper.cryptoOperation();
}
@Override
public ExportKeyringParcel createOperationInput() {
return new ExportKeyringParcel(mMasterKeyIds, mExportSecret, mExportFile.getAbsolutePath());
}
@Override
public void onCryptoOperationSuccess(ExportResult result) {
result.createNotify(mActivity).show();
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(ExportResult result) {
result.createNotify(mActivity).show();
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
}

View File

@@ -1214,6 +1214,15 @@
<string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
<!-- Messages for Keybase Verification operation -->
<string name="msg_keybase_verification">"Attempting keybase verification for %s"</string>
<string name="msg_keybase_error_no_prover">"No proof checker found for %s"</string>
<string name="msg_keybase_error_fetching_evidence">"Problem with fetching proof"</string>
<string name="msg_keybase_error_key_mismatch">"Key fingerprint doesnt match that in proof post"</string>
<string name="msg_keybase_error_dns_fail">"DNS TXT Record retrieval failed"</string>
<string name="msg_keybase_error_specific">"%s"</string>
<string name="msg_keybase_error_msg_payload_mismatch">"Decrypted proof post does not match expected value"</string>
<!-- Messages for Export Log operation -->
<string name="msg_export_log_start">"Exporting log"</string>
<string name="msg_export_log_error_fopen">"Error opening file"</string>

View File

@@ -30,7 +30,6 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
@@ -127,7 +126,7 @@ public class ExportTest {
@Test
public void testExportAll() throws Exception {
ImportExportOperation op = new ImportExportOperation(RuntimeEnvironment.application,
ExportOperation op = new ExportOperation(RuntimeEnvironment.application,
new ProviderHelper(RuntimeEnvironment.application), null);
// make sure there is a local cert (so the later checks that there are none are meaningful)