diff options
author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-02 14:39:28 +0100 |
---|---|---|
committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-02 14:39:28 +0100 |
commit | 2f83291920e024b0f8038fe0caa747051b41cf1c (patch) | |
tree | 780fc0975e56b3dbd8a1eb4cb2988ac3e2e23ea1 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service | |
parent | aa22a0defcf94d5f734d3a00288f5c8d916d2e57 (diff) | |
parent | 4e29d027af05fc574dc5398d2fb3afcdf3defc70 (diff) | |
download | open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.gz open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.bz2 open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.zip |
NON-WORKING Merge branch 'development' into linked-identities
Conflicts:
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
OpenKeychain/src/main/res/values/strings.xml
extern/spongycastle
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service')
6 files changed, 384 insertions, 306 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java index dd9c0d769..f0dbf0820 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -34,7 +34,7 @@ public class CertifyActionsParcel implements Parcelable { final public long mMasterKeyId; public CertifyLevel mLevel; - public ArrayList<CertifyAction> mCertifyActions = new ArrayList<CertifyAction>(); + public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>(); public CertifyActionsParcel(long masterKeyId) { mMasterKeyId = masterKeyId; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java index 447f413ef..7688b9252 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -53,7 +53,7 @@ public class ContactSyncAdapterService extends Service { // new Handler.Callback() { // @Override // public boolean handleMessage(Message msg) { -// Bundle data = msg.getData(); +// Bundle data = msg.getInputData(); // switch (msg.arg1) { // case KeychainIntentServiceHandler.MESSAGE_OKAY: // Log.d(Constants.TAG, "Syncing... Done."); 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 3626076b7..0ddf0c76d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -26,10 +26,25 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; +import com.textuality.keybase.lib.Proof; +import com.textuality.keybase.lib.prover.Prover; + +import org.json.JSONObject; +import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; +import org.sufficientlysecure.keychain.keyimport.Keyserver; +import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.CertifyOperation; import org.sufficientlysecure.keychain.operations.DeleteOperation; import org.sufficientlysecure.keychain.operations.EditKeyOperation; +import org.sufficientlysecure.keychain.operations.ImportExportOperation; +import org.sufficientlysecure.keychain.operations.PromoteKeyOperation; +import org.sufficientlysecure.keychain.operations.SignEncryptOperation; +import org.sufficientlysecure.keychain.operations.results.CertifyResult; +import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.ExportResult; @@ -41,35 +56,40 @@ import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult; +import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.operations.ImportExportOperation; -import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; -import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; -import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; -import org.sufficientlysecure.keychain.util.ParcelableFileCache; +import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ParcelableFileCache; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; 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; +import de.measite.minidns.Client; +import de.measite.minidns.DNSMessage; +import de.measite.minidns.Question; +import de.measite.minidns.Record; +import de.measite.minidns.record.Data; +import de.measite.minidns.record.TXT; + /** * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with * data from the activities or other apps, queues these intents, executes them, and stops itself @@ -86,10 +106,14 @@ public class KeychainIntentService extends IntentService implements Progressable public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY"; + public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF"; + public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA"; public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING"; + public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING"; + public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING"; public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING"; @@ -108,33 +132,25 @@ public class KeychainIntentService extends IntentService implements Progressable // encrypt, decrypt, import export public static final String TARGET = "target"; public static final String SOURCE = "source"; + // possible targets: public static final int IO_BYTES = 1; public static final int IO_URI = 2; - public static final int IO_URIS = 3; - - public static final String SELECTED_URI = "selected_uri"; // encrypt - public static final String ENCRYPT_SIGNATURE_MASTER_ID = "secret_key_id"; - public static final String ENCRYPT_SIGNATURE_KEY_PASSPHRASE = "secret_key_passphrase"; - public static final String ENCRYPT_SIGNATURE_NFC_TIMESTAMP = "signature_nfc_timestamp"; - public static final String ENCRYPT_SIGNATURE_NFC_HASH = "signature_nfc_hash"; - public static final String ENCRYPT_USE_ASCII_ARMOR = "use_ascii_armor"; - public static final String ENCRYPT_ENCRYPTION_KEYS_IDS = "encryption_keys_ids"; - public static final String ENCRYPT_COMPRESSION_ID = "compression_id"; - public static final String ENCRYPT_MESSAGE_BYTES = "message_bytes"; public static final String ENCRYPT_DECRYPT_INPUT_URI = "input_uri"; - public static final String ENCRYPT_INPUT_URIS = "input_uris"; public static final String ENCRYPT_DECRYPT_OUTPUT_URI = "output_uri"; - public static final String ENCRYPT_OUTPUT_URIS = "output_uris"; - public static final String ENCRYPT_SYMMETRIC_PASSPHRASE = "passphrase"; + public static final String SIGN_ENCRYPT_PARCEL = "sign_encrypt_parcel"; // decrypt/verify public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes"; public static final String DECRYPT_PASSPHRASE = "passphrase"; public static final String DECRYPT_NFC_DECRYPTED_SESSION_KEY = "nfc_decrypted_session_key"; + // keybase proof + public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint"; + public static final String KEYBASE_PROOF = "keybase_proof"; + // save keyring public static final String EDIT_KEYRING_PARCEL = "save_parcel"; public static final String EDIT_KEYRING_PASSPHRASE = "passphrase"; @@ -160,6 +176,10 @@ public class KeychainIntentService extends IntentService implements Progressable // certify key public static final String CERTIFY_PARCEL = "certify_parcel"; + // promote key + public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id"; + public static final String PROMOTE_TYPE = "promote_type"; + // consolidate public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery"; @@ -168,9 +188,6 @@ public class KeychainIntentService extends IntentService implements Progressable * possible data keys as result send over messenger */ - // encrypt - public static final String RESULT_BYTES = "encrypted_data"; - // decrypt/verify public static final String RESULT_DECRYPTED_BYTES = "decrypted_data"; @@ -223,303 +240,357 @@ public class KeychainIntentService extends IntentService implements Progressable String action = intent.getAction(); // executeServiceMethod action from extra bundle - if (ACTION_CERTIFY_KEYRING.equals(action)) { + switch (action) { + case ACTION_CERTIFY_KEYRING: { - // Input - CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL); - String keyServerUri = data.getString(UPLOAD_KEY_SERVER); - - // Operation - CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled); - CertifyResult result = op.certify(parcel, keyServerUri); + // Input + CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL); + String keyServerUri = data.getString(UPLOAD_KEY_SERVER); - // Result - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + // Operation + CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled); + CertifyResult result = op.certify(parcel, keyServerUri); - } else if (ACTION_CONSOLIDATE.equals(action)) { + // Result + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - // Operation - ConsolidateResult result; - if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) { - result = new ProviderHelper(this).consolidateDatabaseStep2(this); - } else { - result = new ProviderHelper(this).consolidateDatabaseStep1(this); + break; } + case ACTION_CONSOLIDATE: { - // Result - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + // Operation + ConsolidateResult result; + if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) { + result = new ProviderHelper(this).consolidateDatabaseStep2(this); + } else { + result = new ProviderHelper(this).consolidateDatabaseStep1(this); + } - } else if (ACTION_DECRYPT_METADATA.equals(action)) { + // Result + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - try { + break; + } + case ACTION_DECRYPT_METADATA: { + + try { /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); - byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); + String passphrase = data.getString(DECRYPT_PASSPHRASE); + byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); - InputData inputData = createDecryptInputData(data); + InputData inputData = createDecryptInputData(data); /* Operation */ - Bundle resultData = new Bundle(); + Bundle resultData = new Bundle(); + + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( + this, new ProviderHelper(this), this, inputData, null + ); + builder.setAllowSymmetricDecryption(true) + .setPassphrase(passphrase) + .setDecryptMetadataOnly(true) + .setNfcState(nfcDecryptedSessionKey); - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - this, new ProviderHelper(this), this, inputData, null - ); - builder.setAllowSymmetricDecryption(true) - .setPassphrase(passphrase) - .setDecryptMetadataOnly(true) - .setNfcState(nfcDecryptedSessionKey); + DecryptVerifyResult decryptVerifyResult = builder.build().execute(); - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, decryptVerifyResult); + } catch (Exception e) { + sendErrorToHandler(e); + } - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, decryptVerifyResult); - } catch (Exception e) { - sendErrorToHandler(e); + break; } + case ACTION_VERIFY_KEYBASE_PROOF: { - } else if (ACTION_DECRYPT_VERIFY.equals(action)) { + try { + Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF))); + setProgress(R.string.keybase_message_fetching_data, 0, 100); - try { - /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); - byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); + Prover prover = Prover.findProverFor(proof); - InputData inputData = createDecryptInputData(data); - OutputStream outStream = createCryptOutputStream(data); + if (prover == null) { + sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof.getPrettyName()); + return; + } - /* Operation */ + if (!prover.fetchProofData()) { + sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence)); + return; + } + String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT); + if (!prover.checkFingerprint(requiredFingerprint)) { + sendProofError(getString(R.string.keybase_key_mismatch)); + return; + } - Bundle resultData = new Bundle(); + String domain = prover.dnsTxtCheckRequired(); + if (domain != null) { + DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT)); + if (dnsQuery == null) { + sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure)); + return; + } + Record[] records = dnsQuery.getAnswers(); + List<List<byte[]>> extents = new ArrayList<List<byte[]>>(); + for (Record r : records) { + Data d = r.getPayload(); + if (d instanceof TXT) { + extents.add(((TXT) d).getExtents()); + } + } + if (!prover.checkDnsTxt(extents)) { + sendProofError(prover.getLog(), null); + return; + } + } - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - this, new ProviderHelper(this), this, - inputData, outStream - ); - builder.setAllowSymmetricDecryption(true) - .setPassphrase(passphrase) - .setNfcState(nfcDecryptedSessionKey); + byte[] messageBytes = prover.getPgpMessage().getBytes(); + if (prover.rawMessageCheckRequired()) { + InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes)); + if (!prover.checkRawMessageBytes(messageByteStream)) { + sendProofError(prover.getLog(), null); + return; + } + } - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + // kind of awkward, but this whole class wants to pull bytes out of “data” + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); + data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, messageBytes); - outStream.close(); + InputData inputData = createDecryptInputData(data); + OutputStream outStream = createCryptOutputStream(data); - resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); + PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( + this, new ProviderHelper(this), this, + inputData, outStream + ); + builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint); - /* Output */ + DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + outStream.close(); - finalizeDecryptOutputStream(data, resultData, outStream); + if (!decryptVerifyResult.success()) { + OperationLog log = decryptVerifyResult.getLog(); + OperationResult.LogEntryParcel lastEntry = null; + for (OperationResult.LogEntryParcel entry : log) { + lastEntry = entry; + } + sendProofError(getString(lastEntry.mType.getMsgId())); + return; + } - Log.logDebugBundle(resultData, "resultData"); + if (!prover.validate(outStream.toString())) { + sendProofError(getString(R.string.keybase_message_payload_mismatch)); + return; + } - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } + Bundle resultData = new Bundle(); + resultData.putString(KeychainIntentServiceHandler.DATA_MESSAGE, "OK"); - } else if (ACTION_DELETE.equals(action)) { + // these help the handler construct a useful human-readable message + resultData.putString(KeychainIntentServiceHandler.KEYBASE_PROOF_URL, prover.getProofUrl()); + resultData.putString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl()); + resultData.putString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_LABEL, prover.getPresenceLabel()); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } - // Input - long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST); - boolean isSecret = data.getBoolean(DELETE_IS_SECRET); + break; + } + case ACTION_DECRYPT_VERIFY: { - // Operation - DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this); - DeleteResult result = op.execute(masterKeyIds, isSecret); + try { + /* Input */ + String passphrase = data.getString(DECRYPT_PASSPHRASE); + byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); - // Result - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + InputData inputData = createDecryptInputData(data); + OutputStream outStream = createCryptOutputStream(data); - } else if (ACTION_EDIT_KEYRING.equals(action)) { + /* Operation */ - // Input - SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); - String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); + Bundle resultData = new Bundle(); - // Operation - EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled); - EditKeyResult result = op.execute(saveParcel, passphrase); + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( + this, new ProviderHelper(this), this, + inputData, outStream + ); + builder.setAllowSymmetricDecryption(true) + .setPassphrase(passphrase) + .setNfcState(nfcDecryptedSessionKey); - // Result - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + DecryptVerifyResult decryptVerifyResult = builder.build().execute(); - } else if (ACTION_EXPORT_KEYRING.equals(action)) { + outStream.close(); - // Input - boolean exportSecret = data.getBoolean(EXPORT_SECRET, false); - String outputFile = data.getString(EXPORT_FILENAME); - Uri outputUri = data.getParcelable(EXPORT_URI); + resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); - boolean exportAll = data.getBoolean(EXPORT_ALL); - long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID); + /* Output */ - // Operation - ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this); - ExportResult result; - if (outputFile != null) { - result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile); - } else { - result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri); - } + finalizeDecryptOutputStream(data, resultData, outStream); - // Result - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + Log.logDebugBundle(resultData, "resultData"); - } else if (ACTION_IMPORT_KEYRING.equals(action)) { + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } - try { + break; + } + case ACTION_DELETE: { // Input - String keyServer = data.getString(IMPORT_KEY_SERVER); - Iterator<ParcelableKeyRing> entries; - int numEntries; - if (data.containsKey(IMPORT_KEY_LIST)) { - // get entries from intent - ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST); - entries = list.iterator(); - numEntries = list.size(); - } else { - // get entries from cached file - ParcelableFileCache<ParcelableKeyRing> cache = - new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl"); - IteratorWithSize<ParcelableKeyRing> it = cache.readCache(); - entries = it; - numEntries = it.getSize(); - } + long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST); + boolean isSecret = data.getBoolean(DELETE_IS_SECRET); // Operation - ImportExportOperation importExportOperation = new ImportExportOperation( - this, providerHelper, this, mActionCanceled); - ImportKeyResult result = importExportOperation.importKeyRings(entries, numEntries, keyServer); + DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this); + DeleteResult result = op.execute(masterKeyIds, isSecret); - // Special: consolidate on secret key import (cannot be cancelled!) - if (result.mSecret > 0) { - // TODO move this into the import operation - providerHelper.consolidateDatabaseStep1(this); - } + // Result + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - // Special: make sure new data is synced into contacts - ContactSyncAdapterService.requestSync(); + break; + } + case ACTION_EDIT_KEYRING: { + + // Input + SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); + String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); + + // Operation + EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled); + EditKeyResult result = op.execute(saveParcel, passphrase); // Result sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - } catch (Exception e) { - sendErrorToHandler(e); + + break; } + case ACTION_PROMOTE_KEYRING: { - } else if (ACTION_SIGN_ENCRYPT.equals(action)) { + // Input + long keyRingId = data.getInt(EXPORT_KEY_RING_MASTER_KEY_ID); - try { - /* Input */ - int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET); - Bundle resultData = new Bundle(); + // Operation + PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled); + PromoteKeyResult result = op.execute(keyRingId); - long sigMasterKeyId = data.getLong(ENCRYPT_SIGNATURE_MASTER_ID); - String sigKeyPassphrase = data.getString(ENCRYPT_SIGNATURE_KEY_PASSPHRASE); + // Result + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - byte[] nfcHash = data.getByteArray(ENCRYPT_SIGNATURE_NFC_HASH); - Date nfcTimestamp = (Date) data.getSerializable(ENCRYPT_SIGNATURE_NFC_TIMESTAMP); + break; + } + case ACTION_EXPORT_KEYRING: { - String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE); + // Input + boolean exportSecret = data.getBoolean(EXPORT_SECRET, false); + String outputFile = data.getString(EXPORT_FILENAME); + Uri outputUri = data.getParcelable(EXPORT_URI); - boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR); - long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS); - int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID); - int urisCount = data.containsKey(ENCRYPT_INPUT_URIS) ? data.getParcelableArrayList(ENCRYPT_INPUT_URIS).size() : 1; - for (int i = 0; i < urisCount; i++) { - data.putInt(SELECTED_URI, i); - InputData inputData = createEncryptInputData(data); - OutputStream outStream = createCryptOutputStream(data); - String originalFilename = getOriginalFilename(data); + boolean exportAll = data.getBoolean(EXPORT_ALL); + long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID); - /* Operation */ - PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( - this, new ProviderHelper(this), this, inputData, outStream - ); - builder.setEnableAsciiArmorOutput(useAsciiArmor) - .setVersionHeader(PgpHelper.getVersionForHeader(this)) - .setCompressionId(compressionId) - .setSymmetricEncryptionAlgorithm( - Preferences.getPreferences(this).getDefaultEncryptionAlgorithm()) - .setEncryptionMasterKeyIds(encryptionKeyIds) - .setSymmetricPassphrase(symmetricPassphrase) - .setOriginalFilename(originalFilename); + // Operation + ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this); + ExportResult result; + if (outputFile != null) { + result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile); + } else { + result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri); + } - try { + // Result + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - // Find the appropriate subkey to sign with - CachedPublicKeyRing signingRing = - new ProviderHelper(this).getCachedPublicKeyRing(sigMasterKeyId); - long sigSubKeyId = signingRing.getSecretSignId(); - - // Set signature settings - builder.setSignatureMasterKeyId(sigMasterKeyId) - .setSignatureSubKeyId(sigSubKeyId) - .setSignaturePassphrase(sigKeyPassphrase) - .setSignatureHashAlgorithm( - Preferences.getPreferences(this).getDefaultHashAlgorithm()) - .setAdditionalEncryptId(sigMasterKeyId); - if (nfcHash != null && nfcTimestamp != null) { - builder.setNfcState(nfcHash, nfcTimestamp); - } + break; + } + case ACTION_IMPORT_KEYRING: { - } catch (PgpKeyNotFoundException e) { - // encrypt-only - // TODO Just silently drop the requested signature? Shouldn't we throw here? - } + // Input + String keyServer = data.getString(IMPORT_KEY_SERVER); + ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST); + ParcelableFileCache<ParcelableKeyRing> cache = + new ParcelableFileCache<>(this, "key_import.pcl"); - // this assumes that the bytes are cleartext (valid for current implementation!) - if (source == IO_BYTES) { - builder.setCleartextInput(true); - } + // Operation + ImportExportOperation importExportOperation = new ImportExportOperation( + this, providerHelper, this, mActionCanceled); + // Either list or cache must be null, no guarantees otherwise. + ImportKeyResult result = list != null + ? importExportOperation.importKeyRings(list, keyServer) + : importExportOperation.importKeyRings(cache, keyServer); - SignEncryptResult result = builder.build().execute(); - resultData.putParcelable(SignEncryptResult.EXTRA_RESULT, result); + // Result + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - outStream.close(); + break; - /* Output */ + } + case ACTION_SIGN_ENCRYPT: { - finalizeEncryptOutputStream(data, resultData, outStream); + // Input + SignEncryptParcel inputParcel = data.getParcelable(SIGN_ENCRYPT_PARCEL); - } + // Operation + SignEncryptOperation op = new SignEncryptOperation( + this, new ProviderHelper(this), this, mActionCanceled); + SignEncryptResult result = op.execute(inputParcel); - Log.logDebugBundle(resultData, "resultData"); + // Result + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); + break; } + case ACTION_UPLOAD_KEYRING: { + try { - } else if (ACTION_UPLOAD_KEYRING.equals(action)) { - - try { + /* Input */ + String keyServer = data.getString(UPLOAD_KEY_SERVER); + // and dataUri! - /* Input */ - String keyServer = data.getString(UPLOAD_KEY_SERVER); - // and dataUri! + /* Operation */ + HkpKeyserver server = new HkpKeyserver(keyServer); - /* Operation */ - HkpKeyserver server = new HkpKeyserver(keyServer); + CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri); + ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this); - CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri); - ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this); + try { + importExportOperation.uploadKeyRingToServer(server, keyring); + } catch (Keyserver.AddKeyException e) { + throw new PgpGeneralException("Unable to export key to selected server"); + } - try { - importExportOperation.uploadKeyRingToServer(server, keyring); - } catch (Keyserver.AddKeyException e) { - throw new PgpGeneralException("Unable to export key to selected server"); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); } - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); + break; } } + } + + private void sendProofError(List<String> log, String label) { + String msg = null; + label = (label == null) ? "" : label + ": "; + for (String m : log) { + Log.e(Constants.TAG, label + m); + msg = m; + } + sendProofError(label + msg); + } + private void sendProofError(String msg) { + Bundle bundle = new Bundle(); + bundle.putString(KeychainIntentServiceHandler.DATA_ERROR, msg); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, bundle); } private void sendErrorToHandler(Exception e) { @@ -532,7 +603,6 @@ public class KeychainIntentService extends IntentService implements Progressable } else { message = e.getMessage(); } - Log.d(Constants.TAG, "KeychainIntentService Exception: ", e); Bundle data = new Bundle(); @@ -609,10 +679,6 @@ public class KeychainIntentService extends IntentService implements Progressable return createCryptInputData(data, DECRYPT_CIPHERTEXT_BYTES); } - private InputData createEncryptInputData(Bundle data) throws IOException, PgpGeneralException { - return createCryptInputData(data, ENCRYPT_MESSAGE_BYTES); - } - private InputData createCryptInputData(Bundle data, String bytesName) throws PgpGeneralException, IOException { int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET); switch (source) { @@ -626,33 +692,6 @@ public class KeychainIntentService extends IntentService implements Progressable // InputStream return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0)); - case IO_URIS: - providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_INPUT_URIS).get(data.getInt(SELECTED_URI)); - - // InputStream - return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0)); - - default: - throw new PgpGeneralException("No target choosen!"); - } - } - - private String getOriginalFilename(Bundle data) throws PgpGeneralException, FileNotFoundException { - int target = data.getInt(TARGET); - switch (target) { - case IO_BYTES: - return ""; - - case IO_URI: - Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_INPUT_URI); - - return FileHelper.getFilename(this, providerUri); - - case IO_URIS: - providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_INPUT_URIS).get(data.getInt(SELECTED_URI)); - - return FileHelper.getFilename(this, providerUri); - default: throw new PgpGeneralException("No target choosen!"); } @@ -669,20 +708,11 @@ public class KeychainIntentService extends IntentService implements Progressable return getContentResolver().openOutputStream(providerUri); - case IO_URIS: - providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_OUTPUT_URIS).get(data.getInt(SELECTED_URI)); - - return getContentResolver().openOutputStream(providerUri); - default: throw new PgpGeneralException("No target choosen!"); } } - private void finalizeEncryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) { - finalizeCryptOutputStream(data, resultData, outStream, RESULT_BYTES); - } - private void finalizeDecryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) { finalizeCryptOutputStream(data, resultData, outStream, RESULT_DECRYPTED_BYTES); } @@ -695,7 +725,6 @@ public class KeychainIntentService extends IntentService implements Progressable resultData.putByteArray(bytesName, output); break; case IO_URI: - case IO_URIS: // nothing, output was written, just send okay and verification bundle break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java index 180020d0b..635fe86f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -27,8 +27,8 @@ import android.support.v4.app.FragmentManager; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.Log; public class KeychainIntentServiceHandler extends Handler { @@ -45,6 +45,11 @@ public class KeychainIntentServiceHandler extends Handler { public static final String DATA_MESSAGE = "message"; public static final String DATA_MESSAGE_ID = "message_id"; + // keybase proof specific + public static final String KEYBASE_PROOF_URL = "keybase_proof_url"; + public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url"; + public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label"; + Activity mActivity; ProgressDialogFragment mProgressDialogFragment; @@ -73,6 +78,10 @@ public class KeychainIntentServiceHandler extends Handler { } public void showProgressDialog(FragmentActivity activity) { + if (mProgressDialogFragment == null) { + return; + } + // TODO: This is a hack!, see // http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult final FragmentManager manager = activity.getSupportFragmentManager(); @@ -89,7 +98,8 @@ public class KeychainIntentServiceHandler extends Handler { Bundle data = message.getData(); if (mProgressDialogFragment == null) { - Log.e(Constants.TAG, "Progress has not been updated because mProgressDialogFragment was null!"); + // Log.e(Constants.TAG, + // "Progress has not been updated because mProgressDialogFragment was null!"); return; } @@ -131,6 +141,7 @@ public class KeychainIntentServiceHandler extends Handler { case MESSAGE_PREVENT_CANCEL: mProgressDialogFragment.setPreventCancel(true); + break; default: Log.e(Constants.TAG, "unknown handler message!"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 869d2e71b..57881f8ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -39,12 +39,11 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; import java.util.Date; @@ -103,7 +102,7 @@ public class PassphraseCacheService extends Service { private BroadcastReceiver mIntentReceiver; - private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>(); + private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<>(); Context mContext; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index 2f2224b24..e2d0c03c9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -59,7 +59,6 @@ public class SaveKeyringParcel implements Parcelable { public ArrayList<String> mRevokeUserIds; public ArrayList<Long> mRevokeSubKeys; - public ArrayList<Long> mStripSubKeys; public SaveKeyringParcel() { reset(); @@ -73,14 +72,30 @@ public class SaveKeyringParcel implements Parcelable { public void reset() { mNewUnlock = null; - mAddUserIds = new ArrayList<String>(); - mAddUserAttribute = new ArrayList<WrappedUserAttribute>(); - mAddSubKeys = new ArrayList<SubkeyAdd>(); + mAddUserIds = new ArrayList<>(); + mAddUserAttribute = new ArrayList<>(); + mAddSubKeys = new ArrayList<>(); mChangePrimaryUserId = null; - mChangeSubKeys = new ArrayList<SubkeyChange>(); - mRevokeUserIds = new ArrayList<String>(); - mRevokeSubKeys = new ArrayList<Long>(); - mStripSubKeys = new ArrayList<Long>(); + mChangeSubKeys = new ArrayList<>(); + mRevokeUserIds = new ArrayList<>(); + mRevokeSubKeys = new ArrayList<>(); + } + + /** Returns true iff this parcel does not contain any operations which require a passphrase. */ + public boolean isRestrictedOnly() { + if (mNewUnlock != null || !mAddUserIds.isEmpty() || !mAddUserAttribute.isEmpty() + || !mAddSubKeys.isEmpty() || mChangePrimaryUserId != null || !mRevokeSubKeys .isEmpty() + || !mRevokeSubKeys.isEmpty()) { + return false; + } + + for (SubkeyChange change : mChangeSubKeys) { + if (change.mRecertify || change.mFlags != null || change.mExpiry != null) { + return false; + } + } + + return true; } // performance gain for using Parcelable here would probably be negligible, @@ -113,26 +128,53 @@ public class SaveKeyringParcel implements Parcelable { } public static class SubkeyChange implements Serializable { - public long mKeyId; + public final long mKeyId; public Integer mFlags; // this is a long unix timestamp, in seconds (NOT MILLISECONDS!) public Long mExpiry; + // if this flag is true, the key will be recertified even if all above + // values are no-ops + public boolean mRecertify; + // if this flag is true, the subkey should be changed to a stripped key + public boolean mDummyStrip; + // if this is non-null, the subkey will be changed to a divert-to-card + // key for the given serial number + public byte[] mDummyDivert; public SubkeyChange(long keyId) { mKeyId = keyId; } + public SubkeyChange(long keyId, boolean recertify) { + mKeyId = keyId; + mRecertify = recertify; + } + public SubkeyChange(long keyId, Integer flags, Long expiry) { mKeyId = keyId; mFlags = flags; mExpiry = expiry; } + public SubkeyChange(long keyId, boolean dummyStrip, byte[] dummyDivert) { + this(keyId, null, null); + + // these flags are mutually exclusive! + if (dummyStrip && dummyDivert != null) { + throw new AssertionError( + "cannot set strip and divert flags at the same time - this is a bug!"); + } + mDummyStrip = dummyStrip; + mDummyDivert = dummyDivert; + } + @Override public String toString() { String out = "mKeyId: " + mKeyId + ", "; out += "mFlags: " + mFlags + ", "; - out += "mExpiry: " + mExpiry; + out += "mExpiry: " + mExpiry + ", "; + out += "mDummyStrip: " + mDummyStrip + ", "; + out += "mDummyDivert: [" + (mDummyDivert == null ? 0 : mDummyDivert.length) + " bytes]"; return out; } @@ -174,7 +216,6 @@ public class SaveKeyringParcel implements Parcelable { mRevokeUserIds = source.createStringArrayList(); mRevokeSubKeys = (ArrayList<Long>) source.readSerializable(); - mStripSubKeys = (ArrayList<Long>) source.readSerializable(); } @Override @@ -197,7 +238,6 @@ public class SaveKeyringParcel implements Parcelable { destination.writeStringList(mRevokeUserIds); destination.writeSerializable(mRevokeSubKeys); - destination.writeSerializable(mStripSubKeys); } public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() { @@ -225,8 +265,7 @@ public class SaveKeyringParcel implements Parcelable { out += "mChangeSubKeys: " + mChangeSubKeys + "\n"; out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n"; out += "mRevokeUserIds: " + mRevokeUserIds + "\n"; - out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n"; - out += "mStripSubKeys: " + mStripSubKeys; + out += "mRevokeSubKeys: " + mRevokeSubKeys; return out; } |