aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java53
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java38
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RedirectImportKeysActivity.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java176
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java860
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java18
21 files changed, 555 insertions, 1046 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index b1fec3aae..44b185c52 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -47,9 +47,9 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
public static final String EXTRA_SECURITY_TOKEN_PIN = "yubi_key_pin";
public static final String EXTRA_SECURITY_TOKEN_ADMIN_PIN = "yubi_key_admin_pin";
- public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
- public static final String EXTRA_NFC_AID = "nfc_aid";
- public static final String EXTRA_NFC_FINGERPRINTS = "nfc_fingerprints";
+ public static final String EXTRA_SECURITY_TOKEN_USER_ID = "nfc_user_id";
+ public static final String EXTRA_SECURITY_TOKEN_AID = "nfc_aid";
+ public static final String EXTRA_SECURITY_FINGERPRINTS = "nfc_fingerprints";
public static final String FRAGMENT_TAG = "currentFragment";
@@ -66,8 +66,8 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
byte[] mScannedFingerprints;
- byte[] mNfcAid;
- String mNfcUserId;
+ byte[] mSecurityTokenAid;
+ String mSecurityTokenUserId;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -107,10 +107,10 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
mFirstTime = intent.getBooleanExtra(EXTRA_FIRST_TIME, false);
mCreateSecurityToken = intent.getBooleanExtra(EXTRA_CREATE_SECURITY_TOKEN, false);
- if (intent.hasExtra(EXTRA_NFC_FINGERPRINTS)) {
- byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS);
- String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
- byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
+ if (intent.hasExtra(EXTRA_SECURITY_FINGERPRINTS)) {
+ byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_SECURITY_FINGERPRINTS);
+ String nfcUserId = intent.getStringExtra(EXTRA_SECURITY_TOKEN_USER_ID);
+ byte[] nfcAid = intent.getByteArrayExtra(EXTRA_SECURITY_TOKEN_AID);
if (containsKeys(nfcFingerprints)) {
Fragment frag = CreateSecurityTokenImportResetFragment.newInstance(
@@ -143,24 +143,29 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
}
@Override
- protected void doNfcInBackground() throws IOException {
- if (mCurrentFragment instanceof NfcListenerFragment) {
- ((NfcListenerFragment) mCurrentFragment).doNfcInBackground();
+ protected void doSecurityTokenInBackground() throws IOException {
+ if (mCurrentFragment instanceof SecurityTokenListenerFragment) {
+ ((SecurityTokenListenerFragment) mCurrentFragment).doSecurityTokenInBackground();
return;
}
- mScannedFingerprints = nfcGetFingerprints();
- mNfcAid = nfcGetAid();
- mNfcUserId = nfcGetUserId();
+ mScannedFingerprints = mSecurityTokenHelper.getFingerprints();
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
+ mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
}
@Override
- protected void onNfcPostExecute() {
- if (mCurrentFragment instanceof NfcListenerFragment) {
- ((NfcListenerFragment) mCurrentFragment).onNfcPostExecute();
+ protected void onSecurityTokenPostExecute() {
+ if (mCurrentFragment instanceof SecurityTokenListenerFragment) {
+ ((SecurityTokenListenerFragment) mCurrentFragment).onSecurityTokenPostExecute();
return;
}
+ // We don't want get back to wait activity mainly because it looks weird with otg token
+ if (mCurrentFragment instanceof CreateSecurityTokenWaitFragment) {
+ getSupportFragmentManager().popBackStackImmediate();
+ }
+
if (containsKeys(mScannedFingerprints)) {
try {
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mScannedFingerprints);
@@ -169,15 +174,15 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
Intent intent = new Intent(this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mScannedFingerprints);
startActivity(intent);
finish();
} catch (PgpKeyNotFoundException e) {
Fragment frag = CreateSecurityTokenImportResetFragment.newInstance(
- mScannedFingerprints, mNfcAid, mNfcUserId);
+ mScannedFingerprints, mSecurityTokenAid, mSecurityTokenUserId);
loadFragment(frag, FragAction.TO_RIGHT);
}
} else {
@@ -255,9 +260,9 @@ public class CreateKeyActivity extends BaseSecurityTokenNfcActivity {
}
- interface NfcListenerFragment {
- void doNfcInBackground() throws IOException;
- void onNfcPostExecute();
+ interface SecurityTokenListenerFragment {
+ void doSecurityTokenInBackground() throws IOException;
+ void onSecurityTokenPostExecute();
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
index b020a0dba..b871f471c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
@@ -44,7 +44,6 @@ import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
import java.util.ArrayList;
import java.util.List;
-import java.util.regex.Pattern;
public class CreateKeyEmailFragment extends Fragment {
private CreateKeyActivity mCreateKeyActivity;
@@ -52,10 +51,6 @@ public class CreateKeyEmailFragment extends Fragment {
private ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels = new ArrayList<>();
private EmailAdapter mEmailAdapter;
- // NOTE: Do not use more complicated pattern like defined in android.util.Patterns.EMAIL_ADDRESS
- // EMAIL_ADDRESS fails for mails with umlauts for example
- private static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\S]+@[\\S]+\\.[a-z]+$");
-
/**
* Creates new instance of this fragment
*/
@@ -76,16 +71,15 @@ public class CreateKeyEmailFragment extends Fragment {
* @return true if EditText is not empty
*/
private boolean isMainEmailValid(EditText editText) {
- boolean output = true;
- if (!checkEmail(editText.getText().toString(), false)) {
+ if (editText.getText().length() == 0) {
editText.setError(getString(R.string.create_key_empty));
editText.requestFocus();
- output = false;
- } else {
- editText.setError(null);
+ return false;
+ } else if (!checkEmail(editText.getText().toString(), false)){
+ return false;
}
-
- return output;
+ editText.setError(null);
+ return true;
}
@Override
@@ -146,10 +140,9 @@ public class CreateKeyEmailFragment extends Fragment {
* @return
*/
private boolean checkEmail(String email, boolean additionalEmail) {
- // check for email format or if the user did any input
- if (!isEmailFormatValid(email)) {
+ if (email.isEmpty()) {
Notify.create(getActivity(),
- getString(R.string.create_key_email_invalid_email),
+ getString(R.string.create_key_email_empty_email),
Notify.LENGTH_LONG, Notify.Style.ERROR).show(CreateKeyEmailFragment.this);
return false;
}
@@ -167,18 +160,6 @@ public class CreateKeyEmailFragment extends Fragment {
}
/**
- * Checks the email format
- * Uses the default Android Email Pattern
- *
- * @param email
- * @return
- */
- private boolean isEmailFormatValid(String email) {
- // check for email format or if the user did any input
- return !(email.length() == 0 || !EMAIL_PATTERN.matcher(email).matches());
- }
-
- /**
* Checks for duplicated emails inside the additional email adapter.
*
* @param email
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index cbf862074..896df0ad2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.util.Preferences;
import java.util.Date;
import java.util.Iterator;
+import java.util.regex.Pattern;
public class CreateKeyFinalFragment extends Fragment {
@@ -81,6 +82,10 @@ public class CreateKeyFinalFragment extends Fragment {
private OperationResult mQueuedFinishResult;
private EditKeyResult mQueuedDisplayResult;
+ // NOTE: Do not use more complicated pattern like defined in android.util.Patterns.EMAIL_ADDRESS
+ // EMAIL_ADDRESS fails for mails with umlauts for example
+ private static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\S]+@[\\S]+\\.[a-z]+$");
+
public static CreateKeyFinalFragment newInstance() {
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
frag.setRetainInstance(true);
@@ -106,7 +111,11 @@ public class CreateKeyFinalFragment extends Fragment {
CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
// set values
- mNameEdit.setText(createKeyActivity.mName);
+ if (createKeyActivity.mName != null) {
+ mNameEdit.setText(createKeyActivity.mName);
+ } else {
+ mNameEdit.setText(getString(R.string.user_id_no_name));
+ }
if (createKeyActivity.mAdditionalEmails != null && createKeyActivity.mAdditionalEmails.size() > 0) {
String emailText = createKeyActivity.mEmail + ", ";
Iterator<?> it = createKeyActivity.mAdditionalEmails.iterator();
@@ -122,6 +131,8 @@ public class CreateKeyFinalFragment extends Fragment {
mEmailEdit.setText(createKeyActivity.mEmail);
}
+ checkEmailValidity();
+
mCreateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -309,6 +320,31 @@ public class CreateKeyFinalFragment extends Fragment {
return saveKeyringParcel;
}
+ private void checkEmailValidity() {
+ CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
+
+ boolean emailsValid = true;
+ if (!EMAIL_PATTERN.matcher(createKeyActivity.mEmail).matches()) {
+ emailsValid = false;
+ }
+ if (createKeyActivity.mAdditionalEmails != null && createKeyActivity.mAdditionalEmails.size() > 0) {
+ for (Iterator<?> it = createKeyActivity.mAdditionalEmails.iterator(); it.hasNext(); ) {
+ if (!EMAIL_PATTERN.matcher(it.next().toString()).matches()) {
+ emailsValid = false;
+ }
+ }
+ }
+ if (!emailsValid) {
+ mEmailEdit.setError(getString(R.string.create_key_final_email_valid_warning));
+ mEmailEdit.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mNameEdit.requestFocus(); // Workaround to remove focus from email
+ }
+ });
+ }
+ }
+
private void createKey() {
CreateKeyActivity activity = (CreateKeyActivity) getActivity();
if (activity == null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java
index 7480367bb..3332b9cf9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java
@@ -18,13 +18,11 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
@@ -50,27 +48,6 @@ public class CreateKeyNameFragment extends Fragment {
return frag;
}
- /**
- * Checks if text of given EditText is not empty. If it is empty an error is
- * set and the EditText gets the focus.
- *
- * @param context
- * @param editText
- * @return true if EditText is not empty
- */
- private static boolean isEditTextNotEmpty(Context context, EditText editText) {
- boolean output = true;
- if (editText.getText().length() == 0) {
- editText.setError(context.getString(R.string.create_key_empty));
- editText.requestFocus();
- output = false;
- } else {
- editText.setError(null);
- }
-
- return output;
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_key_name_fragment, container, false);
@@ -109,13 +86,11 @@ public class CreateKeyNameFragment extends Fragment {
}
private void nextClicked() {
- if (isEditTextNotEmpty(getActivity(), mNameEdit)) {
- // save state
- mCreateKeyActivity.mName = mNameEdit.getText().toString();
+ // save state
+ mCreateKeyActivity.mName = mNameEdit.getText().length() == 0 ? null : mNameEdit.getText().toString();
- CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance();
- mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
- }
+ CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
index ea57fe558..6f35fdd38 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
@@ -25,7 +25,6 @@ import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -43,7 +42,7 @@ import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.SecurityTokenListenerFragment;
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -51,7 +50,7 @@ import org.sufficientlysecure.keychain.util.Preferences;
public class CreateSecurityTokenImportResetFragment
extends QueueingCryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
- implements NfcListenerFragment {
+ implements SecurityTokenListenerFragment {
private static final int REQUEST_CODE_RESET = 0x00005001;
@@ -231,7 +230,7 @@ public class CreateSecurityTokenImportResetFragment
public void resetCard() {
Intent intent = new Intent(getActivity(), SecurityTokenOperationActivity.class);
- RequiredInputParcel resetP = RequiredInputParcel.createNfcReset();
+ RequiredInputParcel resetP = RequiredInputParcel.createSecurityTokenReset();
intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, resetP);
intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, new CryptoInputParcel());
startActivityForResult(intent, REQUEST_CODE_RESET);
@@ -248,11 +247,11 @@ public class CreateSecurityTokenImportResetFragment
}
@Override
- public void doNfcInBackground() throws IOException {
+ public void doSecurityTokenInBackground() throws IOException {
- mTokenFingerprints = mCreateKeyActivity.nfcGetFingerprints();
- mTokenAid = mCreateKeyActivity.nfcGetAid();
- mTokenUserId = mCreateKeyActivity.nfcGetUserId();
+ mTokenFingerprints = mCreateKeyActivity.getSecurityTokenHelper().getFingerprints();
+ mTokenAid = mCreateKeyActivity.getSecurityTokenHelper().getAid();
+ mTokenUserId = mCreateKeyActivity.getSecurityTokenHelper().getUserId();
byte[] fp = new byte[20];
ByteBuffer.wrap(fp).put(mTokenFingerprints, 0, 20);
@@ -260,7 +259,7 @@ public class CreateSecurityTokenImportResetFragment
}
@Override
- public void onNfcPostExecute() {
+ public void onSecurityTokenPostExecute() {
setData();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
index a3ea38e40..5dc2c478b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.os.Bundle;
+import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -26,6 +27,7 @@ import android.view.ViewGroup;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenNfcActivity;
public class CreateSecurityTokenWaitFragment extends Fragment {
@@ -34,6 +36,15 @@ public class CreateSecurityTokenWaitFragment extends Fragment {
View mBackButton;
@Override
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (this.getActivity() instanceof BaseSecurityTokenNfcActivity) {
+ ((BaseSecurityTokenNfcActivity) this.getActivity()).checkDeviceConnection();
+ }
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_security_token_wait_fragment, container, false);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 4cbb4724e..9ed8e369d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -35,6 +35,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@@ -440,50 +441,45 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
}
break;
}
- case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_CARD: {
- // TODO: enable later when Admin PIN handling is resolved
- Notify.create(getActivity(),
- "This feature will be available in an upcoming OpenKeychain version.",
- Notify.Style.WARN).show();
- break;
+ case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN: {
+ SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
+ if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
+ secretKeyType == SecretKeyType.GNU_DUMMY) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_stripped, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ int algorithm = mSubkeysAdapter.getAlgorithm(position);
+ if (algorithm != PublicKeyAlgorithmTags.RSA_GENERAL
+ && algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT
+ && algorithm != PublicKeyAlgorithmTags.RSA_SIGN) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ if (mSubkeysAdapter.getKeySize(position) != 2048) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
+ .show();
+ break;
+ }
-// Activity activity = EditKeyFragment.this.getActivity();
-// SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
-// if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
-// secretKeyType == SecretKeyType.GNU_DUMMY) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_stripped, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// int algorithm = mSubkeysAdapter.getAlgorithm(position);
-// // these are the PGP constants for RSA_GENERAL, RSA_ENCRYPT and RSA_SIGN
-// if (algorithm != 1 && algorithm != 2 && algorithm != 3) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_algo, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// if (mSubkeysAdapter.getKeySize(position) != 2048) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_size, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-//
-//
-// SubkeyChange change;
-// change = mSaveKeyringParcel.getSubkeyChange(keyId);
-// if (change == null) {
-// mSaveKeyringParcel.mChangeSubKeys.add(
-// new SubkeyChange(keyId, false, true)
-// );
-// break;
-// }
-// // toggle
-// change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
-// if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
-// // User had chosen to strip key, but now wants to divert it.
-// change.mDummyStrip = false;
-// }
-// break;
+ SubkeyChange change;
+ change = mSaveKeyringParcel.getSubkeyChange(keyId);
+ if (change == null) {
+ mSaveKeyringParcel.mChangeSubKeys.add(
+ new SubkeyChange(keyId, false, true)
+ );
+ break;
+ }
+ // toggle
+ change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
+ if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
+ // User had chosen to strip key, but now wants to divert it.
+ change.mDummyStrip = false;
+ }
+ break;
}
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
index 22202a35f..d5c540856 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
@@ -729,6 +729,8 @@ public class EncryptFilesFragment
// make sure this is correct at this point
mAfterEncryptAction = AfterEncryptAction.SAVE;
cryptoOperation(new CryptoInputParcel(new Date()));
+ } else if (resultCode == Activity.RESULT_CANCELED) {
+ onCryptoOperationCancelled();
}
return;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
index b2b85ec14..51022094b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
@@ -21,6 +21,7 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.ViewAnimator;
import com.tokenautocomplete.TokenCompleteTextView;
@@ -109,6 +110,14 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment {
}
});
+ ImageView addRecipientImgView = (ImageView) view.findViewById(R.id.add_recipient);
+ addRecipientImgView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mEncryptKeyView.showAllKeys();
+ }
+ });
+
return view;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
index af60a1d9b..37e01e98f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
@@ -90,8 +90,9 @@ public class MainActivity extends BaseSecurityTokenNfcActivity implements FabCon
@Override
public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
if (drawerItem != null) {
+ PrimaryDrawerItem item = (PrimaryDrawerItem) drawerItem;
Intent intent = null;
- switch (drawerItem.getIdentifier()) {
+ switch ((int) item.getIdentifier()) {
case ID_KEYS:
onKeysSelected();
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RedirectImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RedirectImportKeysActivity.java
new file mode 100644
index 000000000..5cb680a57
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RedirectImportKeysActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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.ui;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
+
+public class RedirectImportKeysActivity extends BaseActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ startQrCodeCaptureActivity();
+ }
+
+ private void startQrCodeCaptureActivity() {
+ final Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class);
+ scanQrCode.setAction(ImportKeysProxyActivity.ACTION_QR_CODE_API);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.redirect_import_key_title)
+ .setMessage(R.string.redirect_import_key_message)
+ .setPositiveButton(R.string.redirect_import_key_yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // directly scan with OpenKeychain
+ startActivity(scanQrCode);
+ finish();
+ }
+ })
+ .setNegativeButton(R.string.redirect_import_key_no, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // close window
+ finish();
+ }
+ })
+ .show();
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
index d2ecce242..925ad19d4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
@@ -3,6 +3,7 @@
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2013-2014 Signe Rüsch
* Copyright (C) 2013-2014 Philipp Jakubeit
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
*
* 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
@@ -35,10 +36,12 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.securitytoken.KeyType;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenNfcActivity;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OrientationUtils;
@@ -69,8 +72,6 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
private RequiredInputParcel mRequiredInput;
- private static final byte[] BLANK_FINGERPRINT = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
private CryptoInputParcel mInputParcel;
@Override
@@ -137,9 +138,33 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
private void obtainPassphraseIfRequired() {
// obtain passphrase for this subkey
- if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD
- && mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_RESET_CARD) {
+ if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD
+ && mRequiredInput.mType != RequiredInputParcel.RequiredInputType.SECURITY_TOKEN_RESET_CARD) {
obtainSecurityTokenPin(mRequiredInput);
+ checkPinAvailability();
+ } else {
+ checkDeviceConnection();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (REQUEST_CODE_PIN == requestCode) {
+ checkPinAvailability();
+ }
+ }
+
+ private void checkPinAvailability() {
+ try {
+ Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
+ mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
+ if (passphrase != null) {
+ checkDeviceConnection();
+ }
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new AssertionError(
+ "tried to find passphrase for non-existing key. this is a programming error!");
}
}
@@ -149,39 +174,53 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- public void onNfcPreExecute() {
+ public void onSecurityTokenPreExecute() {
// start with indeterminate progress
vAnimator.setDisplayedChild(1);
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.TRANSFERRING);
}
@Override
- protected void doNfcInBackground() throws IOException {
+ protected void doSecurityTokenInBackground() throws IOException {
switch (mRequiredInput.mType) {
- case NFC_DECRYPT: {
+ case SECURITY_TOKEN_DECRYPT: {
+ long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
+ mSecurityTokenHelper.getKeyFingerprint(KeyType.ENCRYPT));
+
+ if (tokenKeyId != mRequiredInput.getSubKeyId()) {
+ throw new IOException(getString(R.string.error_wrong_security_token));
+ }
+
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] encryptedSessionKey = mRequiredInput.mInputData[i];
- byte[] decryptedSessionKey = nfcDecryptSessionKey(encryptedSessionKey);
+ byte[] decryptedSessionKey = mSecurityTokenHelper.decryptSessionKey(encryptedSessionKey);
mInputParcel.addCryptoData(encryptedSessionKey, decryptedSessionKey);
}
break;
}
- case NFC_SIGN: {
+ case SECURITY_TOKEN_SIGN: {
+ long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
+ mSecurityTokenHelper.getKeyFingerprint(KeyType.SIGN));
+
+ if (tokenKeyId != mRequiredInput.getSubKeyId()) {
+ throw new IOException(getString(R.string.error_wrong_security_token));
+ }
+
mInputParcel.addSignatureTime(mRequiredInput.mSignatureTime);
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] hash = mRequiredInput.mInputData[i];
int algo = mRequiredInput.mSignAlgos[i];
- byte[] signedHash = nfcCalculateSignature(hash, algo);
+ byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo);
mInputParcel.addCryptoData(hash, signedHash);
}
break;
}
- case NFC_MOVE_KEY_TO_CARD: {
+ case SECURITY_TOKEN_MOVE_KEY_TO_CARD: {
// TODO: assume PIN and Admin PIN to be default for this operation
- mPin = new Passphrase("123456");
- mAdminPin = new Passphrase("12345678");
+ mSecurityTokenHelper.setPin(new Passphrase("123456"));
+ mSecurityTokenHelper.setAdminPin(new Passphrase("12345678"));
ProviderHelper providerHelper = new ProviderHelper(this);
CanonicalizedSecretKeyRing secretKeyRing;
@@ -202,11 +241,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
long subkeyId = buf.getLong();
CanonicalizedSecretKey key = secretKeyRing.getSecretKey(subkeyId);
-
- long keyGenerationTimestampMillis = key.getCreationTime().getTime();
- long keyGenerationTimestamp = keyGenerationTimestampMillis / 1000;
- byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array();
- byte[] tokenSerialNumber = Arrays.copyOf(nfcGetAid(), 16);
+ byte[] tokenSerialNumber = Arrays.copyOf(mSecurityTokenHelper.getAid(), 16);
Passphrase passphrase;
try {
@@ -216,46 +251,20 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
throw new IOException("Unable to get cached passphrase!");
}
- if (key.canSign() || key.canCertify()) {
- if (shouldPutKey(key.getFingerprint(), 0)) {
- nfcPutKey(0xB6, key, passphrase);
- nfcPutData(0xCE, timestampBytes);
- nfcPutData(0xC7, key.getFingerprint());
- } else {
- throw new IOException("Key slot occupied; token must be reset to put new signature key.");
- }
- } else if (key.canEncrypt()) {
- if (shouldPutKey(key.getFingerprint(), 1)) {
- nfcPutKey(0xB8, key, passphrase);
- nfcPutData(0xCF, timestampBytes);
- nfcPutData(0xC8, key.getFingerprint());
- } else {
- throw new IOException("Key slot occupied; token must be reset to put new decryption key.");
- }
- } else if (key.canAuthenticate()) {
- if (shouldPutKey(key.getFingerprint(), 2)) {
- nfcPutKey(0xA4, key, passphrase);
- nfcPutData(0xD0, timestampBytes);
- nfcPutData(0xC9, key.getFingerprint());
- } else {
- throw new IOException("Key slot occupied; token must be reset to put new authentication key.");
- }
- } else {
- throw new IOException("Inappropriate key flags for Security Token key.");
- }
+ mSecurityTokenHelper.changeKey(key, passphrase);
// TODO: Is this really used anywhere?
mInputParcel.addCryptoData(subkeyBytes, tokenSerialNumber);
}
// change PINs afterwards
- nfcModifyPin(0x81, newPin);
- nfcModifyPin(0x83, newAdminPin);
+ mSecurityTokenHelper.modifyPin(0x81, newPin);
+ mSecurityTokenHelper.modifyPin(0x83, newAdminPin);
break;
}
- case NFC_RESET_CARD: {
- nfcReset();
+ case SECURITY_TOKEN_RESET_CARD: {
+ mSecurityTokenHelper.resetAndWipeToken();
break;
}
@@ -267,7 +276,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- protected final void onNfcPostExecute() {
+ protected final void onSecurityTokenPostExecute() {
handleResult(mInputParcel);
// show finish
@@ -275,28 +284,33 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE);
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- // check all 200ms if Security Token has been taken away
- while (true) {
- if (isNfcConnected()) {
- try {
- Thread.sleep(200);
- } catch (InterruptedException ignored) {
+ if (mSecurityTokenHelper.isPersistentConnectionAllowed()) {
+ // Just close
+ finish();
+ } else {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // check all 200ms if Security Token has been taken away
+ while (true) {
+ if (isSecurityTokenConnected()) {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignored) {
+ }
+ } else {
+ return null;
}
- } else {
- return null;
}
}
- }
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- finish();
- }
- }.execute();
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ finish();
+ }
+ }.execute();
+ }
}
/**
@@ -311,7 +325,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- protected void onNfcError(String error) {
+ protected void onSecurityTokenError(String error) {
pauseTagHandling();
vErrorText.setText(error + "\n\n" + getString(R.string.security_token_nfc_try_again_text));
@@ -321,31 +335,11 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
}
@Override
- public void onNfcPinError(String error) {
- onNfcError(error);
+ public void onSecurityTokenPinError(String error) {
+ onSecurityTokenError(error);
// clear (invalid) passphrase
PassphraseCacheService.clearCachedPassphrase(
this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
}
-
- private boolean shouldPutKey(byte[] fingerprint, int idx) throws IOException {
- byte[] tokenFingerprint = nfcGetMasterKeyFingerprint(idx);
-
- // Note: special case: This should not happen, but happens with
- // https://github.com/FluffyKaon/OpenPGP-Card, thus for now assume true
- if (tokenFingerprint == null) {
- return true;
- }
-
- // Slot is empty, or contains this key already. PUT KEY operation is safe
- if (Arrays.equals(tokenFingerprint, BLANK_FINGERPRINT) ||
- Arrays.equals(tokenFingerprint, fingerprint)) {
- return true;
- }
-
- // Slot already contains a different key; don't overwrite it.
- return false;
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java
new file mode 100644
index 000000000..05b30b1ae
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * 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.ui;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class UsbEventReceiverActivity extends Activity {
+ public static final String ACTION_USB_PERMISSION =
+ "org.sufficientlysecure.keychain.ui.USB_PERMISSION";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+ UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+
+ Log.d(Constants.TAG, "Requesting permission for " + usbDevice.getDeviceName());
+ usbManager.requestPermission(usbDevice,
+ PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0));
+ }
+ }
+
+ // Close the activity
+ finish();
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index d81797454..e47ca1db9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -32,7 +32,6 @@ import android.app.ActivityOptions;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.nfc.NfcAdapter;
@@ -172,9 +171,9 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
private byte[] mFingerprint;
private String mFingerprintString;
- private byte[] mNfcFingerprints;
- private String mNfcUserId;
- private byte[] mNfcAid;
+ private byte[] mSecurityTokenFingerprints;
+ private String mSecurityTokenUserId;
+ private byte[] mSecurityTokenAid;
@SuppressLint("InflateParams")
@Override
@@ -647,17 +646,17 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
}
@Override
- protected void doNfcInBackground() throws IOException {
+ protected void doSecurityTokenInBackground() throws IOException {
- mNfcFingerprints = nfcGetFingerprints();
- mNfcUserId = nfcGetUserId();
- mNfcAid = nfcGetAid();
+ mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
+ mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
}
@Override
- protected void onNfcPostExecute() {
+ protected void onSecurityTokenPostExecute() {
- long tokenId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
+ long tokenId = KeyFormattingUtils.getKeyIdFromFingerprint(mSecurityTokenFingerprints);
try {
@@ -668,7 +667,7 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
// if the master key of that key matches this one, just show the token dialog
if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprintString)) {
- showSecurityTokenFragment(mNfcFingerprints, mNfcUserId, mNfcAid);
+ showSecurityTokenFragment(mSecurityTokenFingerprints, mSecurityTokenUserId, mSecurityTokenAid);
return;
}
@@ -681,9 +680,9 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
Intent intent = new Intent(
ViewKeyActivity.this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
finish();
}
@@ -696,9 +695,9 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
public void onAction() {
Intent intent = new Intent(
ViewKeyActivity.this, CreateKeyActivity.class);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
finish();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
index fc6db1b92..93b38af9b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
@@ -39,6 +39,7 @@ import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ViewAnimator;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@@ -346,50 +347,45 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
}
break;
}
- case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_CARD: {
- // TODO: enable later when Admin PIN handling is resolved
- Notify.create(getActivity(),
- "This feature will be available in an upcoming OpenKeychain version.",
- Notify.Style.WARN).show();
- break;
+ case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN: {
+ SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
+ if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
+ secretKeyType == SecretKeyType.GNU_DUMMY) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_stripped, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ int algorithm = mSubkeysAdapter.getAlgorithm(position);
+ if (algorithm != PublicKeyAlgorithmTags.RSA_GENERAL
+ && algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT
+ && algorithm != PublicKeyAlgorithmTags.RSA_SIGN) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
+ .show();
+ break;
+ }
+
+ if (mSubkeysAdapter.getKeySize(position) != 2048) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
+ .show();
+ break;
+ }
-// Activity activity = EditKeyFragment.this.getActivity();
-// SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
-// if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
-// secretKeyType == SecretKeyType.GNU_DUMMY) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_stripped, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// int algorithm = mSubkeysAdapter.getAlgorithm(position);
-// // these are the PGP constants for RSA_GENERAL, RSA_ENCRYPT and RSA_SIGN
-// if (algorithm != 1 && algorithm != 2 && algorithm != 3) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_algo, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-// if (mSubkeysAdapter.getKeySize(position) != 2048) {
-// Notify.create(activity, R.string.edit_key_error_bad_nfc_size, Notify.Style.ERROR)
-// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
-// break;
-// }
-//
-//
-// SubkeyChange change;
-// change = mSaveKeyringParcel.getSubkeyChange(keyId);
-// if (change == null) {
-// mSaveKeyringParcel.mChangeSubKeys.add(
-// new SubkeyChange(keyId, false, true)
-// );
-// break;
-// }
-// // toggle
-// change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
-// if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
-// // User had chosen to strip key, but now wants to divert it.
-// change.mDummyStrip = false;
-// }
-// break;
+ SubkeyChange change;
+ change = mEditModeSaveKeyringParcel.getSubkeyChange(keyId);
+ if (change == null) {
+ mEditModeSaveKeyringParcel.mChangeSubKeys.add(
+ new SubkeyChange(keyId, false, true)
+ );
+ break;
+ }
+ // toggle
+ change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
+ if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
+ // User had chosen to strip key, but now wants to divert it.
+ change.mDummyStrip = false;
+ }
+ break;
}
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
index 77044cd2b..f4c0a9365 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java
@@ -3,6 +3,7 @@
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2013-2014 Signe Rüsch
* Copyright (C) 2013-2014 Philipp Jakubeit
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
*
* 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
@@ -20,41 +21,33 @@
package org.sufficientlysecure.keychain.ui.base;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.security.interfaces.RSAPrivateCrtKey;
-
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.os.AsyncTask;
import android.os.Bundle;
-import nordpol.Apdu;
-import nordpol.android.TagDispatcher;
-import nordpol.android.AndroidCard;
-import nordpol.android.OnDiscoveredTagListener;
-import nordpol.IsoCard;
-
-import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService.KeyNotFoundException;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.securitytoken.CardException;
+import org.sufficientlysecure.keychain.securitytoken.NfcTransport;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenHelper;
+import org.sufficientlysecure.keychain.securitytoken.Transport;
+import org.sufficientlysecure.keychain.util.UsbConnectionDispatcher;
+import org.sufficientlysecure.keychain.securitytoken.UsbTransport;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
@@ -63,56 +56,52 @@ import org.sufficientlysecure.keychain.ui.dialog.FidesmoPgpInstallDialog;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
-import org.sufficientlysecure.keychain.util.Iso7816TLV;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
-public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implements OnDiscoveredTagListener {
+import java.io.IOException;
+
+import nordpol.android.OnDiscoveredTagListener;
+import nordpol.android.TagDispatcher;
+
+public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
+ implements OnDiscoveredTagListener, UsbConnectionDispatcher.OnDiscoveredUsbDeviceListener {
public static final int REQUEST_CODE_PIN = 1;
public static final String EXTRA_TAG_HANDLING_ENABLED = "tag_handling_enabled";
- // Fidesmo constants
- private static final String FIDESMO_APPS_AID_PREFIX = "A000000617";
private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android";
- protected Passphrase mPin;
- protected Passphrase mAdminPin;
- protected boolean mPw1ValidForMultipleSignatures;
- protected boolean mPw1ValidatedForSignature;
- protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
- protected boolean mPw3Validated;
+ protected SecurityTokenHelper mSecurityTokenHelper = SecurityTokenHelper.getInstance();
protected TagDispatcher mTagDispatcher;
- private IsoCard mIsoCard;
+ protected UsbConnectionDispatcher mUsbDispatcher;
private boolean mTagHandlingEnabled;
- private static final int TIMEOUT = 100000;
-
- private byte[] mNfcFingerprints;
- private String mNfcUserId;
- private byte[] mNfcAid;
+ private byte[] mSecurityTokenFingerprints;
+ private String mSecurityTokenUserId;
+ private byte[] mSecurityTokenAid;
/**
- * Override to change UI before NFC handling (UI thread)
+ * Override to change UI before SecurityToken handling (UI thread)
*/
- protected void onNfcPreExecute() {
+ protected void onSecurityTokenPreExecute() {
}
/**
- * Override to implement NFC operations (background thread)
+ * Override to implement SecurityToken operations (background thread)
*/
- protected void doNfcInBackground() throws IOException {
- mNfcFingerprints = nfcGetFingerprints();
- mNfcUserId = nfcGetUserId();
- mNfcAid = nfcGetAid();
+ protected void doSecurityTokenInBackground() throws IOException {
+ mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
+ mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
}
/**
- * Override to handle result of NFC operations (UI thread)
+ * Override to handle result of SecurityToken operations (UI thread)
*/
- protected void onNfcPostExecute() {
+ protected void onSecurityTokenPostExecute() {
- final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
+ final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mSecurityTokenFingerprints);
try {
CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(
@@ -121,15 +110,15 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
Intent intent = new Intent(this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
} catch (PgpKeyNotFoundException e) {
Intent intent = new Intent(this, CreateKeyActivity.class);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_AID, mNfcAid);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
}
}
@@ -137,32 +126,49 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
/**
* Override to use something different than Notify (UI thread)
*/
- protected void onNfcError(String error) {
+ protected void onSecurityTokenError(String error) {
Notify.create(this, error, Style.WARN).show();
}
/**
* Override to do something when PIN is wrong, e.g., clear passphrases (UI thread)
*/
- protected void onNfcPinError(String error) {
- onNfcError(error);
+ protected void onSecurityTokenPinError(String error) {
+ onSecurityTokenError(error);
}
public void tagDiscovered(final Tag tag) {
// Actual NFC operations are executed in doInBackground to not block the UI thread
- if(!mTagHandlingEnabled)
+ if (!mTagHandlingEnabled)
+ return;
+
+ securityTokenDiscovered(new NfcTransport(tag));
+ }
+
+ public void usbDeviceDiscovered(final UsbDevice usbDevice) {
+ // Actual USB operations are executed in doInBackground to not block the UI thread
+ if (!mTagHandlingEnabled)
+ return;
+
+ UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ securityTokenDiscovered(new UsbTransport(usbDevice, usbManager));
+ }
+
+ public void securityTokenDiscovered(final Transport transport) {
+ // Actual Security Token operations are executed in doInBackground to not block the UI thread
+ if (!mTagHandlingEnabled)
return;
new AsyncTask<Void, Void, IOException>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
- onNfcPreExecute();
+ onSecurityTokenPreExecute();
}
@Override
protected IOException doInBackground(Void... params) {
try {
- handleTagDiscovered(tag);
+ handleSecurityToken(transport);
} catch (IOException e) {
return e;
}
@@ -175,11 +181,11 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
super.onPostExecute(exception);
if (exception != null) {
- handleNfcError(exception);
+ handleSecurityTokenError(exception);
return;
}
- onNfcPostExecute();
+ onSecurityTokenPostExecute();
}
}.execute();
}
@@ -197,6 +203,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
super.onCreate(savedInstanceState);
mTagDispatcher = TagDispatcher.get(this, this, false, false, true, false);
+ mUsbDispatcher = new UsbConnectionDispatcher(this, this);
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
@@ -230,15 +237,15 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
mTagDispatcher.interceptIntent(intent);
}
- private void handleNfcError(IOException e) {
+ private void handleSecurityTokenError(IOException e) {
if (e instanceof TagLostException) {
- onNfcError(getString(R.string.security_token_error_tag_lost));
+ onSecurityTokenError(getString(R.string.security_token_error_tag_lost));
return;
}
if (e instanceof IsoDepNotSupportedException) {
- onNfcError(getString(R.string.security_token_error_iso_dep_not_supported));
+ onSecurityTokenError(getString(R.string.security_token_error_iso_dep_not_supported));
return;
}
@@ -253,7 +260,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
if ((status & (short) 0xFFF0) == 0x63C0) {
int tries = status & 0x000F;
// hook to do something different when PIN is wrong
- onNfcPinError(getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries));
+ onSecurityTokenPinError(getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries));
return;
}
@@ -262,61 +269,61 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
// These errors should not occur in everyday use; if they are returned, it means we
// made a mistake sending data to the token, or the token is misbehaving.
case 0x6A80: {
- onNfcError(getString(R.string.security_token_error_bad_data));
+ onSecurityTokenError(getString(R.string.security_token_error_bad_data));
break;
}
case 0x6883: {
- onNfcError(getString(R.string.security_token_error_chaining_error));
+ onSecurityTokenError(getString(R.string.security_token_error_chaining_error));
break;
}
case 0x6B00: {
- onNfcError(getString(R.string.security_token_error_header, "P1/P2"));
+ onSecurityTokenError(getString(R.string.security_token_error_header, "P1/P2"));
break;
}
case 0x6D00: {
- onNfcError(getString(R.string.security_token_error_header, "INS"));
+ onSecurityTokenError(getString(R.string.security_token_error_header, "INS"));
break;
}
case 0x6E00: {
- onNfcError(getString(R.string.security_token_error_header, "CLA"));
+ onSecurityTokenError(getString(R.string.security_token_error_header, "CLA"));
break;
}
// These error conditions are more likely to be experienced by an end user.
case 0x6285: {
- onNfcError(getString(R.string.security_token_error_terminated));
+ onSecurityTokenError(getString(R.string.security_token_error_terminated));
break;
}
case 0x6700: {
- onNfcPinError(getString(R.string.security_token_error_wrong_length));
+ onSecurityTokenPinError(getString(R.string.security_token_error_wrong_length));
break;
}
case 0x6982: {
- onNfcError(getString(R.string.security_token_error_security_not_satisfied));
+ onSecurityTokenError(getString(R.string.security_token_error_security_not_satisfied));
break;
}
case 0x6983: {
- onNfcError(getString(R.string.security_token_error_authentication_blocked));
+ onSecurityTokenError(getString(R.string.security_token_error_authentication_blocked));
break;
}
case 0x6985: {
- onNfcError(getString(R.string.security_token_error_conditions_not_satisfied));
+ onSecurityTokenError(getString(R.string.security_token_error_conditions_not_satisfied));
break;
}
// 6A88 is "Not Found" in the spec, but Yubikey also returns 6A83 for this in some cases.
case 0x6A88:
case 0x6A83: {
- onNfcError(getString(R.string.security_token_error_data_not_found));
+ onSecurityTokenError(getString(R.string.security_token_error_data_not_found));
break;
}
// 6F00 is a JavaCard proprietary status code, SW_UNKNOWN, and usually represents an
// unhandled exception on the security token.
case 0x6F00: {
- onNfcError(getString(R.string.security_token_error_unknown));
+ onSecurityTokenError(getString(R.string.security_token_error_unknown));
break;
}
// 6A82 app not installed on security token!
case 0x6A82: {
- if (isFidesmoToken()) {
+ if (mSecurityTokenHelper.isFidesmoToken()) {
// Check if the Fidesmo app is installed
if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) {
promptFidesmoPgpInstall();
@@ -324,12 +331,12 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
promptFidesmoAppInstall();
}
} else { // Other (possibly) compatible hardware
- onNfcError(getString(R.string.security_token_error_pgp_app_not_installed));
+ onSecurityTokenError(getString(R.string.security_token_error_pgp_app_not_installed));
}
break;
}
default: {
- onNfcError(getString(R.string.security_token_error, e.getMessage()));
+ onSecurityTokenError(getString(R.string.security_token_error, e.getMessage()));
break;
}
}
@@ -363,7 +370,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
if (passphrase != null) {
- mPin = passphrase;
+ mSecurityTokenHelper.setPin(passphrase);
return;
}
@@ -371,7 +378,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
RequiredInputParcel.createRequiredPassphrase(requiredInput));
startActivityForResult(intent, REQUEST_CODE_PIN);
- } catch (KeyNotFoundException e) {
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
throw new AssertionError(
"tried to find passphrase for non-existing key. this is a programming error!");
}
@@ -388,7 +395,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
return;
}
CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
- mPin = input.getPassphrase();
+ mSecurityTokenHelper.setPin(input.getPassphrase());
break;
}
default:
@@ -396,639 +403,22 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
}
}
- /** Handle NFC communication and return a result.
- *
- * This method is called by onNewIntent above upon discovery of an NFC tag.
- * It handles initialization and login to the application, subsequently
- * calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then
- * finishes the activity with an appropriate result.
- *
- * On general communication, see also
- * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx
- *
- * References to pages are generally related to the OpenPGP Application
- * on ISO SmartCard Systems specification.
- *
- */
- protected void handleTagDiscovered(Tag tag) throws IOException {
-
- // Connect to the detected tag, setting a couple of settings
- mIsoCard = AndroidCard.get(tag);
- if (mIsoCard == null) {
- throw new IsoDepNotSupportedException("Tag does not support ISO-DEP (ISO 14443-4)");
- }
- mIsoCard.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation
- mIsoCard.connect();
-
- // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
- // See specification, page 51
- String accepted = "9000";
-
- // Command APDU (page 51) for SELECT FILE command (page 29)
- String opening =
- "00" // CLA
- + "A4" // INS
- + "04" // P1
- + "00" // P2
- + "06" // Lc (number of bytes)
- + "D27600012401" // Data (6 bytes)
- + "00"; // Le
- String response = nfcCommunicate(opening); // activate connection
- if ( ! response.endsWith(accepted) ) {
- throw new CardException("Initialization failed!", parseCardStatus(response));
- }
-
- byte[] pwStatusBytes = nfcGetPwStatusBytes();
- mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1);
- mPw1ValidatedForSignature = false;
- mPw1ValidatedForDecrypt = false;
- mPw3Validated = false;
-
- doNfcInBackground();
-
- }
-
- public boolean isNfcConnected() {
- return mIsoCard.isConnected();
- }
-
- /** Return the key id from application specific data stored on tag, or null
- * if it doesn't exist.
- *
- * @param idx Index of the key to return the fingerprint from.
- * @return The long key id of the requested key, or null if not found.
- */
- public Long nfcGetKeyId(int idx) throws IOException {
- byte[] fp = nfcGetMasterKeyFingerprint(idx);
- if (fp == null) {
- return null;
- }
- ByteBuffer buf = ByteBuffer.wrap(fp);
- // skip first 12 bytes of the fingerprint
- buf.position(12);
- // the last eight bytes are the key id (big endian, which is default order in ByteBuffer)
- return buf.getLong();
- }
-
- /** Return fingerprints of all keys from application specific data stored
- * on tag, or null if data not available.
- *
- * @return The fingerprints of all subkeys in a contiguous byte array.
- */
- public byte[] nfcGetFingerprints() throws IOException {
- String data = "00CA006E00";
- byte[] buf = mIsoCard.transceive(Hex.decode(data));
-
- Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true);
- Log.d(Constants.TAG, "nfcGetFingerprints() Iso7816TLV tlv data:\n" + tlv.prettyPrint());
-
- Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5);
- if (fptlv == null) {
- return null;
- }
-
- return fptlv.mV;
- }
-
- /** Return the PW Status Bytes from the token. 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 mIsoCard.transceive(Hex.decode(data));
- }
-
- /** Return the fingerprint from application specific data stored on tag, or
- * null if it doesn't exist.
- *
- * @param idx Index of the key to return the fingerprint from.
- * @return The fingerprint of the requested key, or null if not found.
- */
- public byte[] nfcGetMasterKeyFingerprint(int idx) throws IOException {
- byte[] data = nfcGetFingerprints();
- if (data == null) {
- return null;
- }
-
- // return the master key fingerprint
- ByteBuffer fpbuf = ByteBuffer.wrap(data);
- byte[] fp = new byte[20];
- fpbuf.position(idx * 20);
- fpbuf.get(fp, 0, 20);
-
- return fp;
- }
-
- public byte[] nfcGetAid() throws IOException {
- String info = "00CA004F00";
- return mIsoCard.transceive(Hex.decode(info));
- }
-
- public String nfcGetUserId() throws IOException {
- String info = "00CA006500";
- return getHolderName(nfcCommunicate(info));
- }
-
- /**
- * Call COMPUTE DIGITAL SIGNATURE command and returns the MPI value
- *
- * @param hash the hash for signing
- * @return a big integer representing the MPI for the given hash
- */
- public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException {
- if (!mPw1ValidatedForSignature) {
- nfcVerifyPin(0x81); // (Verify PW1 with mode 81 for signing)
- }
-
- // dsi, including Lc
- String dsi;
-
- Log.i(Constants.TAG, "Hash: " + hashAlgo);
- switch (hashAlgo) {
- case HashAlgorithmTags.SHA1:
- if (hash.length != 20) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 10!");
- }
- dsi = "23" // Lc
- + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes
- + "3009" // Tag/Length of Sequence, the 0x09 are the following header bytes
- + "0605" + "2B0E03021A" // OID of SHA1
- + "0500" // TLV coding of ZERO
- + "0414" + getHex(hash); // 0x14 are 20 hash bytes
- break;
- case HashAlgorithmTags.RIPEMD160:
- if (hash.length != 20) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 20!");
- }
- dsi = "233021300906052B2403020105000414" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA224:
- if (hash.length != 28) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 28!");
- }
- dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA256:
- if (hash.length != 32) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 32!");
- }
- dsi = "333031300D060960864801650304020105000420" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA384:
- if (hash.length != 48) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 48!");
- }
- dsi = "433041300D060960864801650304020205000430" + getHex(hash);
- break;
- case HashAlgorithmTags.SHA512:
- if (hash.length != 64) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 64!");
- }
- dsi = "533051300D060960864801650304020305000440" + getHex(hash);
- break;
- default:
- throw new IOException("Not supported hash algo!");
- }
-
- // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
- String apdu =
- "002A9E9A" // CLA, INS, P1, P2
- + dsi // digital signature input
- + "00"; // Le
-
- String response = nfcCommunicate(apdu);
-
- // split up response into signature and status
- String status = response.substring(response.length()-4);
- String signature = response.substring(0, response.length() - 4);
-
- // while we are getting 0x61 status codes, retrieve more data
- while (status.substring(0, 2).equals("61")) {
- Log.d(Constants.TAG, "requesting more data, status " + status);
- // Send GET RESPONSE command
- response = nfcCommunicate("00C00000" + status.substring(2));
- status = response.substring(response.length()-4);
- signature += response.substring(0, response.length()-4);
- }
-
- Log.d(Constants.TAG, "final response:" + status);
-
- if (!mPw1ValidForMultipleSignatures) {
- mPw1ValidatedForSignature = false;
- }
-
- if ( ! "9000".equals(status)) {
- throw new CardException("Bad NFC response code: " + status, parseCardStatus(response));
- }
-
- // Make sure the signature we received is actually the expected number of bytes long!
- if (signature.length() != 256 && signature.length() != 512) {
- throw new IOException("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2);
- }
-
- return Hex.decode(signature);
- }
-
- /**
- * Call DECIPHER command
- *
- * @param encryptedSessionKey the encoded session key
- * @return the decoded session key
- */
- public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException {
- if (!mPw1ValidatedForDecrypt) {
- nfcVerifyPin(0x82); // (Verify PW1 with mode 82 for decryption)
+ protected void handleSecurityToken(Transport transport) throws IOException {
+ // Don't reconnect if device was already connected
+ if (!(mSecurityTokenHelper.isPersistentConnectionAllowed()
+ && mSecurityTokenHelper.isConnected()
+ && mSecurityTokenHelper.getTransport().equals(transport))) {
+ mSecurityTokenHelper.setTransport(transport);
+ mSecurityTokenHelper.connectToDevice();
}
-
- String firstApdu = "102a8086fe";
- String secondApdu = "002a808603";
- String le = "00";
-
- byte[] one = new byte[254];
- // leave out first byte:
- System.arraycopy(encryptedSessionKey, 1, one, 0, one.length);
-
- byte[] two = new byte[encryptedSessionKey.length - 1 - one.length];
- for (int i = 0; i < two.length; i++) {
- two[i] = encryptedSessionKey[i + one.length + 1];
- }
-
- nfcCommunicate(firstApdu + getHex(one));
- String second = nfcCommunicate(secondApdu + getHex(two) + le);
-
- String decryptedSessionKey = getDataField(second);
-
- return Hex.decode(decryptedSessionKey);
+ doSecurityTokenInBackground();
}
- /** Verifies the user's PW1 or PW3 with the appropriate mode.
- *
- * @param mode For PW1, this is 0x81 for signing, 0x82 for everything else.
- * For PW3 (Admin PIN), mode is 0x83.
- */
- public void nfcVerifyPin(int mode) throws IOException {
- if (mPin != null || mode == 0x83) {
-
- byte[] pin;
- if (mode == 0x83) {
- pin = mAdminPin.toStringUnsafe().getBytes();
- } else {
- pin = mPin.toStringUnsafe().getBytes();
- }
-
- // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
- // See specification, page 51
- String accepted = "9000";
- String response = nfcTryPin(mode, pin); // login
- if (!response.equals(accepted)) {
- throw new CardException("Bad PIN!", parseCardStatus(response));
- }
-
- if (mode == 0x81) {
- mPw1ValidatedForSignature = true;
- } else if (mode == 0x82) {
- mPw1ValidatedForDecrypt = true;
- } else if (mode == 0x83) {
- mPw3Validated = true;
- }
- }
+ public boolean isSecurityTokenConnected() {
+ return mSecurityTokenHelper.isConnected();
}
- /**
- * Resets security token, which deletes all keys and data objects.
- * This works by entering a wrong PIN and then Admin PIN 4 times respectively.
- * Afterwards, the token is reactivated.
- */
- public void nfcReset() throws IOException {
- String accepted = "9000";
-
- // try wrong PIN 4 times until counter goes to C0
- byte[] pin = "XXXXXX".getBytes();
- for (int i = 0; i <= 4; i++) {
- String response = nfcTryPin(0x81, pin);
- if (response.equals(accepted)) { // Should NOT accept!
- throw new CardException("Should never happen, XXXXXX has been accepted!", parseCardStatus(response));
- }
- }
-
- // try wrong Admin PIN 4 times until counter goes to C0
- byte[] adminPin = "XXXXXXXX".getBytes();
- for (int i = 0; i <= 4; i++) {
- String response = nfcTryPin(0x83, adminPin);
- if (response.equals(accepted)) { // Should NOT accept!
- throw new CardException("Should never happen, XXXXXXXX has been accepted", parseCardStatus(response));
- }
- }
-
- // reactivate token!
- String reactivate1 = "00" + "e6" + "00" + "00";
- String reactivate2 = "00" + "44" + "00" + "00";
- String response1 = nfcCommunicate(reactivate1);
- String response2 = nfcCommunicate(reactivate2);
- if (!response1.equals(accepted) || !response2.equals(accepted)) {
- throw new CardException("Reactivating failed!", parseCardStatus(response1));
- }
-
- }
-
- private String nfcTryPin(int mode, byte[] pin) throws IOException {
- // Command APDU for VERIFY command (page 32)
- String login =
- "00" // CLA
- + "20" // INS
- + "00" // P1
- + String.format("%02x", mode) // P2
- + String.format("%02x", pin.length) // Lc
- + Hex.toHexString(pin);
-
- return nfcCommunicate(login);
- }
-
- /** Modifies the user's PW1 or PW3. Before sending, the new PIN will be validated for
- * conformance to the token's requirements for key length.
- *
- * @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83.
- * @param newPin The new PW1 or PW3.
- */
- public void nfcModifyPin(int pw, byte[] newPin) throws IOException {
- final int MAX_PW1_LENGTH_INDEX = 1;
- final int MAX_PW3_LENGTH_INDEX = 3;
-
- byte[] pwStatusBytes = nfcGetPwStatusBytes();
-
- if (pw == 0x81) {
- if (newPin.length < 6 || newPin.length > pwStatusBytes[MAX_PW1_LENGTH_INDEX]) {
- throw new IOException("Invalid PIN length");
- }
- } else if (pw == 0x83) {
- if (newPin.length < 8 || newPin.length > pwStatusBytes[MAX_PW3_LENGTH_INDEX]) {
- throw new IOException("Invalid PIN length");
- }
- } else {
- throw new IOException("Invalid PW index for modify PIN operation");
- }
-
- byte[] pin;
- if (pw == 0x83) {
- pin = mAdminPin.toStringUnsafe().getBytes();
- } else {
- pin = mPin.toStringUnsafe().getBytes();
- }
-
- // Command APDU for CHANGE REFERENCE DATA command (page 32)
- String changeReferenceDataApdu = "00" // CLA
- + "24" // INS
- + "00" // P1
- + String.format("%02x", pw) // P2
- + String.format("%02x", pin.length + newPin.length) // Lc
- + getHex(pin)
- + getHex(newPin);
- String response = nfcCommunicate(changeReferenceDataApdu); // change PIN
- if (!response.equals("9000")) {
- throw new CardException("Failed to change PIN", parseCardStatus(response));
- }
- }
-
- /**
- * Stores a data object on the token. Automatically validates the proper PIN for the operation.
- * Supported for all data objects < 255 bytes in length. Only the cardholder certificate
- * (0x7F21) can exceed this length.
- *
- * @param dataObject The data object to be stored.
- * @param data The data to store in the object
- */
- public void nfcPutData(int dataObject, byte[] data) throws IOException {
- if (data.length > 254) {
- throw new IOException("Cannot PUT DATA with length > 254");
- }
- if (dataObject == 0x0101 || dataObject == 0x0103) {
- if (!mPw1ValidatedForDecrypt) {
- nfcVerifyPin(0x82); // (Verify PW1 for non-signing operations)
- }
- } else if (!mPw3Validated) {
- nfcVerifyPin(0x83); // (Verify PW3)
- }
-
- String putDataApdu = "00" // CLA
- + "DA" // INS
- + String.format("%02x", (dataObject & 0xFF00) >> 8) // P1
- + String.format("%02x", dataObject & 0xFF) // P2
- + String.format("%02x", data.length) // Lc
- + getHex(data);
-
- String response = nfcCommunicate(putDataApdu); // put data
- if (!response.equals("9000")) {
- throw new CardException("Failed to put data.", parseCardStatus(response));
- }
- }
-
- /**
- * Puts a key on the token in the given slot.
- *
- * @param slot The slot on the token where the key should be stored:
- * 0xB6: Signature Key
- * 0xB8: Decipherment Key
- * 0xA4: Authentication Key
- */
- public void nfcPutKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
- throws IOException {
- if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
- throw new IOException("Invalid key slot");
- }
-
- RSAPrivateCrtKey crtSecretKey;
- try {
- secretKey.unlock(passphrase);
- crtSecretKey = secretKey.getCrtSecretKey();
- } catch (PgpGeneralException e) {
- throw new IOException(e.getMessage());
- }
-
- // Shouldn't happen; the UI should block the user from getting an incompatible key this far.
- if (crtSecretKey.getModulus().bitLength() > 2048) {
- throw new IOException("Key too large to export to Security Token.");
- }
-
- // Should happen only rarely; all GnuPG keys since 2006 use public exponent 65537.
- if (!crtSecretKey.getPublicExponent().equals(new BigInteger("65537"))) {
- throw new IOException("Invalid public exponent for smart Security Token.");
- }
-
- if (!mPw3Validated) {
- nfcVerifyPin(0x83); // (Verify PW3 with mode 83)
- }
-
- byte[] header= Hex.decode(
- "4D82" + "03A2" // Extended header list 4D82, length of 930 bytes. (page 23)
- + String.format("%02x", slot) + "00" // CRT to indicate targeted key, no length
- + "7F48" + "15" // Private key template 0x7F48, length 21 (decimal, 0x15 hex)
- + "9103" // Public modulus, length 3
- + "928180" // Prime P, length 128
- + "938180" // Prime Q, length 128
- + "948180" // Coefficient (1/q mod p), length 128
- + "958180" // Prime exponent P (d mod (p - 1)), length 128
- + "968180" // Prime exponent Q (d mod (1 - 1)), length 128
- + "97820100" // Modulus, length 256, last item in private key template
- + "5F48" + "820383");// DO 5F48; 899 bytes of concatenated key data will follow
- byte[] dataToSend = new byte[934];
- byte[] currentKeyObject;
- int offset = 0;
-
- System.arraycopy(header, 0, dataToSend, offset, header.length);
- offset += header.length;
- currentKeyObject = crtSecretKey.getPublicExponent().toByteArray();
- System.arraycopy(currentKeyObject, 0, dataToSend, offset, 3);
- offset += 3;
- // NOTE: For a 2048-bit key, these lengths are fixed. However, bigint includes a leading 0
- // in the array to represent sign, so we take care to set the offset to 1 if necessary.
- currentKeyObject = crtSecretKey.getPrimeP().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getPrimeQ().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getCrtCoefficient().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getPrimeExponentP().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getPrimeExponentQ().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
- Arrays.fill(currentKeyObject, (byte)0);
- offset += 128;
- currentKeyObject = crtSecretKey.getModulus().toByteArray();
- System.arraycopy(currentKeyObject, currentKeyObject.length - 256, dataToSend, offset, 256);
-
- String putKeyCommand = "10DB3FFF";
- String lastPutKeyCommand = "00DB3FFF";
-
- // Now we're ready to communicate with the token.
- offset = 0;
- String response;
- while(offset < dataToSend.length) {
- int dataRemaining = dataToSend.length - offset;
- if (dataRemaining > 254) {
- response = nfcCommunicate(
- putKeyCommand + "FE" + Hex.toHexString(dataToSend, offset, 254)
- );
- offset += 254;
- } else {
- int length = dataToSend.length - offset;
- response = nfcCommunicate(
- lastPutKeyCommand + String.format("%02x", length)
- + Hex.toHexString(dataToSend, offset, length));
- offset += length;
- }
-
- if (!response.endsWith("9000")) {
- throw new CardException("Key export to Security Token failed", parseCardStatus(response));
- }
- }
-
- // Clear array with secret data before we return.
- Arrays.fill(dataToSend, (byte) 0);
- }
-
- /**
- * Generates a key on the card in the given slot. If the slot is 0xB6 (the signature key),
- * this command also has the effect of resetting the digital signature counter.
- * NOTE: This does not set the key fingerprint data object! After calling this command, you
- * must construct a public key packet using the returned public key data objects, compute the
- * key fingerprint, and store it on the card using: nfcPutData(0xC8, key.getFingerprint())
- *
- * @param slot The slot on the card where the key should be generated:
- * 0xB6: Signature Key
- * 0xB8: Decipherment Key
- * 0xA4: Authentication Key
- * @return the public key data objects, in TLV format. For RSA this will be the public modulus
- * (0x81) and exponent (0x82). These may come out of order; proper TLV parsing is required.
- */
- public byte[] nfcGenerateKey(int slot) throws IOException {
- if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
- throw new IOException("Invalid key slot");
- }
-
- if (!mPw3Validated) {
- nfcVerifyPin(0x83); // (Verify PW3 with mode 83)
- }
-
- String generateKeyApdu = "0047800002" + String.format("%02x", slot) + "0000";
- String getResponseApdu = "00C00000";
-
- String first = nfcCommunicate(generateKeyApdu);
- String second = nfcCommunicate(getResponseApdu);
-
- if (!second.endsWith("9000")) {
- throw new IOException("On-card key generation failed");
- }
-
- String publicKeyData = getDataField(first) + getDataField(second);
-
- Log.d(Constants.TAG, "Public Key Data Objects: " + publicKeyData);
-
- return Hex.decode(publicKeyData);
- }
-
- /**
- * Transceive data via NFC encoded as Hex
- */
- public String nfcCommunicate(String apdu) throws IOException {
- return getHex(mIsoCard.transceive(Hex.decode(apdu)));
- }
-
- /**
- * Parses out the status word from a JavaCard response string.
- *
- * @param response A hex string with the response from the token
- * @return A short indicating the SW1/SW2, or 0 if a status could not be determined.
- */
- short parseCardStatus(String response) {
- if (response.length() < 4) {
- return 0; // invalid input
- }
-
- try {
- return Short.parseShort(response.substring(response.length() - 4), 16);
- } catch (NumberFormatException e) {
- return 0;
- }
- }
-
- public String getHolderName(String name) {
- try {
- String slength;
- int ilength;
- name = name.substring(6);
- slength = name.substring(0, 2);
- ilength = Integer.parseInt(slength, 16) * 2;
- name = name.substring(2, ilength + 2);
- name = (new String(Hex.decode(name))).replace('<', ' ');
- return name;
- } catch (IndexOutOfBoundsException e) {
- // try-catch for https://github.com/FluffyKaon/OpenPGP-Card
- // Note: This should not happen, but happens with
- // https://github.com/FluffyKaon/OpenPGP-Card, thus return an empty string for now!
-
- Log.e(Constants.TAG, "Couldn't get holder name, returning empty string!", e);
- return "";
- }
- }
-
- private String getDataField(String output) {
- return output.substring(0, output.length() - 4);
- }
-
- public static String getHex(byte[] raw) {
- return new String(Hex.encode(raw));
- }
-
- public class IsoDepNotSupportedException extends IOException {
+ public static class IsoDepNotSupportedException extends IOException {
public IsoDepNotSupportedException(String detailMessage) {
super(detailMessage);
@@ -1036,38 +426,9 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
}
- public class CardException extends IOException {
- private short mResponseCode;
-
- public CardException(String detailMessage, short responseCode) {
- super(detailMessage);
- mResponseCode = responseCode;
- }
-
- public short getResponseCode() {
- return mResponseCode;
- }
-
- }
-
- private boolean isFidesmoToken() {
- if (isNfcConnected()) { // Check if we can still talk to the card
- try {
- // By trying to select any apps that have the Fidesmo AID prefix we can
- // see if it is a Fidesmo device or not
- byte[] mSelectResponse = mIsoCard.transceive(Apdu.select(FIDESMO_APPS_AID_PREFIX));
- // Compare the status returned by our select with the OK status code
- return Apdu.hasStatus(mSelectResponse, Apdu.OK_APDU);
- } catch (IOException e) {
- Log.e(Constants.TAG, "Card communication failed!", e);
- }
- }
- return false;
- }
-
/**
* Ask user if she wants to install PGP onto her Fidesmo token
- */
+ */
private void promptFidesmoPgpInstall() {
FidesmoPgpInstallDialog fidesmoPgpInstallDialog = new FidesmoPgpInstallDialog();
fidesmoPgpInstallDialog.show(getSupportFragmentManager(), "fidesmoPgpInstallDialog");
@@ -1084,6 +445,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
/**
* Use the package manager to detect if an application is installed on the phone
+ *
* @param uri an URI identifying the application's package
* @return 'true' if the app is installed
*/
@@ -1099,4 +461,28 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
}
return mAppInstalled;
}
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mUsbDispatcher.onStop();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mUsbDispatcher.onStart();
+ }
+
+ public SecurityTokenHelper getSecurityTokenHelper() {
+ return mSecurityTokenHelper;
+ }
+
+ /**
+ * Run Security Token routines if last used token is connected and supports
+ * persistent connections
+ */
+ public void checkDeviceConnection() {
+ mUsbDispatcher.rescanDevices();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
index 451065d6b..ad15c8f68 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
@@ -130,9 +130,9 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
switch (requiredInput.mType) {
// always use CryptoOperationHelper.startActivityForResult!
- case NFC_MOVE_KEY_TO_CARD:
- case NFC_DECRYPT:
- case NFC_SIGN: {
+ case SECURITY_TOKEN_MOVE_KEY_TO_CARD:
+ case SECURITY_TOKEN_DECRYPT:
+ case SECURITY_TOKEN_SIGN: {
Intent intent = new Intent(activity, SecurityTokenOperationActivity.class);
intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
index 9f5ffc358..0c0877bae 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
@@ -35,7 +35,7 @@ public class EditSubkeyDialogFragment extends DialogFragment {
public static final int MESSAGE_CHANGE_EXPIRY = 1;
public static final int MESSAGE_REVOKE = 2;
public static final int MESSAGE_STRIP = 3;
- public static final int MESSAGE_MOVE_KEY_TO_CARD = 4;
+ public static final int MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN = 4;
public static final int SUBKEY_MENU_CHANGE_EXPIRY = 0;
public static final int SUBKEY_MENU_REVOKE_SUBKEY = 1;
public static final int SUBKEY_MENU_STRIP_SUBKEY = 2;
@@ -82,7 +82,7 @@ public class EditSubkeyDialogFragment extends DialogFragment {
showAlertDialog();
break;
case SUBKEY_MENU_MOVE_TO_SECURITY_TOKEN:
- sendMessageToHandler(MESSAGE_MOVE_KEY_TO_CARD, null);
+ sendMessageToHandler(MESSAGE_MOVE_KEY_TO_SECURITY_TOKEN, null);
break;
default:
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
index 5fcc3d58b..4dc0ebaa0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
@@ -64,8 +64,8 @@ public class KeyFormattingUtils {
String algorithmStr;
switch (algorithm) {
- case PublicKeyAlgorithmTags.RSA_ENCRYPT:
case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
case PublicKeyAlgorithmTags.RSA_SIGN: {
algorithmStr = "RSA";
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
index 49b37692c..55d5aec0c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
@@ -23,15 +23,11 @@ import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
-import android.util.Patterns;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
-import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.ContactHelper;
-import java.util.regex.Matcher;
-
public class EmailEditText extends AppCompatAutoCompleteTextView {
public EmailEditText(Context context) {
@@ -70,20 +66,7 @@ public class EmailEditText extends AppCompatAutoCompleteTextView {
@Override
public void afterTextChanged(Editable editable) {
- String email = editable.toString();
- if (email.length() > 0) {
- Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
- if (emailMatcher.matches()) {
- EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.ic_stat_retyped_ok, 0);
- } else {
- EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.ic_stat_retyped_bad, 0);
- }
- } else {
- // remove drawable if email is empty
- EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
+
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
index 78f9f431b..4074d391b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
@@ -177,4 +177,22 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView<KeyItem>
mLoaderManager.restartLoader(0, args, this);
}
+ @Override
+ public boolean enoughToFilter() {
+ return true;
+ }
+
+ public void showAllKeys(){
+ Bundle args = new Bundle();
+ args.putString(ARG_QUERY, "");
+ mLoaderManager.restartLoader(0, args, this);
+ super.showDropDown();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ // increase width to include add button
+ this.setDropDownWidth(this.getRight());
+ }
}