From dcd22d981550bf18cf14362c313640409deaa1c7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 4 Oct 2014 12:30:02 +0200 Subject: [PATCH 1/2] keychainintentservice: rearrange actions in alphabetical order --- .../service/KeychainIntentService.java | 846 +++++++++--------- 1 file changed, 432 insertions(+), 414 deletions(-) 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 2101705bc..c131430cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -253,7 +253,437 @@ public class KeychainIntentService extends IntentService implements Progressable String action = intent.getAction(); // executeServiceMethod action from extra bundle - if (ACTION_SIGN_ENCRYPT.equals(action)) { + if (ACTION_CERTIFY_KEYRING.equals(action)) { + + try { + + /* Input */ + long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID); + long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID); + ArrayList userIds = data.getStringArrayList(CERTIFY_KEY_UIDS); + + /* Operation */ + String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this, + masterKeyId, masterKeyId); + if (signaturePassphrase == null) { + throw new PgpGeneralException("Unable to obtain passphrase"); + } + + ProviderHelper providerHelper = new ProviderHelper(this); + CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId); + CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId); + CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey(); + if (!certificationKey.unlock(signaturePassphrase)) { + throw new PgpGeneralException("Error extracting key (bad passphrase?)"); + } + // TODO: supply nfc stuff + UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds, null, null); + + // store the signed key in our local cache + providerHelper.savePublicKeyRing(newRing); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_CONSOLIDATE.equals(action)) { + + ConsolidateResult result; + if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) { + result = new ProviderHelper(this).consolidateDatabaseStep2(this); + } else { + result = new ProviderHelper(this).consolidateDatabaseStep1(this); + } + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + + } else if (ACTION_DECRYPT_METADATA.equals(action)) { + + try { + /* Input */ + String passphrase = data.getString(DECRYPT_PASSPHRASE); + byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); + + InputData inputData = createDecryptInputData(data); + + /* Operation */ + + Bundle resultData = new Bundle(); + + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( + new ProviderHelper(this), + this, inputData, null + ); + builder.setProgressable(this) + .setAllowSymmetricDecryption(true) + .setPassphrase(passphrase) + .setDecryptMetadataOnly(true) + .setNfcState(nfcDecryptedSessionKey); + + DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + + resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); + + /* Output */ + Log.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_DECRYPT_VERIFY.equals(action)) { + + try { + /* Input */ + String passphrase = data.getString(DECRYPT_PASSPHRASE); + byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); + + InputData inputData = createDecryptInputData(data); + OutputStream outStream = createCryptOutputStream(data); + + /* Operation */ + + Bundle resultData = new Bundle(); + + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( + new ProviderHelper(this), this, + inputData, outStream + ); + builder.setProgressable(this) + .setAllowSymmetricDecryption(true) + .setPassphrase(passphrase) + .setNfcState(nfcDecryptedSessionKey); + + DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + + outStream.close(); + + resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); + + /* Output */ + + finalizeDecryptOutputStream(data, resultData, outStream); + + Log.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_DELETE.equals(action)) { + + try { + + long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST); + boolean isSecret = data.getBoolean(DELETE_IS_SECRET); + + if (masterKeyIds.length == 0) { + throw new PgpGeneralException("List of keys to delete is empty"); + } + + if (isSecret && masterKeyIds.length > 1) { + throw new PgpGeneralException("Secret keys can only be deleted individually!"); + } + + boolean success = false; + for (long masterKeyId : masterKeyIds) { + int count = getContentResolver().delete( + KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null + ); + success |= count > 0; + } + + if (isSecret && success) { + new ProviderHelper(this).consolidateDatabaseStep1(this); + } + + if (success) { + // make sure new data is synced into contacts + ContactSyncAdapterService.requestSync(); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_DELETE_FILE_SECURELY.equals(action)) { + + try { + /* Input */ + String deleteFile = data.getString(DELETE_FILE); + + /* Operation */ + try { + PgpHelper.deleteFileSecurely(this, this, new File(deleteFile)); + } catch (FileNotFoundException e) { + throw new PgpGeneralException( + getString(R.string.error_file_not_found, deleteFile)); + } catch (IOException e) { + throw new PgpGeneralException(getString(R.string.error_file_delete_failed, + deleteFile)); + } + + /* Output */ + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action) || ACTION_IMPORT_KEYBASE_KEYS.equals(action)) { + + ArrayList entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); + + // this downloads the keys and places them into the ImportKeysListEntry entries + String keyServer = data.getString(DOWNLOAD_KEY_SERVER); + + ArrayList keyRings = new ArrayList(entries.size()); + for (ImportKeysListEntry entry : entries) { + try { + Keyserver server; + ArrayList origins = entry.getOrigins(); + if (origins == null) { + origins = new ArrayList(); + } + if (origins.isEmpty()) { + origins.add(keyServer); + } + for (String origin : origins) { + if (KeybaseKeyserver.ORIGIN.equals(origin)) { + server = new KeybaseKeyserver(); + } else { + server = new HkpKeyserver(origin); + } + Log.d(Constants.TAG, "IMPORTING " + entry.getKeyIdHex() + " FROM: " + server); + + // if available use complete fingerprint for get request + byte[] downloadedKeyBytes; + if (KeybaseKeyserver.ORIGIN.equals(origin)) { + downloadedKeyBytes = server.get(entry.getExtraData()).getBytes(); + } else if (entry.getFingerprintHex() != null) { + downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes(); + } else { + downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes(); + } + + // save key bytes in entry object for doing the + // actual import afterwards + keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex())); + } + } catch (Exception e) { + sendErrorToHandler(e); + } + } + + Intent importIntent = new Intent(this, KeychainIntentService.class); + importIntent.setAction(ACTION_IMPORT_KEYRING); + + Bundle importData = new Bundle(); + // This is not going through binder, nothing to fear of + importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings); + importIntent.putExtra(EXTRA_DATA, importData); + importIntent.putExtra(EXTRA_MESSENGER, mMessenger); + + // now import it with this service + onHandleIntent(importIntent); + + // result is handled in ACTION_IMPORT_KEYRING + + } else if (ACTION_EDIT_KEYRING.equals(action)) { + + try { + /* Input */ + SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); + if (saveParcel == null) { + Log.e(Constants.TAG, "bug: missing save_keyring_parcel in data!"); + return; + } + + /* Operation */ + PgpKeyOperation keyOperations = + new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100), mActionCanceled); + EditKeyResult modifyResult; + + if (saveParcel.mMasterKeyId != null) { + String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); + CanonicalizedSecretKeyRing secRing = + new ProviderHelper(this).getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); + + modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); + } else { + modifyResult = keyOperations.createSecretKeyRing(saveParcel); + } + + // If the edit operation didn't succeed, exit here + if (!modifyResult.success()) { + // always return SaveKeyringResult, so create one out of the EditKeyResult + SaveKeyringResult saveResult = new SaveKeyringResult( + SaveKeyringResult.RESULT_ERROR, + modifyResult.getLog(), + null); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); + return; + } + + UncachedKeyRing ring = modifyResult.getRing(); + + // Check if the action was cancelled + if (mActionCanceled.get()) { + OperationLog log = modifyResult.getLog(); + // If it wasn't added before, add log entry + if (!modifyResult.cancelled()) { + log.add(LogType.MSG_OPERATION_CANCELLED, 0); + } + // If so, just stop without saving + SaveKeyringResult saveResult = new SaveKeyringResult( + SaveKeyringResult.RESULT_CANCELLED, log, null); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); + return; + } + + // Save the keyring. The ProviderHelper is initialized with the previous log + SaveKeyringResult saveResult = new ProviderHelper(this, modifyResult.getLog()) + .saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); + + // If the edit operation didn't succeed, exit here + if (!saveResult.success()) { + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); + return; + } + + // cache new passphrase + if (saveParcel.mNewPassphrase != null) { + PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(), ring.getMasterKeyId(), + saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback()); + } + + setProgress(R.string.progress_done, 100, 100); + + // make sure new data is synced into contacts + ContactSyncAdapterService.requestSync(); + + /* Output */ + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_EXPORT_KEYRING.equals(action)) { + + try { + + boolean exportSecret = data.getBoolean(EXPORT_SECRET, false); + long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID); + String outputFile = data.getString(EXPORT_FILENAME); + Uri outputUri = data.getParcelable(EXPORT_URI); + + // If not exporting all keys get the masterKeyIds of the keys to export from the intent + boolean exportAll = data.getBoolean(EXPORT_ALL); + + if (outputFile != null) { + // check if storage is ready + if (!FileHelper.isStorageMounted(outputFile)) { + throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready)); + } + } + + ArrayList publicMasterKeyIds = new ArrayList(); + ArrayList secretMasterKeyIds = new ArrayList(); + + String selection = null; + if (!exportAll) { + selection = KeychainDatabase.Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN( "; + for (long l : masterKeyIds) { + selection += Long.toString(l) + ","; + } + selection = selection.substring(0, selection.length() - 1) + " )"; + } + + Cursor cursor = getContentResolver().query(KeyRings.buildUnifiedKeyRingsUri(), + new String[]{KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET}, + selection, null, null); + try { + if (cursor != null && cursor.moveToFirst()) do { + // export public either way + publicMasterKeyIds.add(cursor.getLong(0)); + // add secret if available (and requested) + if (exportSecret && cursor.getInt(1) != 0) + secretMasterKeyIds.add(cursor.getLong(0)); + } while (cursor.moveToNext()); + } finally { + if (cursor != null) { + cursor.close(); + } + } + + OutputStream outStream; + if (outputFile != null) { + outStream = new FileOutputStream(outputFile); + } else { + outStream = getContentResolver().openOutputStream(outputUri); + } + + PgpImportExport pgpImportExport = new PgpImportExport(this, new ProviderHelper(this), this); + Bundle resultData = pgpImportExport + .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds, outStream); + + if (mActionCanceled.get() && outputFile != null) { + new File(outputFile).delete(); + } + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_IMPORT_KEYRING.equals(action)) { + + try { + + Iterator entries; + int numEntries; + if (data.containsKey(IMPORT_KEY_LIST)) { + // get entries from intent + ArrayList list = data.getParcelableArrayList(IMPORT_KEY_LIST); + entries = list.iterator(); + numEntries = list.size(); + } else { + // get entries from cached file + ParcelableFileCache cache = + new ParcelableFileCache(this, "key_import.pcl"); + IteratorWithSize it = cache.readCache(); + entries = it; + numEntries = it.getSize(); + } + + ProviderHelper providerHelper = new ProviderHelper(this); + PgpImportExport pgpImportExport = new PgpImportExport( + this, providerHelper, this, mActionCanceled); + ImportKeyResult result = pgpImportExport.importKeyRings(entries, numEntries); + + // we do this even on failure or cancellation! + if (result.mSecret > 0) { + // cannot cancel from here on out! + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_PREVENT_CANCEL); + providerHelper.consolidateDatabaseStep1(this); + } + + // make sure new data is synced into contacts + ContactSyncAdapterService.requestSync(); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_SIGN_ENCRYPT.equals(action)) { + try { /* Input */ int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET); @@ -336,285 +766,9 @@ public class KeychainIntentService extends IntentService implements Progressable } catch (Exception e) { sendErrorToHandler(e); } - } else if (ACTION_DECRYPT_VERIFY.equals(action)) { - try { - /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); - byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); - InputData inputData = createDecryptInputData(data); - OutputStream outStream = createCryptOutputStream(data); - - /* Operation */ - - Bundle resultData = new Bundle(); - - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - new ProviderHelper(this), this, - inputData, outStream - ); - builder.setProgressable(this) - .setAllowSymmetricDecryption(true) - .setPassphrase(passphrase) - .setNfcState(nfcDecryptedSessionKey); - - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); - - outStream.close(); - - resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); - - /* Output */ - - finalizeDecryptOutputStream(data, resultData, outStream); - - Log.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_DECRYPT_METADATA.equals(action)) { - try { - /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); - byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); - - InputData inputData = createDecryptInputData(data); - - /* Operation */ - - Bundle resultData = new Bundle(); - - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - new ProviderHelper(this), - this, inputData, null - ); - builder.setProgressable(this) - .setAllowSymmetricDecryption(true) - .setPassphrase(passphrase) - .setDecryptMetadataOnly(true) - .setNfcState(nfcDecryptedSessionKey); - - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); - - resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); - - /* Output */ - Log.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_EDIT_KEYRING.equals(action)) { - try { - /* Input */ - SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); - if (saveParcel == null) { - Log.e(Constants.TAG, "bug: missing save_keyring_parcel in data!"); - return; - } - - /* Operation */ - PgpKeyOperation keyOperations = - new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100), mActionCanceled); - EditKeyResult modifyResult; - - if (saveParcel.mMasterKeyId != null) { - String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); - CanonicalizedSecretKeyRing secRing = - new ProviderHelper(this).getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); - - modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); - } else { - modifyResult = keyOperations.createSecretKeyRing(saveParcel); - } - - // If the edit operation didn't succeed, exit here - if (!modifyResult.success()) { - // always return SaveKeyringResult, so create one out of the EditKeyResult - SaveKeyringResult saveResult = new SaveKeyringResult( - SaveKeyringResult.RESULT_ERROR, - modifyResult.getLog(), - null); - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); - return; - } - - UncachedKeyRing ring = modifyResult.getRing(); - - // Check if the action was cancelled - if (mActionCanceled.get()) { - OperationLog log = modifyResult.getLog(); - // If it wasn't added before, add log entry - if (!modifyResult.cancelled()) { - log.add(LogType.MSG_OPERATION_CANCELLED, 0); - } - // If so, just stop without saving - SaveKeyringResult saveResult = new SaveKeyringResult( - SaveKeyringResult.RESULT_CANCELLED, log, null); - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); - return; - } - - // Save the keyring. The ProviderHelper is initialized with the previous log - SaveKeyringResult saveResult = new ProviderHelper(this, modifyResult.getLog()) - .saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); - - // If the edit operation didn't succeed, exit here - if (!saveResult.success()) { - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); - return; - } - - // cache new passphrase - if (saveParcel.mNewPassphrase != null) { - PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(), ring.getMasterKeyId(), - saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback()); - } - - setProgress(R.string.progress_done, 100, 100); - - // make sure new data is synced into contacts - ContactSyncAdapterService.requestSync(); - - /* Output */ - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); - } catch (Exception e) { - sendErrorToHandler(e); - } - - } else if (ACTION_DELETE_FILE_SECURELY.equals(action)) { - try { - /* Input */ - String deleteFile = data.getString(DELETE_FILE); - - /* Operation */ - try { - PgpHelper.deleteFileSecurely(this, this, new File(deleteFile)); - } catch (FileNotFoundException e) { - throw new PgpGeneralException( - getString(R.string.error_file_not_found, deleteFile)); - } catch (IOException e) { - throw new PgpGeneralException(getString(R.string.error_file_delete_failed, - deleteFile)); - } - - /* Output */ - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_IMPORT_KEYRING.equals(action)) { - try { - - Iterator entries; - int numEntries; - if (data.containsKey(IMPORT_KEY_LIST)) { - // get entries from intent - ArrayList list = data.getParcelableArrayList(IMPORT_KEY_LIST); - entries = list.iterator(); - numEntries = list.size(); - } else { - // get entries from cached file - ParcelableFileCache cache = - new ParcelableFileCache(this, "key_import.pcl"); - IteratorWithSize it = cache.readCache(); - entries = it; - numEntries = it.getSize(); - } - - ProviderHelper providerHelper = new ProviderHelper(this); - PgpImportExport pgpImportExport = new PgpImportExport( - this, providerHelper, this, mActionCanceled); - ImportKeyResult result = pgpImportExport.importKeyRings(entries, numEntries); - - // we do this even on failure or cancellation! - if (result.mSecret > 0) { - // cannot cancel from here on out! - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_PREVENT_CANCEL); - providerHelper.consolidateDatabaseStep1(this); - } - - // make sure new data is synced into contacts - ContactSyncAdapterService.requestSync(); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_EXPORT_KEYRING.equals(action)) { - try { - - boolean exportSecret = data.getBoolean(EXPORT_SECRET, false); - long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID); - String outputFile = data.getString(EXPORT_FILENAME); - Uri outputUri = data.getParcelable(EXPORT_URI); - - // If not exporting all keys get the masterKeyIds of the keys to export from the intent - boolean exportAll = data.getBoolean(EXPORT_ALL); - - if (outputFile != null) { - // check if storage is ready - if (!FileHelper.isStorageMounted(outputFile)) { - throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready)); - } - } - - ArrayList publicMasterKeyIds = new ArrayList(); - ArrayList secretMasterKeyIds = new ArrayList(); - - String selection = null; - if (!exportAll) { - selection = KeychainDatabase.Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN( "; - for (long l : masterKeyIds) { - selection += Long.toString(l) + ","; - } - selection = selection.substring(0, selection.length() - 1) + " )"; - } - - Cursor cursor = getContentResolver().query(KeyRings.buildUnifiedKeyRingsUri(), - new String[]{KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET}, - selection, null, null); - try { - if (cursor != null && cursor.moveToFirst()) do { - // export public either way - publicMasterKeyIds.add(cursor.getLong(0)); - // add secret if available (and requested) - if (exportSecret && cursor.getInt(1) != 0) - secretMasterKeyIds.add(cursor.getLong(0)); - } while (cursor.moveToNext()); - } finally { - if (cursor != null) { - cursor.close(); - } - } - - OutputStream outStream; - if (outputFile != null) { - outStream = new FileOutputStream(outputFile); - } else { - outStream = getContentResolver().openOutputStream(outputUri); - } - - PgpImportExport pgpImportExport = new PgpImportExport(this, new ProviderHelper(this), this); - Bundle resultData = pgpImportExport - .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds, outStream); - - if (mActionCanceled.get() && outputFile != null) { - new File(outputFile).delete(); - } - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } } else if (ACTION_UPLOAD_KEYRING.equals(action)) { + try { /* Input */ @@ -638,142 +792,6 @@ public class KeychainIntentService extends IntentService implements Progressable } catch (Exception e) { sendErrorToHandler(e); } - } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action) || ACTION_IMPORT_KEYBASE_KEYS.equals(action)) { - ArrayList entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); - - // this downloads the keys and places them into the ImportKeysListEntry entries - String keyServer = data.getString(DOWNLOAD_KEY_SERVER); - - ArrayList keyRings = new ArrayList(entries.size()); - for (ImportKeysListEntry entry : entries) { - try { - Keyserver server; - ArrayList origins = entry.getOrigins(); - if (origins == null) { - origins = new ArrayList(); - } - if (origins.isEmpty()) { - origins.add(keyServer); - } - for (String origin : origins) { - if (KeybaseKeyserver.ORIGIN.equals(origin)) { - server = new KeybaseKeyserver(); - } else { - server = new HkpKeyserver(origin); - } - Log.d(Constants.TAG, "IMPORTING " + entry.getKeyIdHex() + " FROM: " + server); - - // if available use complete fingerprint for get request - byte[] downloadedKeyBytes; - if (KeybaseKeyserver.ORIGIN.equals(origin)) { - downloadedKeyBytes = server.get(entry.getExtraData()).getBytes(); - } else if (entry.getFingerprintHex() != null) { - downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes(); - } else { - downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes(); - } - - // save key bytes in entry object for doing the - // actual import afterwards - keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex())); - } - } catch (Exception e) { - sendErrorToHandler(e); - } - } - - Intent importIntent = new Intent(this, KeychainIntentService.class); - importIntent.setAction(ACTION_IMPORT_KEYRING); - - Bundle importData = new Bundle(); - // This is not going through binder, nothing to fear of - importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings); - importIntent.putExtra(EXTRA_DATA, importData); - importIntent.putExtra(EXTRA_MESSENGER, mMessenger); - - // now import it with this service - onHandleIntent(importIntent); - - // result is handled in ACTION_IMPORT_KEYRING - } else if (ACTION_CERTIFY_KEYRING.equals(action)) { - try { - - /* Input */ - long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID); - long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID); - ArrayList userIds = data.getStringArrayList(CERTIFY_KEY_UIDS); - - /* Operation */ - String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this, - masterKeyId, masterKeyId); - if (signaturePassphrase == null) { - throw new PgpGeneralException("Unable to obtain passphrase"); - } - - ProviderHelper providerHelper = new ProviderHelper(this); - CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId); - CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId); - CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey(); - if (!certificationKey.unlock(signaturePassphrase)) { - throw new PgpGeneralException("Error extracting key (bad passphrase?)"); - } - // TODO: supply nfc stuff - UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds, null, null); - - // store the signed key in our local cache - providerHelper.savePublicKeyRing(newRing); - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); - - } catch (Exception e) { - sendErrorToHandler(e); - } - - } else if (ACTION_DELETE.equals(action)) { - - try { - - long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST); - boolean isSecret = data.getBoolean(DELETE_IS_SECRET); - - if (masterKeyIds.length == 0) { - throw new PgpGeneralException("List of keys to delete is empty"); - } - - if (isSecret && masterKeyIds.length > 1) { - throw new PgpGeneralException("Secret keys can only be deleted individually!"); - } - - boolean success = false; - for (long masterKeyId : masterKeyIds) { - int count = getContentResolver().delete( - KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null - ); - success |= count > 0; - } - - if (isSecret && success) { - new ProviderHelper(this).consolidateDatabaseStep1(this); - } - - if (success) { - // make sure new data is synced into contacts - ContactSyncAdapterService.requestSync(); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); - } - - } catch (Exception e) { - sendErrorToHandler(e); - } - - } else if (ACTION_CONSOLIDATE.equals(action)) { - ConsolidateResult result; - if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) { - result = new ProviderHelper(this).consolidateDatabaseStep2(this); - } else { - result = new ProviderHelper(this).consolidateDatabaseStep1(this); - } - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); } } From 0e0e3d8dd09deb2ff36d46ccceba08bb5c0967ce Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 4 Oct 2014 14:11:51 +0200 Subject: [PATCH 2/2] redesign certify action to work with parcel input and result --- .../keychain/pgp/CanonicalizedKeyRing.java | 4 + .../keychain/pgp/CanonicalizedSecretKey.java | 9 +- .../keychain/pgp/PgpCertifyOperation.java | 149 ++++++++++++++++++ .../keychain/pgp/PgpImportExport.java | 1 - .../service/CertifyActionsParcel.java | 111 +++++++++++++ .../service/KeychainIntentService.java | 35 ++-- .../service/results/CertifyResult.java | 57 +++++++ .../service/results/OperationResult.java | 17 +- .../keychain/ui/CertifyKeyFragment.java | 29 ++-- OpenKeychain/src/main/res/values/strings.xml | 20 ++- 10 files changed, 389 insertions(+), 43 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java index f43cbbeef..905cae17e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java @@ -51,6 +51,10 @@ public abstract class CanonicalizedKeyRing extends KeyRing { return mVerified; } + public byte[] getFingerprint() { + return getRing().getPublicKey().getFingerprint(); + } + public String getPrimaryUserId() throws PgpGeneralException { return getPublicKey().getPrimaryUserId(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index bec07ce21..595f37872 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -278,13 +278,12 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { * Certify the given pubkeyid with the given masterkeyid. * * @param publicKeyRing Keyring to add certification to. - * @param userIds User IDs to certify, must not be null or empty + * @param userIds User IDs to certify, or all if null * @return A keyring with added certifications */ public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List userIds, byte[] nfcSignedHash, Date nfcCreationTimestamp) - throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, - PGPException, SignatureException { + throws PGPException { if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { throw new PrivateKeyNotUnlockedException(); } @@ -314,7 +313,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey(); // fetch public key ring, add the certification and return it - for (String userId : new IterableIterator(userIds.iterator())) { + Iterable it = userIds != null ? userIds + : new IterableIterator(publicKey.getUserIDs()); + for (String userId : it) { PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java new file mode 100644 index 000000000..ffa2181e1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java @@ -0,0 +1,149 @@ +package org.sufficientlysecure.keychain.pgp; + +import android.content.Context; + +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.EditKeyResult; +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); + if ( ! Arrays.equals(publicRing.getFingerprint(), action.mFingerprint)) { + log.add(LogType.MSG_CRT_FP_MISMATCH, 3); + certifyError += 1; + continue; + } + + 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 + SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey); + + if (result.success()) { + certifyOk += 1; + } else { + log.add(LogType.MSG_CRT_WARN_SAVE_FAILED, 3); + } + + // TODO do something with import results + + } + + 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 index dd0549adc..c3320ed6a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -38,7 +38,6 @@ import org.sufficientlysecure.keychain.service.results.OperationResult.Operation 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.IterableIterator; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java new file mode 100644 index 000000000..d2562d728 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 Vincent Breitmoser + * + * 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.service; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * This class is a a transferable representation for a number of keyrings to + * be certified. + */ +public class CertifyActionsParcel implements Parcelable { + + // the master key id to certify with + final public long mMasterKeyId; + public CertifyLevel mLevel; + + public ArrayList mCertifyActions = new ArrayList(); + + public CertifyActionsParcel(long masterKeyId) { + mMasterKeyId = masterKeyId; + mLevel = CertifyLevel.DEFAULT; + } + + public CertifyActionsParcel(Parcel source) { + mMasterKeyId = source.readLong(); + // just like parcelables, this is meant for ad-hoc IPC only and is NOT portable! + mLevel = CertifyLevel.values()[source.readInt()]; + + mCertifyActions = (ArrayList) source.readSerializable(); + } + + public void add(CertifyAction action) { + mCertifyActions.add(action); + } + + @Override + public void writeToParcel(Parcel destination, int flags) { + destination.writeLong(mMasterKeyId); + destination.writeInt(mLevel.ordinal()); + + destination.writeSerializable(mCertifyActions); + } + + public static final Creator CREATOR = new Creator() { + public CertifyActionsParcel createFromParcel(final Parcel source) { + return new CertifyActionsParcel(source); + } + + public CertifyActionsParcel[] newArray(final int size) { + return new CertifyActionsParcel[size]; + } + }; + + // TODO make this parcelable + public static class CertifyAction implements Serializable { + final public long mMasterKeyId; + final public byte[] mFingerprint; + + final public ArrayList mUserIds; + + public CertifyAction(long masterKeyId, byte[] fingerprint) { + this(masterKeyId, fingerprint, null); + } + + public CertifyAction(long masterKeyId, byte[] fingerprint, ArrayList userIds) { + mMasterKeyId = masterKeyId; + mFingerprint = fingerprint; + mUserIds = userIds; + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + String out = "mMasterKeyId: " + mMasterKeyId + "\n"; + out += "mLevel: " + mLevel + "\n"; + out += "mCertifyActions: " + mCertifyActions + "\n"; + + return out; + } + + // All supported algorithms + public enum CertifyLevel { + DEFAULT, NONE, CASUAL, POSITIVE + } + +} 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 c131430cf..f5e6dedc2 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,9 @@ import android.os.RemoteException; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; +import org.sufficientlysecure.keychain.service.results.CertifyResult; import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.Preferences; @@ -39,7 +41,6 @@ import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; @@ -80,7 +81,6 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -183,10 +183,8 @@ public class KeychainIntentService extends IntentService implements Progressable public static final String DOWNLOAD_KEY_SERVER = "query_key_server"; public static final String DOWNLOAD_KEY_LIST = "query_key_id"; - // sign key - public static final String CERTIFY_KEY_MASTER_KEY_ID = "sign_key_master_key_id"; - public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id"; - public static final String CERTIFY_KEY_UIDS = "sign_key_uids"; + // certify key + public static final String CERTIFY_PARCEL = "certify_parcel"; // consolidate public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery"; @@ -258,30 +256,21 @@ public class KeychainIntentService extends IntentService implements Progressable try { /* Input */ - long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID); - long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID); - ArrayList userIds = data.getStringArrayList(CERTIFY_KEY_UIDS); + CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL); /* Operation */ - String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this, - masterKeyId, masterKeyId); - if (signaturePassphrase == null) { + String passphrase = PassphraseCacheService.getCachedPassphrase(this, + // certification is always with the master key id, so use that one + parcel.mMasterKeyId, parcel.mMasterKeyId); + if (passphrase == null) { throw new PgpGeneralException("Unable to obtain passphrase"); } ProviderHelper providerHelper = new ProviderHelper(this); - CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId); - CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId); - CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey(); - if (!certificationKey.unlock(signaturePassphrase)) { - throw new PgpGeneralException("Error extracting key (bad passphrase?)"); - } - // TODO: supply nfc stuff - UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds, null, null); + PgpCertifyOperation op = new PgpCertifyOperation(providerHelper, mActionCanceled); + CertifyResult result = op.certify(parcel, passphrase); - // store the signed key in our local cache - providerHelper.savePublicKeyRing(newRing); - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); } catch (Exception e) { sendErrorToHandler(e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java new file mode 100644 index 000000000..cf54238ec --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 Vincent Breitmoser + * + * 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.service.results; + +import android.os.Parcel; + +public class CertifyResult extends OperationResult { + + int mCertifyOk, mCertifyError; + + public CertifyResult(int result, OperationLog log) { + super(result, log); + } + + public CertifyResult(int result, OperationLog log, int certifyOk, int certifyError) { + this(result, log); + mCertifyOk = certifyOk; + mCertifyError = certifyError; + } + + /** Construct from a parcel - trivial because we have no extra data. */ + public CertifyResult(Parcel source) { + super(source); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + public static Creator CREATOR = new Creator() { + public CertifyResult createFromParcel(final Parcel source) { + return new CertifyResult(source); + } + + public CertifyResult[] newArray(final int size) { + return new CertifyResult[size]; + } + }; + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java index 29924ee5d..90e1d0037 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java @@ -517,8 +517,23 @@ public abstract class OperationResult implements Parcelable { MSG_SE_SIGCRYPTING (LogLevel.DEBUG, R.string.msg_se_sigcrypting), MSG_SE_SYMMETRIC (LogLevel.INFO, R.string.msg_se_symmetric), - MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success), + MSG_CRT_CERTIFYING (LogLevel.DEBUG, R.string.msg_crt_certifying), + MSG_CRT_CERTIFY_ALL (LogLevel.DEBUG, R.string.msg_crt_certify_all), + MSG_CRT_CERTIFY_SOME (LogLevel.DEBUG, R.plurals.msg_crt_certify_some), + MSG_CRT_ERROR_MASTER_NOT_FOUND (LogLevel.ERROR, R.string.msg_crt_error_master_not_found), + MSG_CRT_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_crt_error_unlock), + MSG_CRT_FP_MISMATCH (LogLevel.WARN, R.string.msg_crt_fp_mismatch), + MSG_CRT (LogLevel.START, R.string.msg_crt), + MSG_CRT_MASTER_FETCH (LogLevel.DEBUG, R.string.msg_crt_master_fetch), + MSG_CRT_SAVE (LogLevel.DEBUG, R.string.msg_crt_save), + MSG_CRT_SAVING (LogLevel.DEBUG, R.string.msg_crt_saving), MSG_CRT_SUCCESS (LogLevel.OK, R.string.msg_crt_success), + MSG_CRT_UNLOCK (LogLevel.DEBUG, R.string.msg_crt_unlock), + MSG_CRT_WARN_NOT_FOUND (LogLevel.WARN, R.string.msg_crt_warn_not_found), + MSG_CRT_WARN_CERT_FAILED (LogLevel.WARN, R.string.msg_crt_warn_cert_failed), + MSG_CRT_WARN_SAVE_FAILED (LogLevel.WARN, R.string.msg_crt_warn_save_failed), + + MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success), MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 5eec8454a..685581be7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -48,6 +48,10 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +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.SingletonResult; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -55,9 +59,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.service.results.OperationResult.LogLevel; import org.sufficientlysecure.keychain.service.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.service.results.SingletonResult; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; @@ -83,6 +85,7 @@ public class CertifyKeyFragment extends LoaderFragment private Uri mDataUri; private long mPubKeyId = Constants.key.none; + private byte[] mPubFingerprint; private long mMasterKeyId = Constants.key.none; private UserIdsAdapter mUserIdsAdapter; @@ -243,8 +246,8 @@ public class CertifyKeyFragment extends LoaderFragment String mainUserId = data.getString(INDEX_USER_ID); mInfoPrimaryUserId.setText(mainUserId); - byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT); - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob); + mPubFingerprint = data.getBlob(INDEX_FINGERPRINT); + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(mPubFingerprint); mInfoFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint)); } break; @@ -312,27 +315,27 @@ public class CertifyKeyFragment extends LoaderFragment intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING); // fill values for this action + CertifyActionsParcel parcel = new CertifyActionsParcel(mMasterKeyId); + parcel.add(new CertifyAction(mPubKeyId, mPubFingerprint, userIds)); + Bundle data = new Bundle(); - - data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId); - data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId); - data.putStringArrayList(KeychainIntentService.CERTIFY_KEY_UIDS, userIds); - + data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Message is received after signing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity, - getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER) { + getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - SingletonResult result = new SingletonResult( - SingletonResult.RESULT_OK, LogType.MSG_CRT_SUCCESS); + Bundle data = message.getData(); + CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); + Intent intent = new Intent(); - intent.putExtra(SingletonResult.EXTRA_RESULT, result); + intent.putExtra(CertifyResult.EXTRA_RESULT, result); mActivity.setResult(CertifyKeyActivity.RESULT_OK, intent); // check if we need to send the key to the server or not diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 9f5d3f535..28933ac89 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -875,8 +875,26 @@ "Starting sign and/or encrypt operation" "Preparing symmetric encryption" - "Successfully uploaded key to server" + "Generating certifications" + "Certifying all user ids for key %s" + + "Certifying one user id for key %2$s" + "Certifying %1$d user ids for key %2$s" + + "Master key not found!" + "Error unlocking master key!" + "Fingerprint mismatch, not certifying!" + "Certifying keyrings" + "Fetching certifying master key" + "Saving certified key %s" + "Saving keyrings" + "Unlocking master key" "Successfully certified identities" + "Key not found!" + "Certificate generation failed!" + "Save operation failed!" + + "Successfully uploaded key to server" "Account saved"