Implement ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN

This commit is contained in:
Dominik Schürmann 2014-04-03 15:02:55 +02:00
parent 11b08c4d98
commit 2df4ccafb5
10 changed files with 155 additions and 147 deletions

View file

@ -112,7 +112,6 @@ public class KeychainIntentService extends IntentService
// possible targets:
public static final int TARGET_BYTES = 1;
public static final int TARGET_URI = 2;
public static final int TARGET_STREAM = 3;
// encrypt
public static final String ENCRYPT_SIGNATURE_KEY_ID = "secret_key_id";
@ -122,7 +121,6 @@ public class KeychainIntentService extends IntentService
public static final String ENCRYPT_MESSAGE_BYTES = "message_bytes";
public static final String ENCRYPT_INPUT_FILE = "input_file";
public static final String ENCRYPT_OUTPUT_FILE = "output_file";
public static final String ENCRYPT_PROVIDER_URI = "provider_uri";
public static final String ENCRYPT_SYMMETRIC_PASSPHRASE = "passphrase";
// decrypt/verify
@ -173,11 +171,7 @@ public class KeychainIntentService extends IntentService
public static final String RESULT_KEY_USAGES = "new_key_usages";
// encrypt
public static final String RESULT_SIGNATURE_BYTES = "signature_data";
public static final String RESULT_SIGNATURE_STRING = "signature_text";
public static final String RESULT_ENCRYPTED_STRING = "encrypted_message";
public static final String RESULT_BYTES = "encrypted_data";
public static final String RESULT_URI = "result_uri";
// decrypt/verify
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
@ -191,10 +185,6 @@ public class KeychainIntentService extends IntentService
// export
public static final String RESULT_EXPORT = "exported";
// query
public static final String RESULT_QUERY_KEY_DATA = "query_key_data";
public static final String RESULT_QUERY_KEY_SEARCH_RESULT = "query_key_search_result";
Messenger mMessenger;
private boolean mIsCanceled;
@ -738,9 +728,6 @@ public class KeychainIntentService extends IntentService
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
// TODO: add extra which requires fingerprint suport and force verification!
// only supported by newer sks keyserver versions
// this downloads the keys and places them into the ImportKeysListEntry entries
HkpKeyServer server = new HkpKeyServer(keyServer);

View file

@ -38,6 +38,8 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@ -88,13 +90,9 @@ public class DecryptFragment extends Fragment {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
// this request is returned after LookupUnknownKeyDialogFragment started
// ImportKeysActivity and user looked uo key
case RESULT_CODE_LOOKUP_KEY: {
Log.d(Constants.TAG, "Returning from Lookup Key...");
if (resultCode == Activity.RESULT_OK) {
// decrypt again
// decryptStart();
// TODO: generate new OpenPgpSignatureResult and display it
}
return;
}
@ -189,6 +187,7 @@ public class DecryptFragment extends Fragment {
/**
* Should be overridden by MessageFragment and FileFragment to start actual decryption
*
* @param passphrase
*/
protected void decryptStart(String passphrase) {

View file

@ -151,6 +151,10 @@ public class DrawerActivity extends ActionBarActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mDrawerToggle == null) {
return super.onCreateOptionsMenu(menu);
}
menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
menu.add(42, MENU_ID_HELP, 101, R.string.menu_help);
@ -159,6 +163,10 @@ public class DrawerActivity extends ActionBarActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle == null) {
return super.onOptionsItemSelected(item);
}
// The action bar home/up action should open or close the drawer.
// ActionBarDrawerToggle will take care of this.
if (mDrawerToggle.onOptionsItemSelected(item)) {
@ -216,14 +224,18 @@ public class DrawerActivity extends ActionBarActivity {
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
if (mDrawerToggle != null) {
mDrawerToggle.syncState();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggles
mDrawerToggle.onConfigurationChanged(newConfig);
if (mDrawerToggle != null) {
mDrawerToggle.onConfigurationChanged(newConfig);
}
}
private class NavItem {

View file

@ -252,7 +252,6 @@ public class EncryptAsymmetricFragment extends Fragment {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case RESULT_CODE_PUBLIC_KEYS: {
if (resultCode == Activity.RESULT_OK) {
Bundle bundle = data.getExtras();
@ -273,11 +272,11 @@ public class EncryptAsymmetricFragment extends Fragment {
}
default: {
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}

View file

@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
@ -34,8 +35,10 @@ import android.support.v7.app.ActionBar;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@ -54,7 +57,6 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
+ "IMPORT_KEY_FROM_QR_CODE";
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEYSERVER";
// TODO: implement:
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN";
@ -86,7 +88,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
ImportKeysNFCFragment.class
};
private int mCurrentNavPostition = -1;
private int mCurrentNavPosition = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -104,15 +106,20 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
getSupportActionBar().setDisplayShowTitleEnabled(false);
setupDrawerNavigation(savedInstanceState);
// set drop down navigation
mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
Context context = getSupportActionBar().getThemedContext();
ArrayAdapter<CharSequence> navigationAdapter = ArrayAdapter.createFromResource(context,
R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this);
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
getSupportActionBar().setTitle(R.string.nav_import);
} else {
setupDrawerNavigation(savedInstanceState);
// set drop down navigation
Context context = getSupportActionBar().getThemedContext();
ArrayAdapter<CharSequence> navigationAdapter = ArrayAdapter.createFromResource(context,
R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this);
}
handleActions(savedInstanceState, getIntent());
}
@ -152,34 +159,47 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// action: directly load data
startListFragment(savedInstanceState, importData, null, null);
}
} else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)) {
String query = null;
if (extras.containsKey(EXTRA_QUERY)) {
query = extras.getString(EXTRA_QUERY);
} else if (extras.containsKey(EXTRA_KEY_ID)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
if (keyId != 0) {
query = PgpKeyHelper.convertKeyIdToHex(keyId);
} else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)
|| ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(action)) {
if (extras.containsKey(EXTRA_QUERY) || extras.containsKey(EXTRA_KEY_ID)) {
/* simple search based on query or key id */
String query = null;
if (extras.containsKey(EXTRA_QUERY)) {
query = extras.getString(EXTRA_QUERY);
} else if (extras.containsKey(EXTRA_KEY_ID)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
if (keyId != 0) {
query = PgpKeyHelper.convertKeyIdToHex(keyId);
}
}
if (query != null && query.length() > 0) {
// display keyserver fragment with query
Bundle args = new Bundle();
args.putString(ImportKeysServerFragment.ARG_QUERY, query);
loadNavFragment(0, args);
// action: search immediately
startListFragment(savedInstanceState, null, null, query);
} else {
Log.e(Constants.TAG, "Query is empty!");
return;
}
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
/*
* search based on fingerprint, here we can enforce a check in the end
* if the right key has been downloaded
*/
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
if (fingerprint != null) {
query = "0x" + fingerprint;
}
loadFromFingerprint(savedInstanceState, fingerprint);
} else {
Log.e(Constants.TAG,
"IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " +
"'fingerprint' extra!");
"IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " +
"'fingerprint' extra!");
return;
}
// display keyserver fragment with query
Bundle args = new Bundle();
args.putString(ImportKeysServerFragment.ARG_QUERY, query);
loadNavFragment(0, args);
// action: search immediately
startListFragment(savedInstanceState, null, null, query);
} else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
// NOTE: this only displays the appropriate fragment, no actions are taken
@ -234,14 +254,14 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
* onNavigationItemSelected() should check whether the Fragment is already in existence
* inside your Activity."
* <p/>
* from http://bit.ly/1dBYThO
* from http://stackoverflow.com/a/14295474
* <p/>
* In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint,
* the fragment would be loaded twice resulting in the query being empty after the second load.
* <p/>
* Our solution:
* To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment
* checks against mCurrentNavPostition.
* checks against mCurrentNavPosition.
*
* @param itemPosition
* @param itemId
@ -257,10 +277,12 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
}
private void loadNavFragment(int itemPosition, Bundle args) {
if (mCurrentNavPostition != itemPosition) {
getSupportActionBar().setSelectedNavigationItem(itemPosition);
if (mCurrentNavPosition != itemPosition) {
if (ActionBar.NAVIGATION_MODE_LIST == getSupportActionBar().getNavigationMode()) {
getSupportActionBar().setSelectedNavigationItem(itemPosition);
}
loadFragment(NAVIGATION_CLASSES[itemPosition], args, mNavigationStrings[itemPosition]);
mCurrentNavPostition = itemPosition;
mCurrentNavPosition = itemPosition;
}
}
@ -280,7 +302,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
if (fingerprint.length() < 16) {
loadFromFingerprint(savedInstanceState, fingerprint);
}
public void loadFromFingerprint(Bundle savedInstanceState, String fingerprint) {
if (fingerprint == null || fingerprint.length() < 40) {
AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint,
AppMsg.STYLE_ALERT).show();
return;
@ -291,6 +317,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// display keyserver fragment with query
Bundle args = new Bundle();
args.putString(ImportKeysServerFragment.ARG_QUERY, query);
args.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true);
loadNavFragment(0, args);
// action: search directly
@ -301,66 +328,6 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
mListFragment.loadNew(importData, dataUri, serverQuery, keyServer);
}
// private void importAndSignOld(final long keyId, final String expectedFingerprint) {
// if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
//
// Thread t = new Thread() {
// @Override
// public void run() {
// try {
// // TODO: display some sort of spinner here while the user waits
//
// // TODO: there should be only 1
// HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]);
// String encodedKey = server.get(keyId);
//
// PGPKeyRing keyring = PGPHelper.decodeKeyRing(new ByteArrayInputStream(
// encodedKey.getBytes()));
// if (keyring != null && keyring instanceof PGPPublicKeyRing) {
// PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
//
// // make sure the fingerprints match before we cache this thing
// String actualFingerprint = PGPHelper.convertFingerprintToHex(publicKeyRing
// .getPublicKey().getFingerprint());
// if (expectedFingerprint.equals(actualFingerprint)) {
// // store the signed key in our local cache
// int retval = PGPMain.storeKeyRingInCache(publicKeyRing);
// if (retval != Id.return_value.ok
// && retval != Id.return_value.updated) {
// status.putString(EXTRA_ERROR,
// "Failed to store signed key in local cache");
// } else {
// Intent intent = new Intent(ImportFromQRCodeActivity.this,
// SignKeyActivity.class);
// intent.putExtra(EXTRA_KEY_ID, keyId);
// startActivityForResult(intent, Id.request.sign_key);
// }
// } else {
// status.putString(
// EXTRA_ERROR,
// "Scanned fingerprint does NOT match the fingerprint of the received key. " +
// "You shouldnt trust this key.");
// }
// }
// } catch (QueryException e) {
// Log.e(TAG, "Failed to query KeyServer", e);
// status.putString(EXTRA_ERROR, "Failed to query KeyServer");
// status.putInt(Constants.extras.STATUS, Id.message.done);
// } catch (IOException e) {
// Log.e(TAG, "Failed to query KeyServer", e);
// status.putString(EXTRA_ERROR, "Failed to query KeyServer");
// status.putInt(Constants.extras.STATUS, Id.message.done);
// }
// }
// };
//
// t.setName("KeyExchange Download Thread");
// t.setDaemon(true);
// t.start();
// }
// }
/**
* Import keys with mImportData
*/
@ -405,6 +372,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
BadImportKeyDialogFragment.newInstance(bad);
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
}
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
ImportKeysActivity.this.setResult(Activity.RESULT_OK);
finish();
}
}
}
};

View file

@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.util.Log;
public class ImportKeysServerFragment extends Fragment {
public static final String ARG_QUERY = "query";
public static final String ARG_KEY_SERVER = "key_server";
public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
private ImportKeysActivity mImportActivity;
@ -140,6 +141,10 @@ public class ImportKeysServerFragment extends Fragment {
Log.d(Constants.TAG, "keyServer: " + keyServer);
}
if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) {
mQueryEditText.setEnabled(false);
}
}
}

View file

@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@ -163,18 +164,13 @@ public class ViewKeyActivity extends ActionBarActivity {
}
private void updateFromKeyserver(Uri dataUri) {
long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri);
if (updateKeyId == 0) {
Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
return;
}
byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri);
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
Intent queryIntent = new Intent(this, ImportKeysActivity.class);
queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
queryIntent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, updateKeyId);
queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
// TODO: lookup with onactivityresult!
startActivityForResult(queryIntent, RESULT_CODE_LOOKUP_KEY);
}
@ -245,4 +241,21 @@ public class ViewKeyActivity extends ActionBarActivity {
mExportHelper.deleteKey(dataUri, returnHandler);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case RESULT_CODE_LOOKUP_KEY: {
if (resultCode == Activity.RESULT_OK) {
// TODO: reload key??? move this into fragment?
}
break;
}
default: {
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
}
}

View file

@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.HkpKeyServer;
import org.sufficientlysecure.keychain.util.KeyServer;
@ -53,7 +54,12 @@ public class ImportKeysListServerLoader
return mEntryListWrapper;
}
queryServer(mServerQuery, mKeyServer);
if (mServerQuery.startsWith("0x") && mServerQuery.length() == 42) {
Log.d(Constants.TAG, "This search is based on a unique fingerprint. Enforce a fingerprint check!");
queryServer(mServerQuery, mKeyServer, true);
} else {
queryServer(mServerQuery, mKeyServer, false);
}
return mEntryListWrapper;
}
@ -84,14 +90,30 @@ public class ImportKeysListServerLoader
/**
* Query keyserver
*/
private void queryServer(String query, String keyServer) {
private void queryServer(String query, String keyServer, boolean enforceFingerprint) {
HkpKeyServer server = new HkpKeyServer(keyServer);
try {
ArrayList<ImportKeysListEntry> searchResult = server.search(query);
mEntryList.clear();
// add result to data
mEntryList.addAll(searchResult);
if (enforceFingerprint) {
String fingerprint = query.substring(2);
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
// query must return only one result!
if (searchResult.size() > 0) {
ImportKeysListEntry uniqueEntry = searchResult.get(0);
/*
* set fingerprint explicitly after query
* to enforce a check when the key is imported by KeychainIntentService
*/
uniqueEntry.setFingerPrintHex(fingerprint);
uniqueEntry.setSelected(true);
mEntryList.add(uniqueEntry);
}
} else {
mEntryList.addAll(searchResult);
}
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
} catch (KeyServer.InsufficientQuery e) {
Log.e(Constants.TAG, "InsufficientQuery", e);

View file

@ -28,20 +28,6 @@
android:src="@drawable/overlay_error" />
</RelativeLayout>
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/lookup_key"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:padding="4dp"
android:text="@string/btn_lookup_key"
bootstrapbutton:bb_icon_left="fa-download"
bootstrapbutton:bb_type="info"
bootstrapbutton:bb_size="small"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<TextView
android:id="@+id/mainUserId"
android:layout_width="wrap_content"
@ -60,4 +46,17 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_below="@+id/mainUserId"
android:layout_toRightOf="@+id/relativeLayout" />
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/lookup_key"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:padding="4dp"
android:text="@string/btn_lookup_key"
bootstrapbutton:bb_icon_left="fa-download"
bootstrapbutton:bb_type="info"
bootstrapbutton:bb_size="small"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>

View file

@ -406,7 +406,7 @@
<string name="import_qr_code_start_with_one">Please start with QR Code with ID 1</string>
<string name="import_qr_code_wrong">QR Code malformed! Please try again!</string>
<string name="import_qr_code_finished">QR Code scanning finished!</string>
<string name="import_qr_code_too_short_fingerprint">Fingerprint contained in this QR Code is too short (&lt; 16 characters)</string>
<string name="import_qr_code_too_short_fingerprint">Fingerprint is too short (&lt; 16 characters)</string>
<string name="import_qr_scan_button">Scan QR Code with \'Barcode Scanner\'</string>
<string name="import_nfc_text">To receive keys via NFC, the device needs to be unlocked.</string>
<string name="import_nfc_help_button">Help</string>