From 45dcc7d070adbbdddd954d614b4d202d83eaee2b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 10 Oct 2014 19:27:33 +0200 Subject: move operations into ops package, introduce BaseOperation class --- .../keychain/pgp/PgpCertifyOperation.java | 149 ---------- .../keychain/pgp/PgpImportExport.java | 305 --------------------- .../keychain/pgp/ops/BaseOperation.java | 55 ++++ .../keychain/pgp/ops/ImportExportOperation.java | 279 +++++++++++++++++++ .../keychain/pgp/ops/PgpCertifyOperation.java | 145 ++++++++++ .../keychain/provider/ProviderHelper.java | 6 +- .../keychain/service/KeychainIntentService.java | 18 +- 7 files changed, 491 insertions(+), 466 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/BaseOperation.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/PgpCertifyOperation.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java deleted file mode 100644 index b0c801a93..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.sufficientlysecure.keychain.pgp; - -import org.spongycastle.openpgp.PGPException; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; -import org.sufficientlysecure.keychain.service.CertifyActionsParcel; -import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; -import org.sufficientlysecure.keychain.service.results.CertifyResult; -import org.sufficientlysecure.keychain.service.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.service.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.service.results.SaveKeyringResult; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicBoolean; - -public class PgpCertifyOperation { - - private AtomicBoolean mCancelled; - - private ProviderHelper mProviderHelper; - - public PgpCertifyOperation(ProviderHelper providerHelper, AtomicBoolean cancelled) { - mProviderHelper = providerHelper; - - mCancelled = cancelled; - } - - private boolean checkCancelled() { - return mCancelled != null && mCancelled.get(); - } - - public CertifyResult certify(CertifyActionsParcel parcel, String passphrase) { - - OperationLog log = new OperationLog(); - log.add(LogType.MSG_CRT, 0); - - // Retrieve and unlock secret key - CanonicalizedSecretKey certificationKey; - try { - log.add(LogType.MSG_CRT_MASTER_FETCH, 1); - CanonicalizedSecretKeyRing secretKeyRing = - mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId); - log.add(LogType.MSG_CRT_UNLOCK, 1); - certificationKey = secretKeyRing.getSecretKey(); - if (!certificationKey.unlock(passphrase)) { - log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); - return new CertifyResult(CertifyResult.RESULT_ERROR, log); - } - } catch (PgpGeneralException e) { - log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); - return new CertifyResult(CertifyResult.RESULT_ERROR, log); - } catch (NotFoundException e) { - log.add(LogType.MSG_CRT_ERROR_MASTER_NOT_FOUND, 2); - return new CertifyResult(CertifyResult.RESULT_ERROR, log); - } - - ArrayList certifiedKeys = new ArrayList(); - - log.add(LogType.MSG_CRT_CERTIFYING, 1); - - int certifyOk = 0, certifyError = 0; - - // Work through all requested certifications - for (CertifyAction action : parcel.mCertifyActions) { - - // Check if we were cancelled - if (checkCancelled()) { - log.add(LogType.MSG_OPERATION_CANCELLED, 0); - return new CertifyResult(CertifyResult.RESULT_CANCELLED, log); - } - - try { - - if (action.mUserIds == null) { - log.add(LogType.MSG_CRT_CERTIFY_ALL, 2, - KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); - } else { - log.add(LogType.MSG_CRT_CERTIFY_SOME, 2, action.mUserIds.size(), - KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); - } - - CanonicalizedPublicKeyRing publicRing = - mProviderHelper.getCanonicalizedPublicKeyRing(action.mMasterKeyId); - - UncachedKeyRing certifiedKey = certificationKey.certifyUserIds(publicRing, action.mUserIds, null, null); - certifiedKeys.add(certifiedKey); - - } catch (NotFoundException e) { - certifyError += 1; - log.add(LogType.MSG_CRT_WARN_NOT_FOUND, 3); - } catch (PGPException e) { - certifyError += 1; - log.add(LogType.MSG_CRT_WARN_CERT_FAILED, 3); - Log.e(Constants.TAG, "Encountered PGPException during certification", e); - } - - } - - log.add(LogType.MSG_CRT_SAVING, 1); - - // Check if we were cancelled - if (checkCancelled()) { - log.add(LogType.MSG_OPERATION_CANCELLED, 0); - return new CertifyResult(CertifyResult.RESULT_CANCELLED, log); - } - - // Write all certified keys into the database - for (UncachedKeyRing certifiedKey : certifiedKeys) { - - // Check if we were cancelled - if (checkCancelled()) { - log.add(LogType.MSG_OPERATION_CANCELLED, 0); - return new CertifyResult(CertifyResult.RESULT_CANCELLED, log, certifyOk, certifyError); - } - - log.add(LogType.MSG_CRT_SAVE, 2, - KeyFormattingUtils.convertKeyIdToHex(certifiedKey.getMasterKeyId())); - // store the signed key in our local cache - mProviderHelper.clearLog(); - SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey); - - if (result.success()) { - certifyOk += 1; - } else { - log.add(LogType.MSG_CRT_WARN_SAVE_FAILED, 3); - } - - log.add(result, 2); - - // TODO do something with import results - - } - - if (certifyOk == 0) { - log.add(LogType.MSG_CRT_ERROR_NOTHING, 0); - return new CertifyResult(CertifyResult.RESULT_ERROR, log, certifyOk, certifyError); - } - - log.add(LogType.MSG_CRT_SUCCESS, 0); - return new CertifyResult(CertifyResult.RESULT_OK, log, certifyOk, certifyError); - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java deleted file mode 100644 index 9b21b49ce..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann - * Copyright (C) 2010-2014 Thialfihar - * - * 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 . - */ - -package org.sufficientlysecure.keychain.pgp; - -import android.content.Context; -import android.os.Bundle; -import android.os.Environment; - -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.openpgp.PGPException; -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.keyimport.ParcelableKeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.service.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.service.results.ImportKeyResult; -import org.sufficientlysecure.keychain.service.results.SaveKeyringResult; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.ProgressScaler; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicBoolean; - -public class PgpImportExport { - - private Context mContext; - private Progressable mProgressable; - private AtomicBoolean mCancelled; - - private ProviderHelper mProviderHelper; - - public PgpImportExport(Context context, ProviderHelper providerHelper, Progressable progressable) { - super(); - this.mContext = context; - this.mProgressable = progressable; - this.mProviderHelper = providerHelper; - } - - public PgpImportExport(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) { - super(); - mContext = context; - mProgressable = progressable; - mProviderHelper = providerHelper; - mCancelled = cancelled; - } - - public void updateProgress(int message, int current, int total) { - if (mProgressable != null) { - mProgressable.setProgress(message, current, total); - } - } - - public void updateProgress(String message, int current, int total) { - if (mProgressable != null) { - mProgressable.setProgress(message, current, total); - } - } - - public void updateProgress(int current, int total) { - if (mProgressable != null) { - mProgressable.setProgress(current, total); - } - } - - public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing 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(Iterator entries, int num) { - updateProgress(R.string.progress_importing, 0, 100); - - OperationLog log = new OperationLog(); - - // If there aren't even any keys, do nothing here. - if (entries == null || !entries.hasNext()) { - return new ImportKeyResult( - ImportKeyResult.RESULT_FAIL_NOTHING, log, 0, 0, 0, 0, - new long[]{}); - } - - int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0; - ArrayList importedMasterKeyIds = new ArrayList(); - - int position = 0; - double progSteps = 100.0 / num; - - // iterate over all entries - while (entries.hasNext()) { - ParcelableKeyRing entry = entries.next(); - - // Has this action been cancelled? If so, don't proceed any further - if (mCancelled != null && mCancelled.get()) { - break; - } - - try { - UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes()); - - String expectedFp = entry.getExpectedFingerprint(); - if(expectedFp != null) { - if(!KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint()).equals(expectedFp)) { - Log.d(Constants.TAG, "fingerprint: " + KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint())); - Log.d(Constants.TAG, "expected fingerprint: " + expectedFp); - Log.e(Constants.TAG, "Actual key fingerprint is not the same as expected!"); - badKeys += 1; - continue; - } else { - Log.d(Constants.TAG, "Actual key fingerprint matches expected one."); - } - } - - SaveKeyringResult result; - mProviderHelper.clearLog(); - if (key.isSecret()) { - result = mProviderHelper.saveSecretKeyRing(key, - new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100)); - } else { - result = mProviderHelper.savePublicKeyRing(key, - new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100)); - } - if (!result.success()) { - badKeys += 1; - } else if (result.updated()) { - oldKeys += 1; - importedMasterKeyIds.add(key.getMasterKeyId()); - } else { - newKeys += 1; - if (key.isSecret()) { - secret += 1; - } - importedMasterKeyIds.add(key.getMasterKeyId()); - } - - log.add(result, 1); - - } catch (IOException e) { - Log.e(Constants.TAG, "Encountered bad key on import!", e); - ++badKeys; - } catch (PgpGeneralException e) { - Log.e(Constants.TAG, "Encountered bad key on import!", e); - ++badKeys; - } - // update progress - position++; - } - - int resultType = 0; - // special return case: no new keys at all - if (badKeys == 0 && newKeys == 0 && oldKeys == 0) { - resultType = ImportKeyResult.RESULT_FAIL_NOTHING; - } else { - if (newKeys > 0) { - resultType |= ImportKeyResult.RESULT_OK_NEWKEYS; - } - if (oldKeys > 0) { - resultType |= ImportKeyResult.RESULT_OK_UPDATED; - } - if (badKeys > 0) { - resultType |= ImportKeyResult.RESULT_WITH_ERRORS; - if (newKeys == 0 && oldKeys == 0) { - resultType |= ImportKeyResult.RESULT_ERROR; - } - } - if (log.containsWarnings()) { - resultType |= ImportKeyResult.RESULT_WARNINGS; - } - } - if (mCancelled != null && mCancelled.get()) { - log.add(LogType.MSG_OPERATION_CANCELLED, 0); - resultType |= ImportKeyResult.RESULT_CANCELLED; - } - - // convert to long array - long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()]; - for (int i = 0; i < importedMasterKeyIds.size(); ++i) { - importedMasterKeyIdsArray[i] = importedMasterKeyIds.get(i); - } - - return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret, - importedMasterKeyIdsArray); - } - - public Bundle exportKeyRings(ArrayList publicKeyRingMasterIds, - ArrayList secretKeyRingMasterIds, - OutputStream outStream) throws PgpGeneralException, - PGPException, IOException { - Bundle returnData = new Bundle(); - - int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size(); - int progress = 0; - - updateProgress( - mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, - masterKeyIdsSize), 0, 100); - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new PgpGeneralException( - mContext.getString(R.string.error_external_storage_not_ready)); - } - // For each public masterKey id - for (long pubKeyMasterId : publicKeyRingMasterIds) { - progress++; - // Create an output stream - ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); - String version = PgpHelper.getVersionForHeader(mContext); - if (version != null) { - arOutStream.setHeader("Version", version); - } - - updateProgress(progress * 100 / masterKeyIdsSize, 100); - - try { - CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing( - KeychainContract.KeyRings.buildUnifiedKeyRingUri(pubKeyMasterId) - ); - - ring.encode(arOutStream); - } catch (ProviderHelper.NotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - // TODO: inform user? - } - - arOutStream.close(); - } - - // For each secret masterKey id - for (long secretKeyMasterId : secretKeyRingMasterIds) { - progress++; - // Create an output stream - ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); - String version = PgpHelper.getVersionForHeader(mContext); - if (version != null) { - arOutStream.setHeader("Version", version); - } - - updateProgress(progress * 100 / masterKeyIdsSize, 100); - - try { - CanonicalizedSecretKeyRing secretKeyRing = - mProviderHelper.getCanonicalizedSecretKeyRing(secretKeyMasterId); - secretKeyRing.encode(arOutStream); - } catch (ProviderHelper.NotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - // TODO: inform user? - } - - arOutStream.close(); - } - - returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize); - - updateProgress(R.string.progress_done, 100, 100); - - return returnData; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/BaseOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/BaseOperation.java new file mode 100644 index 000000000..811df7563 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/BaseOperation.java @@ -0,0 +1,55 @@ +package org.sufficientlysecure.keychain.pgp.ops; + +import android.content.Context; + +import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.provider.ProviderHelper; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class BaseOperation { + + final public Context mContext; + final public Progressable mProgressable; + final public AtomicBoolean mCancelled; + + final public ProviderHelper mProviderHelper; + + // TODO do we really need the context in these operations? + public BaseOperation(Context context, ProviderHelper providerHelper, Progressable progressable) { + this.mContext = context; + this.mProgressable = progressable; + this.mProviderHelper = providerHelper; + mCancelled = null; + } + + public BaseOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) { + mContext = context; + mProgressable = progressable; + mProviderHelper = providerHelper; + mCancelled = cancelled; + } + + public void updateProgress(int message, int current, int total) { + if (mProgressable != null) { + mProgressable.setProgress(message, current, total); + } + } + + public void updateProgress(String message, int current, int total) { + if (mProgressable != null) { + mProgressable.setProgress(message, current, total); + } + } + + public void updateProgress(int current, int total) { + if (mProgressable != null) { + mProgressable.setProgress(current, total); + } + } + + protected boolean checkCancelled() { + return mCancelled != null && mCancelled.get(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java new file mode 100644 index 000000000..32151c4d9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2010-2014 Thialfihar + * + * 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 . + */ + +package org.sufficientlysecure.keychain.pgp.ops; + +import android.content.Context; +import android.os.Bundle; +import android.os.Environment; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.openpgp.PGPException; +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.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +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; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.service.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.service.results.ImportKeyResult; +import org.sufficientlysecure.keychain.service.results.SaveKeyringResult; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ProgressScaler; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ImportExportOperation extends BaseOperation { + + public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) { + super(context, providerHelper, progressable); + } + + public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) { + super(context, providerHelper, progressable, cancelled); + } + + public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing 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(Iterator entries, int num) { + updateProgress(R.string.progress_importing, 0, 100); + + OperationLog log = new OperationLog(); + + // If there aren't even any keys, do nothing here. + if (entries == null || !entries.hasNext()) { + return new ImportKeyResult( + ImportKeyResult.RESULT_FAIL_NOTHING, log, 0, 0, 0, 0, + new long[]{}); + } + + int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0; + ArrayList importedMasterKeyIds = new ArrayList(); + + int position = 0; + double progSteps = 100.0 / num; + + // iterate over all entries + while (entries.hasNext()) { + ParcelableKeyRing entry = entries.next(); + + // Has this action been cancelled? If so, don't proceed any further + if (mCancelled != null && mCancelled.get()) { + break; + } + + try { + UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes()); + + String expectedFp = entry.getExpectedFingerprint(); + if(expectedFp != null) { + if(!KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint()).equals(expectedFp)) { + Log.d(Constants.TAG, "fingerprint: " + KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint())); + Log.d(Constants.TAG, "expected fingerprint: " + expectedFp); + Log.e(Constants.TAG, "Actual key fingerprint is not the same as expected!"); + badKeys += 1; + continue; + } else { + Log.d(Constants.TAG, "Actual key fingerprint matches expected one."); + } + } + + SaveKeyringResult result; + mProviderHelper.clearLog(); + if (key.isSecret()) { + result = mProviderHelper.saveSecretKeyRing(key, + new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100)); + } else { + result = mProviderHelper.savePublicKeyRing(key, + new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100)); + } + if (!result.success()) { + badKeys += 1; + } else if (result.updated()) { + oldKeys += 1; + importedMasterKeyIds.add(key.getMasterKeyId()); + } else { + newKeys += 1; + if (key.isSecret()) { + secret += 1; + } + importedMasterKeyIds.add(key.getMasterKeyId()); + } + + log.add(result, 1); + + } catch (IOException e) { + Log.e(Constants.TAG, "Encountered bad key on import!", e); + ++badKeys; + } catch (PgpGeneralException e) { + Log.e(Constants.TAG, "Encountered bad key on import!", e); + ++badKeys; + } + // update progress + position++; + } + + int resultType = 0; + // special return case: no new keys at all + if (badKeys == 0 && newKeys == 0 && oldKeys == 0) { + resultType = ImportKeyResult.RESULT_FAIL_NOTHING; + } else { + if (newKeys > 0) { + resultType |= ImportKeyResult.RESULT_OK_NEWKEYS; + } + if (oldKeys > 0) { + resultType |= ImportKeyResult.RESULT_OK_UPDATED; + } + if (badKeys > 0) { + resultType |= ImportKeyResult.RESULT_WITH_ERRORS; + if (newKeys == 0 && oldKeys == 0) { + resultType |= ImportKeyResult.RESULT_ERROR; + } + } + if (log.containsWarnings()) { + resultType |= ImportKeyResult.RESULT_WARNINGS; + } + } + if (mCancelled != null && mCancelled.get()) { + log.add(LogType.MSG_OPERATION_CANCELLED, 0); + resultType |= ImportKeyResult.RESULT_CANCELLED; + } + + // convert to long array + long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()]; + for (int i = 0; i < importedMasterKeyIds.size(); ++i) { + importedMasterKeyIdsArray[i] = importedMasterKeyIds.get(i); + } + + return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret, + importedMasterKeyIdsArray); + } + + public Bundle exportKeyRings(ArrayList publicKeyRingMasterIds, + ArrayList secretKeyRingMasterIds, + OutputStream outStream) throws PgpGeneralException, + PGPException, IOException { + Bundle returnData = new Bundle(); + + int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size(); + int progress = 0; + + updateProgress( + mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, + masterKeyIdsSize), 0, 100); + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new PgpGeneralException( + mContext.getString(R.string.error_external_storage_not_ready)); + } + // For each public masterKey id + for (long pubKeyMasterId : publicKeyRingMasterIds) { + progress++; + // Create an output stream + ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); + String version = PgpHelper.getVersionForHeader(mContext); + if (version != null) { + arOutStream.setHeader("Version", version); + } + + updateProgress(progress * 100 / masterKeyIdsSize, 100); + + try { + CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing( + KeychainContract.KeyRings.buildUnifiedKeyRingUri(pubKeyMasterId) + ); + + ring.encode(arOutStream); + } catch (ProviderHelper.NotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + // TODO: inform user? + } + + arOutStream.close(); + } + + // For each secret masterKey id + for (long secretKeyMasterId : secretKeyRingMasterIds) { + progress++; + // Create an output stream + ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); + String version = PgpHelper.getVersionForHeader(mContext); + if (version != null) { + arOutStream.setHeader("Version", version); + } + + updateProgress(progress * 100 / masterKeyIdsSize, 100); + + try { + CanonicalizedSecretKeyRing secretKeyRing = + mProviderHelper.getCanonicalizedSecretKeyRing(secretKeyMasterId); + secretKeyRing.encode(arOutStream); + } catch (ProviderHelper.NotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + // TODO: inform user? + } + + arOutStream.close(); + } + + returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize); + + updateProgress(R.string.progress_done, 100, 100); + + return returnData; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/PgpCertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/PgpCertifyOperation.java new file mode 100644 index 000000000..63dabc8db --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/PgpCertifyOperation.java @@ -0,0 +1,145 @@ +package org.sufficientlysecure.keychain.pgp.ops; + +import android.content.Context; + +import org.spongycastle.openpgp.PGPException; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; +import org.sufficientlysecure.keychain.service.CertifyActionsParcel; +import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; +import org.sufficientlysecure.keychain.service.results.CertifyResult; +import org.sufficientlysecure.keychain.service.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.service.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.service.results.SaveKeyringResult; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +public class PgpCertifyOperation extends BaseOperation { + + public PgpCertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) { + super(context, providerHelper, progressable, cancelled); + } + + public CertifyResult certify(CertifyActionsParcel parcel, String passphrase) { + + OperationLog log = new OperationLog(); + log.add(LogType.MSG_CRT, 0); + + // Retrieve and unlock secret key + CanonicalizedSecretKey certificationKey; + try { + log.add(LogType.MSG_CRT_MASTER_FETCH, 1); + CanonicalizedSecretKeyRing secretKeyRing = + mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId); + log.add(LogType.MSG_CRT_UNLOCK, 1); + certificationKey = secretKeyRing.getSecretKey(); + if (!certificationKey.unlock(passphrase)) { + log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); + return new CertifyResult(CertifyResult.RESULT_ERROR, log); + } + } catch (PgpGeneralException e) { + log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); + return new CertifyResult(CertifyResult.RESULT_ERROR, log); + } catch (NotFoundException e) { + log.add(LogType.MSG_CRT_ERROR_MASTER_NOT_FOUND, 2); + return new CertifyResult(CertifyResult.RESULT_ERROR, log); + } + + ArrayList certifiedKeys = new ArrayList(); + + log.add(LogType.MSG_CRT_CERTIFYING, 1); + + int certifyOk = 0, certifyError = 0; + + // Work through all requested certifications + for (CertifyAction action : parcel.mCertifyActions) { + + // Check if we were cancelled + if (checkCancelled()) { + log.add(LogType.MSG_OPERATION_CANCELLED, 0); + return new CertifyResult(CertifyResult.RESULT_CANCELLED, log); + } + + try { + + if (action.mUserIds == null) { + log.add(LogType.MSG_CRT_CERTIFY_ALL, 2, + KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); + } else { + log.add(LogType.MSG_CRT_CERTIFY_SOME, 2, action.mUserIds.size(), + KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); + } + + CanonicalizedPublicKeyRing publicRing = + mProviderHelper.getCanonicalizedPublicKeyRing(action.mMasterKeyId); + + UncachedKeyRing certifiedKey = certificationKey.certifyUserIds(publicRing, action.mUserIds, null, null); + certifiedKeys.add(certifiedKey); + + } catch (NotFoundException e) { + certifyError += 1; + log.add(LogType.MSG_CRT_WARN_NOT_FOUND, 3); + } catch (PGPException e) { + certifyError += 1; + log.add(LogType.MSG_CRT_WARN_CERT_FAILED, 3); + Log.e(Constants.TAG, "Encountered PGPException during certification", e); + } + + } + + log.add(LogType.MSG_CRT_SAVING, 1); + + // Check if we were cancelled + if (checkCancelled()) { + log.add(LogType.MSG_OPERATION_CANCELLED, 0); + return new CertifyResult(CertifyResult.RESULT_CANCELLED, log); + } + + // Write all certified keys into the database + for (UncachedKeyRing certifiedKey : certifiedKeys) { + + // Check if we were cancelled + if (checkCancelled()) { + log.add(LogType.MSG_OPERATION_CANCELLED, 0); + return new CertifyResult(CertifyResult.RESULT_CANCELLED, log, certifyOk, certifyError); + } + + log.add(LogType.MSG_CRT_SAVE, 2, + KeyFormattingUtils.convertKeyIdToHex(certifiedKey.getMasterKeyId())); + // store the signed key in our local cache + mProviderHelper.clearLog(); + SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey); + + if (result.success()) { + certifyOk += 1; + } else { + log.add(LogType.MSG_CRT_WARN_SAVE_FAILED, 3); + } + + log.add(result, 2); + + // TODO do something with import results + + } + + if (certifyOk == 0) { + log.add(LogType.MSG_CRT_ERROR_NOTHING, 0); + return new CertifyResult(CertifyResult.RESULT_ERROR, log, certifyOk, certifyError); + } + + log.add(LogType.MSG_CRT_SUCCESS, 0); + return new CertifyResult(CertifyResult.RESULT_OK, log, certifyOk, certifyError); + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 273f9c75f..b99a06ce0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -41,7 +41,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.pgp.PgpImportExport; +import org.sufficientlysecure.keychain.pgp.ops.ImportExportOperation; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; @@ -1090,7 +1090,7 @@ public class ProviderHelper { // 3. Re-Import secret keyrings from cache if (numSecrets > 0) { - new PgpImportExport(mContext, this, + new ImportExportOperation(mContext, this, new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport)) .importKeyRings(itSecrets, numSecrets); } else { @@ -1116,7 +1116,7 @@ public class ProviderHelper { // 4. Re-Import public keyrings from cache if (numPublics > 0) { - new PgpImportExport(mContext, this, + new ImportExportOperation(mContext, this, new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport)) .importKeyRings(itPublics, numPublics); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 90091ce35..14c72970f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -29,7 +29,7 @@ import android.os.RemoteException; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation; +import org.sufficientlysecure.keychain.pgp.ops.PgpCertifyOperation; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.service.results.CertifyResult; @@ -47,7 +47,7 @@ import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.pgp.PgpImportExport; +import org.sufficientlysecure.keychain.pgp.ops.ImportExportOperation; import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.Progressable; @@ -268,7 +268,7 @@ public class KeychainIntentService extends IntentService implements Progressable } ProviderHelper providerHelper = new ProviderHelper(this); - PgpCertifyOperation op = new PgpCertifyOperation(providerHelper, mActionCanceled); + PgpCertifyOperation op = new PgpCertifyOperation(this, providerHelper, this, mActionCanceled); CertifyResult result = op.certify(parcel, passphrase); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); @@ -610,8 +610,8 @@ public class KeychainIntentService extends IntentService implements Progressable outStream = getContentResolver().openOutputStream(outputUri); } - PgpImportExport pgpImportExport = new PgpImportExport(this, new ProviderHelper(this), this); - Bundle resultData = pgpImportExport + ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this); + Bundle resultData = importExportOperation .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds, outStream); if (mActionCanceled.get() && outputFile != null) { @@ -644,9 +644,9 @@ public class KeychainIntentService extends IntentService implements Progressable } ProviderHelper providerHelper = new ProviderHelper(this); - PgpImportExport pgpImportExport = new PgpImportExport( + ImportExportOperation importExportOperation = new ImportExportOperation( this, providerHelper, this, mActionCanceled); - ImportKeyResult result = pgpImportExport.importKeyRings(entries, numEntries); + ImportKeyResult result = importExportOperation.importKeyRings(entries, numEntries); // we do this even on failure or cancellation! if (result.mSecret > 0) { @@ -761,10 +761,10 @@ public class KeychainIntentService extends IntentService implements Progressable ProviderHelper providerHelper = new ProviderHelper(this); CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri); - PgpImportExport pgpImportExport = new PgpImportExport(this, new ProviderHelper(this), this); + ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this); try { - pgpImportExport.uploadKeyRingToServer(server, keyring); + importExportOperation.uploadKeyRingToServer(server, keyring); } catch (Keyserver.AddKeyException e) { throw new PgpGeneralException("Unable to export key to selected server"); } -- cgit v1.2.3