diff options
author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-24 15:29:02 +0100 |
---|---|---|
committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-24 15:29:58 +0100 |
commit | f9ef1160ca764d508dafcb45fbf65f18911fff9c (patch) | |
tree | 816564326b238848669d7a0713f48c84791cd13b /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util | |
parent | fe32e7bff4e724d37903d07cd3b4f0287ec85879 (diff) | |
parent | 8b4388e1a2d5cb2b7f0524c18ad3383e3375ba7b (diff) | |
download | open-keychain-f9ef1160ca764d508dafcb45fbf65f18911fff9c.tar.gz open-keychain-f9ef1160ca764d508dafcb45fbf65f18911fff9c.tar.bz2 open-keychain-f9ef1160ca764d508dafcb45fbf65f18911fff9c.zip |
Merge branch 'development' into linked-identities
Conflicts:
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util')
4 files changed, 393 insertions, 32 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java index 6efc0a5ea..c782d2507 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java @@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.util; import android.accounts.Account; import android.accounts.AccountManager; -import android.annotation.TargetApi; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentUris; @@ -28,7 +27,6 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; -import android.os.Build; import android.provider.ContactsContract; import android.util.Patterns; @@ -37,7 +35,6 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.io.InputStream; import java.util.ArrayList; @@ -303,10 +300,9 @@ public class ContactHelper { return new ArrayList<>(names); } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public static Uri dataUriFromContactUri(Context context, Uri contactUri) { Cursor contactMasterKey = context.getContentResolver().query(contactUri, - new String[]{ContactsContract.Data.DATA2}, null, null, null, null); + new String[]{ContactsContract.Data.DATA2}, null, null, null); if (contactMasterKey != null) { if (contactMasterKey.moveToNext()) { return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0)); @@ -447,7 +443,7 @@ public class ContactHelper { if (cursor != null) { while (cursor.moveToNext()) { long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); - String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; @@ -470,19 +466,19 @@ public class ContactHelper { if (rawContactId != -1) { deleteRawContactById(resolver, rawContactId); } - } else if (userIdSplit[0] != null) { + } else if (userIdSplit.name != null) { // Create a new rawcontact with corresponding key if it does not exist yet if (rawContactId == -1) { Log.d(Constants.TAG, "Insert new raw contact with masterKeyId " + masterKeyId); insertContact(ops, context, masterKeyId); - writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]); + writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name); } // We always update the display name (which is derived from primary user id) // and email addresses from user id - writeContactDisplayName(ops, rawContactId, userIdSplit[0]); + writeContactDisplayName(ops, rawContactId, userIdSplit.name); writeContactEmail(ops, resolver, rawContactId, masterKeyId); try { resolver.applyBatch(ContactsContract.AUTHORITY, ops); @@ -521,9 +517,9 @@ public class ContactHelper { long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); - if (!isExpired && !isRevoked && userIdSplit[0] != null) { + if (!isExpired && !isRevoked && userIdSplit.name != null) { // if expired or revoked will not be removed from keysToDelete or inserted // into main profile ("me" contact) boolean existsInMainProfile = keysToDelete.remove(masterKeyId); @@ -534,7 +530,7 @@ public class ContactHelper { ArrayList<ContentProviderOperation> ops = new ArrayList<>(); insertMainProfileRawContact(ops, masterKeyId); - writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]); + writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name); try { resolver.applyBatch(ContactsContract.AUTHORITY, ops); @@ -715,7 +711,6 @@ public class ContactHelper { * * @return raw contact id or -1 if not found */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private static long findRawContactId(ContentResolver resolver, long masterKeyId) { long rawContactId = -1; Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, @@ -725,7 +720,7 @@ public class ContactHelper { ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?", new String[]{ Constants.ACCOUNT_TYPE, Long.toString(masterKeyId) - }, null, null); + }, null); if (raw != null) { if (raw.moveToNext()) { rawContactId = raw.getLong(0); @@ -776,14 +771,14 @@ public class ContactHelper { null, null); if (ids != null) { while (ids.moveToNext()) { - String[] userId = KeyRing.splitUserId(ids.getString(0)); - if (userId[1] != null) { + KeyRing.UserId userId = KeyRing.splitUserId(ids.getString(0)); + if (userId.email != null) { ops.add(referenceRawContact( ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), rawContactId) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1]) + .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId.email) .build()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java index cda5892fe..7b164f2b2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java @@ -47,21 +47,6 @@ public class ExportHelper { this.mActivity = activity; } - public void deleteKey(Uri dataUri, Handler deleteHandler) { - try { - long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(deleteHandler); - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - new long[]{ masterKeyId }); - deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog"); - } catch (PgpKeyNotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - } - } - /** * Show dialog where to export keys */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java new file mode 100644 index 000000000..e4e4e4d05 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 Kent Nguyen <kentnguyen@moneylover.me> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.util; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.nfc.NdefMessage; +import android.nfc.NdefRecord; +import android.nfc.NfcAdapter; +import android.nfc.NfcEvent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.provider.Settings; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.lang.ref.WeakReference; + +/** + * This class contains NFC functionality that can be shared across Fragments or Activities. + */ + +public class NfcHelper { + + private Activity mActivity; + private ProviderHelper mProviderHelper; + + /** + * NFC: This handler receives a message from onNdefPushComplete + */ + private static NfcHandler mNfcHandler; + + private NfcAdapter mNfcAdapter; + private NfcAdapter.CreateNdefMessageCallback mNdefCallback; + private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback; + private byte[] mNfcKeyringBytes; + private static final int NFC_SENT = 1; + + /** + * Initializes the NfcHelper. + */ + public NfcHelper(final Activity activity, final ProviderHelper providerHelper) { + mActivity = activity; + mProviderHelper = providerHelper; + + mNfcHandler = new NfcHandler(mActivity); + } + + /** + * Return true if the NFC Adapter of this Helper has any features enabled. + * + * @return true if this NFC Adapter has any features enabled + */ + public boolean isEnabled() { + return mNfcAdapter.isEnabled(); + } + + /** + * NFC: Initialize NFC sharing if OS and device supports it + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public void initNfc(final Uri dataUri) { + // check if NFC Beam is supported (>= Android 4.1) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + + // Implementation for the CreateNdefMessageCallback interface + mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() { + @Override + public NdefMessage createNdefMessage(NfcEvent event) { + /* + * When a device receives a push with an AAR in it, the application specified in the AAR is + * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to + * guarantee that this activity starts when receiving a beamed message. For now, this code + * uses the tag dispatch system. + */ + return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, + mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); + } + }; + + // Implementation for the OnNdefPushCompleteCallback interface + mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() { + @Override + public void onNdefPushComplete(NfcEvent event) { + // A handler is needed to send messages to the activity when this + // callback occurs, because it happens from a binder thread + mNfcHandler.obtainMessage(NFC_SENT).sendToTarget(); + } + }; + + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter != null) { + /* + * Retrieve mNfcKeyringBytes here asynchronously (to not block the UI) + * and init nfc adapter afterwards. + * mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process + * has no permissions to query the Uri. + */ + AsyncTask<Void, Void, Void> initTask = + new AsyncTask<Void, Void, Void>() { + protected Void doInBackground(Void... unused) { + try { + Uri blobUri = + KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri); + mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData( + blobUri, + KeychainContract.KeyRingData.KEY_RING_DATA, + ProviderHelper.FIELD_TYPE_BLOB); + } catch (ProviderHelper.NotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + } + + // no AsyncTask return (Void) + return null; + } + + protected void onPostExecute(Void unused) { + // Register callback to set NDEF message + mNfcAdapter.setNdefPushMessageCallback(mNdefCallback, + mActivity); + // Register callback to listen for message-sent success + mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback, + mActivity); + } + }; + + initTask.execute(); + } + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void invokeNfcBeam() { + // Check if device supports NFC + if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { + Notify.create(mActivity, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + return; + } + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { + Notify.create(mActivity, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_nfc_preferences).show(); + + return; + } + + if (!mNfcAdapter.isNdefPushEnabled()) { + Notify.create(mActivity, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_beam_preferences).show(); + + return; + } + + mNfcAdapter.invokeBeam(mActivity); + } + + /** + * A static subclass of {@link Handler} with a {@link WeakReference} to an {@link Activity} to avoid memory leaks. + */ + private static class NfcHandler extends Handler { + private final WeakReference<Activity> mActivityReference; + + public NfcHandler(Activity activity) { + mActivityReference = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + Activity activity = mActivityReference.get(); + + if (activity != null) { + switch (msg.what) { + case NFC_SENT: + Notify.create(activity, R.string.nfc_successful, Notify.Style.OK).show(); + break; + } + } + } + } + +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java new file mode 100644 index 000000000..06efdde4d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.util; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.Editable; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.Constants; + +import java.util.Arrays; + +/** + * Passwords should not be stored as Strings in memory. + * This class wraps a char[] that can be erased after it is no longer used. + * See also: + * <p/> + * http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#PBEEx + * https://github.com/c-a-m/passfault/blob/master/core/src/main/java/org/owasp/passfault/SecureString.java + * http://stackoverflow.com/q/8881291 + * http://stackoverflow.com/a/15844273 + */ +public class Passphrase implements Parcelable { + private char[] mPassphrase; + + /** + * According to http://stackoverflow.com/a/15844273 EditText is not using String internally + * but char[]. Thus, we can get the char[] directly from it. + */ + public Passphrase(Editable editable) { + int pl = editable.length(); + mPassphrase = new char[pl]; + editable.getChars(0, pl, mPassphrase, 0); + // TODO: clean up internal char[] of EditText after getting the passphrase? +// editText.getText().replace() + } + + public Passphrase(EditText editText) { + this(editText.getText()); + } + + public Passphrase(char[] passphrase) { + mPassphrase = passphrase; + } + + public Passphrase(String passphrase) { + mPassphrase = passphrase.toCharArray(); + } + + /** + * Creates a passphrase object with an empty ("") passphrase + */ + public Passphrase() { + setEmpty(); + } + + public char[] getCharArray() { + return mPassphrase; + } + + public void setEmpty() { + removeFromMemory(); + mPassphrase = new char[0]; + } + + public boolean isEmpty() { + return (length() == 0); + } + + public int length() { + return mPassphrase.length; + } + + public char charAt(int index) { + return mPassphrase[index]; + } + + /** + * Manually clear the underlying array holding the characters + */ + public void removeFromMemory() { + if (mPassphrase != null) { + Arrays.fill(mPassphrase, ' '); + } + } + + @Override + public void finalize() throws Throwable { + removeFromMemory(); + super.finalize(); + } + + @Override + public String toString() { + if (Constants.DEBUG) { + return "Passphrase{" + + "mPassphrase=" + Arrays.toString(mPassphrase) + + '}'; + } else { + return "Passphrase: hidden"; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Passphrase that = (Passphrase) o; + if (!Arrays.equals(mPassphrase, that.mPassphrase)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return mPassphrase != null ? Arrays.hashCode(mPassphrase) : 0; + } + + private Passphrase(Parcel source) { + mPassphrase = source.createCharArray(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharArray(mPassphrase); + } + + public static final Creator<Passphrase> CREATOR = new Creator<Passphrase>() { + public Passphrase createFromParcel(final Parcel source) { + return new Passphrase(source); + } + + public Passphrase[] newArray(final int size) { + return new Passphrase[size]; + } + }; + + public int describeContents() { + return 0; + } +} |