diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index d0f50d5cc..8c66176fd 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -624,6 +624,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java
index 1cd0aaf2f..db62d53c5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java
@@ -123,7 +123,8 @@ public class CreateKeyYubiImportFragment extends Fragment implements NfcListener
});
}
- mListFragment = ImportKeysListFragment.newInstance(null, null, "0x" + mNfcFingerprint, true);
+ mListFragment = ImportKeysListFragment.newInstance(null, null,
+ "0x" + mNfcFingerprint, true, null);
view.findViewById(R.id.button_search).setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 7fe5be793..5d9950db6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.service.CloudImportService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -62,6 +63,8 @@ public class ImportKeysActivity extends BaseNfcActivity {
// Actions for internal use only:
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_FILE";
+ public static final String ACTION_SEARCH_KEYSERVER_FROM_URL = Constants.INTENT_PREFIX
+ + "SEARCH_KEYSERVER_FROM_URL";
public static final String EXTRA_RESULT = "result";
// only used by ACTION_IMPORT_KEY
@@ -112,15 +115,19 @@ public class ImportKeysActivity extends BaseNfcActivity {
}
if (action == null) {
- startCloudFragment(savedInstanceState, null, false);
- startListFragment(savedInstanceState, null, null, null);
+ startCloudFragment(savedInstanceState, null, false, null);
+ startListFragment(savedInstanceState, null, null, null, null);
return;
}
if (Intent.ACTION_VIEW.equals(action)) {
- // Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
- // delegate action to ACTION_IMPORT_KEY
- action = ACTION_IMPORT_KEY;
+ if (scheme.equals("http") || scheme.equals("https")) {
+ action = ACTION_SEARCH_KEYSERVER_FROM_URL;
+ } else {
+ // Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
+ // delegate action to ACTION_IMPORT_KEY
+ action = ACTION_IMPORT_KEY;
+ }
}
switch (action) {
@@ -130,12 +137,12 @@ public class ImportKeysActivity extends BaseNfcActivity {
if (dataUri != null) {
// action: directly load data
- startListFragment(savedInstanceState, null, dataUri, null);
+ startListFragment(savedInstanceState, null, dataUri, null, null);
} else if (extras.containsKey(EXTRA_KEY_BYTES)) {
byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES);
// action: directly load data
- startListFragment(savedInstanceState, importData, null, null);
+ startListFragment(savedInstanceState, importData, null, null, null);
}
break;
}
@@ -162,10 +169,10 @@ public class ImportKeysActivity extends BaseNfcActivity {
if (query != null && query.length() > 0) {
// display keyserver fragment with query
- startCloudFragment(savedInstanceState, query, false);
+ startCloudFragment(savedInstanceState, query, false, null);
// action: search immediately
- startListFragment(savedInstanceState, null, null, query);
+ startListFragment(savedInstanceState, null, null, query, null);
} else {
Log.e(Constants.TAG, "Query is empty!");
return;
@@ -181,10 +188,10 @@ public class ImportKeysActivity extends BaseNfcActivity {
String query = "0x" + fingerprint;
// display keyserver fragment with query
- startCloudFragment(savedInstanceState, query, true);
+ startCloudFragment(savedInstanceState, query, true, null);
// action: search immediately
- startListFragment(savedInstanceState, null, null, query);
+ startListFragment(savedInstanceState, null, null, query, null);
}
} else {
Log.e(Constants.TAG,
@@ -200,7 +207,29 @@ public class ImportKeysActivity extends BaseNfcActivity {
startFileFragment(savedInstanceState);
// no immediate actions!
- startListFragment(savedInstanceState, null, null, null);
+ startListFragment(savedInstanceState, null, null, null, null);
+ break;
+ }
+ case ACTION_SEARCH_KEYSERVER_FROM_URL: {
+ // need to process URL to get search query and keyserver authority
+ String query = dataUri.getQueryParameter("search");
+ String keyserver = dataUri.getAuthority();
+ // if query not specified, we still allow users to search the keyserver in the link
+ if (query == null) {
+ Notify.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE,
+ Notify.Style.WARN).show(mTopFragment);
+ // we just set the keyserver
+ startCloudFragment(savedInstanceState, null, false, keyserver);
+ // it's not necessary to set the keyserver for ImportKeysListFragment since
+ // it'll be taken care of by ImportKeysCloudFragment when the user clicks
+ // the search button
+ startListFragment(savedInstanceState, null, null, null, null);
+ } else {
+ // we allow our users to edit the query if they wish
+ startCloudFragment(savedInstanceState, query, false, keyserver);
+ // search immediately
+ startListFragment(savedInstanceState, null, null, query, keyserver);
+ }
break;
}
case ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN: {
@@ -208,18 +237,31 @@ public class ImportKeysActivity extends BaseNfcActivity {
startFileFragment(savedInstanceState);
// no immediate actions!
- startListFragment(savedInstanceState, null, null, null);
+ startListFragment(savedInstanceState, null, null, null, null);
break;
}
default: {
- startCloudFragment(savedInstanceState, null, false);
- startListFragment(savedInstanceState, null, null, null);
+ startCloudFragment(savedInstanceState, null, false, null);
+ startListFragment(savedInstanceState, null, null, null, null);
break;
}
}
}
- private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) {
+
+ /**
+ * if the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately
+ * load content
+ *
+ * @param savedInstanceState
+ * @param bytes bytes containing list of keyrings to import
+ * @param dataUri uri to file to import keyrings from
+ * @param serverQuery query to search for on the keyserver
+ * @param keyserver keyserver authority to search on. If null will use keyserver from
+ * user preferences
+ */
+ private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri,
+ String serverQuery, String keyserver) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
@@ -227,8 +269,8 @@ public class ImportKeysActivity extends BaseNfcActivity {
return;
}
- // Create an instance of the fragment
- mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery);
+ mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false,
+ keyserver);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
@@ -259,7 +301,18 @@ public class ImportKeysActivity extends BaseNfcActivity {
getSupportFragmentManager().executePendingTransactions();
}
- private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit) {
+ /**
+ * loads the CloudFragment, which consists of the search bar, search button and settings icon
+ * visually.
+ *
+ * @param savedInstanceState
+ * @param query search query
+ * @param disableQueryEdit if true, user will not be able to edit the search query
+ * @param keyserver keyserver authority to use for search, if null will use keyserver
+ * specified in user preferences
+ */
+
+ private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String keyserver) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
@@ -268,7 +321,7 @@ public class ImportKeysActivity extends BaseNfcActivity {
}
// Create an instance of the fragment
- mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit);
+ mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit, keyserver);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
index 91ca93c36..1cd5c24f3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
@@ -45,6 +45,7 @@ import java.util.List;
public class ImportKeysCloudFragment extends Fragment {
public static final String ARG_QUERY = "query";
public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
+ public static final String ARG_KEYSERVER = "keyserver";
private ImportKeysActivity mImportActivity;
@@ -54,13 +55,20 @@ public class ImportKeysCloudFragment extends Fragment {
/**
* Creates new instance of this fragment
+ *
+ * @param query query to search for
+ * @param disableQueryEdit if true, user cannot edit query
+ * @param keyserver specified keyserver authority to use. If null, will use keyserver
+ * specified in user preferences
*/
- public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit) {
+ public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit,
+ String keyserver) {
ImportKeysCloudFragment frag = new ImportKeysCloudFragment();
Bundle args = new Bundle();
args.putString(ARG_QUERY, query);
args.putBoolean(ARG_DISABLE_QUERY_EDIT, disableQueryEdit);
+ args.putString(ARG_KEYSERVER, keyserver);
frag.setArguments(args);
@@ -151,8 +159,17 @@ public class ImportKeysCloudFragment extends Fragment {
}
private void search(String query) {
- Preferences prefs = Preferences.getPreferences(getActivity());
- mImportActivity.loadCallback(new ImportKeysListFragment.CloudLoaderState(query, prefs.getCloudSearchPrefs()));
+ Preferences.CloudSearchPrefs cloudSearchPrefs;
+ String explicitKeyserver = getArguments().getString(ARG_KEYSERVER);
+ // no explicit keyserver passed
+ if (explicitKeyserver == null) {
+ cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
+ } else {
+ // assume we are also meant to search keybase.io
+ cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, explicitKeyserver);
+ }
+ mImportActivity.loadCallback(
+ new ImportKeysListFragment.CloudLoaderState(query, cloudSearchPrefs));
toggleKeyboard(false);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index b9fdbea5c..81ff87216 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -58,6 +58,7 @@ public class ImportKeysListFragment extends ListFragment implements
private static final String ARG_BYTES = "bytes";
public static final String ARG_SERVER_QUERY = "query";
public static final String ARG_NON_INTERACTIVE = "non_interactive";
+ public static final String ARG_KEYSERVER_URL = "keyserver_url";
private Activity mActivity;
private ImportKeysAdapter mAdapter;
@@ -78,7 +79,8 @@ public class ImportKeysListFragment extends ListFragment implements
return mAdapter.getData();
}
- /** Returns an Iterator (with size) of the selected data items.
+ /**
+ * Returns an Iterator (with size) of the selected data items.
* This iterator is sort of a tradeoff, it's slightly more complex than an
* ArrayList would have been, but we save some memory by just returning
* relevant elements on demand.
@@ -121,12 +123,36 @@ public class ImportKeysListFragment extends ListFragment implements
}
- public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery) {
- return newInstance(bytes, dataUri, serverQuery, false);
+ /**
+ * Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified
+ * by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order
+ *
+ * @param bytes byte data containing list of keyrings to be imported
+ * @param dataUri file from which keyrings are to be imported
+ * @param serverQuery query to search for on keyserver
+ * @param keyserver if not null, will perform search on specified keyserver. Else, uses
+ * keyserver specified in user preferences
+ * @return fragment with arguments set based on passed parameters
+ */
+ public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
+ String keyserver) {
+ return newInstance(bytes, dataUri, serverQuery, false, keyserver);
}
- public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri,
- String serverQuery, boolean nonInteractive) {
+ /**
+ * Visually consists of a list of keyrings with checkboxes to specify which are to be imported
+ * Can immediately load keyrings specified by any of its parameters
+ *
+ * @param bytes byte data containing list of keyrings to be imported
+ * @param dataUri file from which keyrings are to be imported
+ * @param serverQuery query to search for on keyserver
+ * @param nonInteractive if true, users will not be able to check/uncheck items in the list
+ * @param keyserver if set, will perform search on specified keyserver. If null, falls back
+ * to keyserver specified in user preferences
+ * @return fragment with arguments set based on passed parameters
+ */
+ public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
+ boolean nonInteractive, String keyserver) {
ImportKeysListFragment frag = new ImportKeysListFragment();
Bundle args = new Bundle();
@@ -134,6 +160,7 @@ public class ImportKeysListFragment extends ListFragment implements
args.putParcelable(ARG_DATA_URI, dataUri);
args.putString(ARG_SERVER_QUERY, serverQuery);
args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive);
+ args.putString(ARG_KEYSERVER_URL, keyserver);
frag.setArguments(args);
@@ -180,16 +207,23 @@ public class ImportKeysListFragment extends ListFragment implements
setListAdapter(mAdapter);
Bundle args = getArguments();
- Uri dataUri = args.containsKey(ARG_DATA_URI) ? args.getParcelable(ARG_DATA_URI) : null;
- byte[] bytes = args.containsKey(ARG_BYTES) ? args.getByteArray(ARG_BYTES) : null;
- String query = args.containsKey(ARG_SERVER_QUERY) ? args.getString(ARG_SERVER_QUERY) : null;
- mNonInteractive = args.containsKey(ARG_NON_INTERACTIVE) ? args.getBoolean(ARG_NON_INTERACTIVE) : false;
+ Uri dataUri = args.getParcelable(ARG_DATA_URI);
+ byte[] bytes = args.getByteArray(ARG_BYTES);
+ String query = args.getString(ARG_SERVER_QUERY);
+ String keyserver = args.getString(ARG_KEYSERVER_URL);
+ mNonInteractive = args.getBoolean(ARG_NON_INTERACTIVE, false);
if (dataUri != null || bytes != null) {
mLoaderState = new BytesLoaderState(bytes, dataUri);
} else if (query != null) {
- Preferences prefs = Preferences.getPreferences(getActivity());
- mLoaderState = new CloudLoaderState(query, prefs.getCloudSearchPrefs());
+ Preferences.CloudSearchPrefs cloudSearchPrefs;
+ if (keyserver != null) {
+ cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
+ } else {
+ cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, keyserver);
+ }
+
+ mLoaderState = new CloudLoaderState(query, cloudSearchPrefs);
}
getListView().setOnTouchListener(new OnTouchListener() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
index b9f1bf870..5c49de727 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
@@ -58,13 +58,12 @@ import java.util.Locale;
public class ImportKeysProxyActivity extends FragmentActivity {
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
+ // implies activity returns scanned fingerprint as extra and does not import
public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT";
public static final String ACTION_SCAN_IMPORT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_IMPORT";
public static final String EXTRA_FINGERPRINT = "fingerprint";
- boolean returnResult;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -82,10 +81,8 @@ public class ImportKeysProxyActivity extends FragmentActivity {
if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
// Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned
- returnResult = false;
processScannedContent(dataUri);
} else if (ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) {
- returnResult = false;
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
.setPrompt(getString(R.string.import_qr_code_text))
@@ -93,7 +90,6 @@ public class ImportKeysProxyActivity extends FragmentActivity {
integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
integrator.initiateScan();
} else if (ACTION_SCAN_WITH_RESULT.equals(action)) {
- returnResult = true;
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
.setPrompt(getString(R.string.import_qr_code_text))
@@ -103,7 +99,6 @@ public class ImportKeysProxyActivity extends FragmentActivity {
} else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
// Check to see if the Activity started due to an Android Beam
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- returnResult = false;
handleActionNdefDiscovered(getIntent());
} else {
Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1");
@@ -148,6 +143,8 @@ public class ImportKeysProxyActivity extends FragmentActivity {
private void processScannedContent(Uri uri) {
+ String action = getIntent().getAction();
+
Log.d(Constants.TAG, "scanned: " + uri);
String fingerprint = null;
@@ -166,7 +163,7 @@ public class ImportKeysProxyActivity extends FragmentActivity {
return;
}
- if (returnResult) {
+ if (ACTION_SCAN_WITH_RESULT.equals(action)) {
Intent result = new Intent();
result.putExtra(EXTRA_FINGERPRINT, fingerprint);
setResult(RESULT_OK, result);
@@ -177,15 +174,17 @@ public class ImportKeysProxyActivity extends FragmentActivity {
}
public void returnResult(Intent data) {
- if (returnResult) {
- setResult(RESULT_OK, data);
- finish();
- } else {
+ String action = getIntent().getAction();
+
+ if (ACTION_QR_CODE_API.equals(action)) {
// display last log message but as Toast for calls from outside OpenKeychain
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
String str = getString(result.getLog().getLast().mType.getMsgId());
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
finish();
+ } else {
+ setResult(RESULT_OK, data);
+ finish();
}
}
@@ -209,7 +208,7 @@ public class ImportKeysProxyActivity extends FragmentActivity {
}
- private void startImportService (ArrayList keyRings) {
+ private void startImportService(ArrayList keyRings) {
// Message is received after importing is done in KeychainIntentService
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index 5447c5f96..139512ba9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -82,12 +82,12 @@ public class ImportKeysListLoader
@Override
protected void onStartLoading() {
- forceLoad();
+ super.forceLoad();
}
@Override
protected void onStopLoading() {
- cancelLoad();
+ super.cancelLoad();
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
index db7aba519..1d09b281f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
@@ -57,6 +57,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
public static final int REQUEST_CODE_PASSPHRASE = 1;
protected Passphrase mPin;
+ protected boolean mPw1ValidForMultipleSignatures;
+ protected boolean mPw1ValidatedForSignature;
+ protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
private NfcAdapter mNfcAdapter;
private IsoDep mIsoDep;
@@ -197,10 +200,15 @@ public abstract class BaseNfcActivity extends BaseActivity {
+ "06" // Lc (number of bytes)
+ "D27600012401" // Data (6 bytes)
+ "00"; // Le
- if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection
+ if ( ! nfcCommunicate(opening).endsWith(accepted)) { // activate connection
throw new IOException("Initialization failed!");
}
+ byte[] pwStatusBytes = nfcGetPwStatusBytes();
+ mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1);
+ mPw1ValidatedForSignature = false;
+ mPw1ValidatedForDecrypt = false;
+
onNfcPerform();
mIsoDep.close();
@@ -278,6 +286,15 @@ public abstract class BaseNfcActivity extends BaseActivity {
return fptlv.mV;
}
+ /** Return the PW Status Bytes from the card. This is a simple DO; no TLV decoding needed.
+ *
+ * @return Seven bytes in fixed format, plus 0x9000 status word at the end.
+ */
+ public byte[] nfcGetPwStatusBytes() throws IOException {
+ String data = "00CA00C400";
+ return mIsoDep.transceive(Hex.decode(data));
+ }
+
/** Return the fingerprint from application specific data stored on tag, or
* null if it doesn't exist.
*
@@ -316,7 +333,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
* @return a big integer representing the MPI for the given hash
*/
public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException {
- nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing)
+ if (!mPw1ValidatedForSignature) {
+ nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing)
+ }
// dsi, including Lc
String dsi;
@@ -391,6 +410,10 @@ public abstract class BaseNfcActivity extends BaseActivity {
Log.d(Constants.TAG, "final response:" + status);
+ if (!mPw1ValidForMultipleSignatures) {
+ mPw1ValidatedForSignature = false;
+ }
+
if ( ! "9000".equals(status)) {
throw new IOException("Bad NFC response code: " + status);
}
@@ -410,7 +433,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
* @return the decoded session key
*/
public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException {
- nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption)
+ if (!mPw1ValidatedForDecrypt) {
+ nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption)
+ }
String firstApdu = "102a8086fe";
String secondApdu = "002a808603";
@@ -458,6 +483,12 @@ public abstract class BaseNfcActivity extends BaseActivity {
handlePinError();
throw new IOException("Bad PIN!");
}
+
+ if (mode == 0x81) {
+ mPw1ValidatedForSignature = true;
+ } else if (mode == 0x82) {
+ mPw1ValidatedForDecrypt = true;
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
index 8a7638054..303687315 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -193,6 +193,11 @@ public class Preferences {
public final boolean searchKeybase;
public final String keyserver;
+ /**
+ * @param searchKeyserver should passed keyserver be searched
+ * @param searchKeybase should keybase.io be searched
+ * @param keyserver the keyserver url authority to search on
+ */
public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, String keyserver) {
this.searchKeyserver = searchKeyserver;
this.searchKeybase = searchKeybase;
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index c1e3b51f9..d4c67aa23 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -404,6 +404,9 @@
"Scan QR Code"
"Place your camera over the QR Code!"
+
+ "No search parameter found. You may still attempt manually searching the keyserver."
+
"Details"
", with warnings"