diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
28 files changed, 589 insertions, 451 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java index 507d4dea5..e19757d65 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java @@ -31,7 +31,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp  import org.sufficientlysecure.keychain.operations.results.SingletonResult;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.remote.AccountSettings; -import org.sufficientlysecure.keychain.ui.BaseActivity; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.util.Log;  public class AccountSettingsActivity extends BaseActivity { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index 407480c98..2b71d6dc1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java @@ -40,9 +40,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.BaseActivity; -import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.dialog.AdvancedAppSettingsDialogFragment;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index e8c3e4511..f312c0d44 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.remote.AccountSettings;  import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.ui.BaseActivity; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java index 98a44466d..cb9f46f7f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java @@ -29,7 +29,7 @@ import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.operations.results.OperationResult;  import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.ui.BaseActivity; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.CreateKeyActivity;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java index b7c80c1ed..016ab5f3c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java @@ -23,6 +23,7 @@ import android.view.View;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.util.Log;  public class CertifyFingerprintActivity extends BaseActivity { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index 1fb88b182..3845e07cb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -19,6 +19,8 @@  package org.sufficientlysecure.keychain.ui;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; +  /**   * Signs the specified public key with the specified secret master key 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 ab76f693e..4ae901c6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -17,12 +17,18 @@  package org.sufficientlysecure.keychain.ui; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter;  import android.os.Bundle;  import android.support.v4.app.Fragment;  import android.support.v4.app.FragmentTransaction; -import android.view.View; +import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; +import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Passphrase;  import java.util.ArrayList; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java index 180a52a1c..d23d598fa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java @@ -78,7 +78,7 @@ public class CreateKeyStartFragment extends Fragment {          mCreateKey = view.findViewById(R.id.create_key_create_key_button);          mImportKey = view.findViewById(R.id.create_key_import_button); -//        mYubiKey = view.findViewById(R.id.create_key_yubikey_button); +        mYubiKey = view.findViewById(R.id.create_key_yubikey_button);          mCancel = (TextView) view.findViewById(R.id.create_key_cancel);          if (mCreateKeyActivity.mFirstTime) { @@ -95,6 +95,14 @@ public class CreateKeyStartFragment extends Fragment {              }          }); +        mYubiKey.setOnClickListener(new View.OnClickListener() { +            @Override +            public void onClick(View v) { +                CreateKeyYubiFragment frag = new CreateKeyYubiFragment(); +                mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); +            } +        }); +          mImportKey.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiFragment.java new file mode 100644 index 000000000..665c68d65 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiFragment.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 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.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; +import org.sufficientlysecure.keychain.ui.widget.NameEditText; + + +public class CreateKeyYubiFragment extends Fragment { + +    CreateKeyActivity mCreateKeyActivity; +    NameEditText mNameEdit; +    View mBackButton; +    View mNextButton; + +    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_yubikey_fragment, container, false); + +        mBackButton.setOnClickListener(new View.OnClickListener() { +            @Override +            public void onClick(View v) { +                mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); +            } +        }); +        mNextButton.setOnClickListener(new View.OnClickListener() { +            @Override +            public void onClick(View v) { +                nextClicked(); +            } +        }); + +        return view; +    } + +    @Override +    public void onAttach(Activity activity) { +        super.onAttach(activity); +        mCreateKeyActivity = (CreateKeyActivity) getActivity(); +    } + +    private void nextClicked() { +        if (isEditTextNotEmpty(getActivity(), mNameEdit)) { +            // save state +            mCreateKeyActivity.mName = mNameEdit.getText().toString(); + +            CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance(); +            mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); +        } +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java index 162b10eca..dce2386b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java @@ -21,12 +21,12 @@ import android.app.Activity;  import android.content.Intent;  import android.net.Uri;  import android.os.Bundle; -import android.os.PersistableBundle;  import android.view.View;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.api.OpenKeychainIntents; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.util.Log;  public class DecryptFilesActivity extends BaseActivity { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java index bc2ec014a..728e3ba41 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java @@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;  import org.sufficientlysecure.keychain.operations.results.OperationResult;  import org.sufficientlysecure.keychain.operations.results.SingletonResult;  import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 6dc2994cf..b607ba9f4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -23,6 +23,7 @@ import android.os.Bundle;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.util.Log;  public class EditKeyActivity extends BaseActivity { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index b87dec8fc..5254af19f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.util.Passphrase; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java index cd6cdf4d6..4aa706f57 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java @@ -26,6 +26,8 @@ import com.astuetz.PagerSlidingTabStrip;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; +  public class HelpActivity extends BaseActivity {      public static final String EXTRA_SELECTED_TAB = "selected_tab"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 9143609ad..9a7c405d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;  import org.sufficientlysecure.keychain.operations.results.OperationResult;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index 0de7bb391..df325d31d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -22,6 +22,8 @@ import android.os.Bundle;  import android.view.View;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; +  public class LogDisplayActivity extends BaseActivity { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java index 7311f4879..57acf3e93 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java @@ -22,6 +22,7 @@ import org.spongycastle.bcpg.HashAlgorithmTags;  import org.spongycastle.util.encoders.Hex;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.util.Iso7816TLV;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java index 0ccb206d1..a1affbc39 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java @@ -21,6 +21,7 @@ import android.widget.Toast;  import org.spongycastle.util.encoders.Hex;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import org.sufficientlysecure.keychain.util.Iso7816TLV;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java index 68eee2513..549d9ece7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -7,31 +7,19 @@  package org.sufficientlysecure.keychain.ui;  import android.annotation.TargetApi; -import android.app.PendingIntent;  import android.content.Intent; -import android.content.IntentFilter; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep;  import android.os.Build;  import android.os.Bundle;  import android.view.WindowManager; -import android.widget.Toast; -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.util.encoders.Hex;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; -import org.sufficientlysecure.keychain.util.Iso7816TLV; +import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;  import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Passphrase; -import org.sufficientlysecure.keychain.util.Preferences;  import java.io.IOException; -import java.nio.ByteBuffer; -  /**   * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant @@ -40,21 +28,13 @@ import java.nio.ByteBuffer;   * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf   */  @TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) -public class NfcOperationActivity extends BaseActivity { - -    public static final int REQUEST_CODE_PASSPHRASE = 1; +public class NfcOperationActivity extends BaseNfcActivity {      public static final String EXTRA_REQUIRED_INPUT = "required_input";      public static final String RESULT_DATA = "result_data"; -    private static final int TIMEOUT = 100000; - -    private NfcAdapter mNfcAdapter; -    private IsoDep mIsoDep; -      RequiredInputParcel mRequiredInput; -    private Passphrase mPin;      @Override      protected void onCreate(Bundle savedInstanceState) { @@ -64,45 +44,12 @@ public class NfcOperationActivity extends BaseActivity {          getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);          Intent intent = getIntent(); -        String action = intent.getAction(); -        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { -            throw new AssertionError("should not happen: NfcOperationActivity.onCreate is called instead of onNewIntent!"); -        } -          Bundle data = intent.getExtras();          mRequiredInput = data.getParcelable(EXTRA_REQUIRED_INPUT); -        obtainPassphrase(); - -    } - -    private void obtainPassphrase() { - -        Preferences prefs = Preferences.getPreferences(this); -        if (prefs.useDefaultYubikeyPin()) { -            mPin = new Passphrase("123456"); -            return; -        } - -        Intent intent = new Intent(this, PassphraseDialogActivity.class); -        intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, -                RequiredInputParcel.createRequiredPassphrase(mRequiredInput)); -        startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - -    } - -    @Override -    protected void onActivityResult(int requestCode, int resultCode, Intent data) { -        switch (requestCode) { -            case REQUEST_CODE_PASSPHRASE: -                CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_DATA); -                mPin = input.getPassphrase(); -                break; - -            default: -                super.onActivityResult(requestCode, resultCode, data); -        } +        // obtain passphrase for this subkey +        obtainYubikeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput));      }      @Override @@ -110,104 +57,8 @@ public class NfcOperationActivity extends BaseActivity {          setContentView(R.layout.nfc_activity);      } -    /** -     * Called when the system is about to start resuming a previous activity, -     * disables NFC Foreground Dispatch -     */ -    public void onPause() { -        super.onPause(); -        Log.d(Constants.TAG, "NfcOperationActivity.onPause"); - -        disableNfcForegroundDispatch(); -    } - -    /** -     * Called when the activity will start interacting with the user, -     * enables NFC Foreground Dispatch -     */ -    public void onResume() { -        super.onResume(); -        Log.d(Constants.TAG, "NfcOperationActivity.onResume"); - -        enableNfcForegroundDispatch(); -    } - -    /** -     * This activity is started as a singleTop activity. -     * All new NFC Intents which are delivered to this activity are handled here -     */ -    public void onNewIntent(Intent intent) { -        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { -            try { -                handleNdefDiscoveredIntent(intent); -            } catch (IOException e) { -                Log.e(Constants.TAG, "Connection error!", e); -                toast("Connection Error: " + e.getMessage()); -                setResult(RESULT_CANCELED); -                finish(); -            } -        } -    } - -    /** 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 appropiate 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. -     * -     */ -    private void handleNdefDiscoveredIntent(Intent intent) throws IOException { - -        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - -        // Connect to the detected tag, setting a couple of settings -        mIsoDep = IsoDep.get(detectedTag); -        mIsoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation -        mIsoDep.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 -        if ( ! card(opening).equals(accepted)) { // activate connection -            toast("Opening Error!"); -            setResult(RESULT_CANCELED); -            finish(); -            return; -        } - -        byte[] pin = new String(mPin.getCharArray()).getBytes(); - -        // Command APDU for VERIFY command (page 32) -        String login = -                "00" // CLA -                        + "20" // INS -                        + "00" // P1 -                        + "82" // P2 (PW1) -                        + String.format("%02x", pin.length) // Lc -                        + Hex.toHexString(pin); -        if ( ! card(login).equals(accepted)) { // login -            toast("Wrong PIN!"); -            setResult(RESULT_CANCELED); -            finish(); -            return; -        } +    @Override +    protected void onNfcPerform() throws IOException {          CryptoInputParcel resultData = new CryptoInputParcel(mRequiredInput.mSignatureTime); @@ -233,292 +84,9 @@ public class NfcOperationActivity extends BaseActivity {          // give data through for new service call          Intent result = new Intent(); -        result.putExtra(RESULT_DATA, resultData); +        result.putExtra(NfcOperationActivity.RESULT_DATA, resultData);          setResult(RESULT_OK, result);          finish();      } - -    /** -     * Gets the user ID -     * -     * @return the user id as "name <email>" -     * @throws java.io.IOException -     */ -    public String getUserId() throws IOException { -        String info = "00CA006500"; -        String data = "00CA005E00"; -        return getName(card(info)) + " <" + (new String(Hex.decode(getDataField(card(data))))) + ">"; -    } - -    /** 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 static Long nfcGetKeyId(IsoDep isoDep, int idx) throws IOException { -        byte[] fp = nfcGetFingerprint(isoDep, 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 static byte[] nfcGetFingerprints(IsoDep isoDep) throws IOException { -        String data = "00CA006E00"; -        byte[] buf = isoDep.transceive(Hex.decode(data)); - -        Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); -        Log.d(Constants.TAG, "nfc tlv data:\n" + tlv.prettyPrint()); - -        Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5); -        if (fptlv == null) { -            return null; -        } - -        return fptlv.mV; -    } - -    /** 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 static byte[] nfcGetFingerprint(IsoDep isoDep, int idx) throws IOException { -        byte[] data = nfcGetFingerprints(isoDep); - -        // 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; -    } - -    /** -     * Calls to calculate the signature and returns the MPI value -     * -     * @param hash the hash for signing -     * @return a big integer representing the MPI for the given hash -     * @throws java.io.IOException -     */ -    public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException { - -        // dsi, including Lc -        String dsi; - -        Log.i(Constants.TAG, "Hash: " + hashAlgo); -        switch (hashAlgo) { -            case HashAlgorithmTags.SHA1: -                if (hash.length != 20) { -                    throw new RuntimeException("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 RuntimeException("Bad hash length (" + hash.length + ", expected 20!"); -                } -                dsi = "233021300906052B2403020105000414" + getHex(hash); -                break; -            case HashAlgorithmTags.SHA224: -                if (hash.length != 28) { -                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 28!"); -                } -                dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash); -                break; -            case HashAlgorithmTags.SHA256: -                if (hash.length != 32) { -                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 32!"); -                } -                dsi = "333031300D060960864801650304020105000420" + getHex(hash); -                break; -            case HashAlgorithmTags.SHA384: -                if (hash.length != 48) { -                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 48!"); -                } -                dsi = "433041300D060960864801650304020205000430" + getHex(hash); -                break; -            case HashAlgorithmTags.SHA512: -                if (hash.length != 64) { -                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 64!"); -                } -                dsi = "533051300D060960864801650304020305000440" + getHex(hash); -                break; -            default: -                throw new RuntimeException("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 = card(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 = card("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 ( ! status.equals("9000")) { -            toast("Bad NFC response code: " + status); -            return null; -        } - -        // Make sure the signature we received is actually the expected number of bytes long! -        if (signature.length() != 256 && signature.length() != 512) { -            toast("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); -            return null; -        } - -        return Hex.decode(signature); -    } - -    /** -     * Calls to calculate the signature and returns the MPI value -     * -     * @param encryptedSessionKey the encoded session key -     * @return the decoded session key -     * @throws java.io.IOException -     */ -    public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException { -        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]; -        } - -        String first = card(firstApdu + getHex(one)); -        String second = card(secondApdu + getHex(two) + le); - -        String decryptedSessionKey = getDataField(second); - -        Log.d(Constants.TAG, "decryptedSessionKey: " + decryptedSessionKey); - -        return Hex.decode(decryptedSessionKey); -    } - -    /** -     * Prints a message to the screen -     * -     * @param text the text which should be contained within the toast -     */ -    private void toast(String text) { -        Toast.makeText(this, text, Toast.LENGTH_LONG).show(); -    } - -    /** -     * Receive new NFC Intents to this activity only by enabling foreground dispatch. -     * This can only be done in onResume! -     */ -    public void enableNfcForegroundDispatch() { -        mNfcAdapter = NfcAdapter.getDefaultAdapter(this); -        Intent nfcI = new Intent(this, NfcOperationActivity.class) -                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); -        PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, PendingIntent.FLAG_CANCEL_CURRENT); -        IntentFilter[] writeTagFilters = new IntentFilter[]{ -                new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) -        }; - -        // https://code.google.com/p/android/issues/detail?id=62918 -        // maybe mNfcAdapter.enableReaderMode(); ? -        try { -            mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null); -        } catch (IllegalStateException e) { -            Log.i(Constants.TAG, "NfcForegroundDispatch Error!", e); -        } -        Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!"); -    } - -    /** -     * Disable foreground dispatch in onPause! -     */ -    public void disableNfcForegroundDispatch() { -        mNfcAdapter.disableForegroundDispatch(this); -        Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!"); -    } - -    /** -     * Gets the name of the user out of the raw card output regarding card holder related data -     * -     * @param name the raw card holder related data from the card -     * @return the name given in this data -     */ -    public String getName(String name) { -        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); -    } - -    /** -     * Reduces the raw data from the card by four characters -     * -     * @param output the raw data from the card -     * @return the data field of that data -     */ -    private String getDataField(String output) { -        return output.substring(0, output.length() - 4); -    } - -    /** -     * Communicates with the OpenPgpCard via the APDU -     * -     * @param hex the hexadecimal APDU -     * @return The answer from the card -     * @throws java.io.IOException throws an exception if something goes wrong -     */ -    public String card(String hex) throws IOException { -        return getHex(mIsoDep.transceive(Hex.decode(hex))); -    } - -    /** -     * Converts a byte array into an hex string -     * -     * @param raw the byte array representation -     * @return the  hexadecimal string representation -     */ -    public static String getHex(byte[] raw) { -        return new String(Hex.encode(raw)); -    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java index 43af07bbe..d4858ee5d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java @@ -30,6 +30,7 @@ import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.ui.util.Notify.Style; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java index 863aef65f..a41416a47 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.ParcelableFileCache; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java index 080dc2495..9f2e46b38 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java @@ -27,6 +27,7 @@ import android.view.ViewGroup;  import android.widget.TextView;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.widget.Editor;  import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;  import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java index 4fb2074a5..ad13e390d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java @@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Preferences; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index a80503591..8d876ba69 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;  import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import org.sufficientlysecure.keychain.util.Log; 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 484956e6a..fc30eafcc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -66,6 +66,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;  import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;  import org.sufficientlysecure.keychain.ui.util.FormattingUtils;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java index f17d6e0fd..9e8a12c8a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.base.BaseActivity;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import org.sufficientlysecure.keychain.util.ContactHelper;  import org.sufficientlysecure.keychain.util.ExportHelper; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java index 41fa50705..07d2ef8c0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java @@ -15,7 +15,7 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ -package org.sufficientlysecure.keychain.ui; +package org.sufficientlysecure.keychain.ui.base;  import android.app.Activity;  import android.os.Bundle; @@ -63,8 +63,8 @@ public abstract class BaseActivity extends ActionBarActivity {       * Inflate custom design to look like a full screen dialog, as specified in Material Design Guidelines       * see http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs       */ -    protected void setFullScreenDialogDoneClose(int doneText, View.OnClickListener doneOnClickListener, -                                                View.OnClickListener cancelOnClickListener) { +    public void setFullScreenDialogDoneClose(int doneText, View.OnClickListener doneOnClickListener, +            View.OnClickListener cancelOnClickListener) {          setActionBarIcon(R.drawable.ic_close_white_24dp);          // Inflate the custom action bar view diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java new file mode 100644 index 000000000..0a7b7611b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -0,0 +1,448 @@ +package org.sufficientlysecure.keychain.ui.base; + + +import java.io.IOException; +import java.nio.ByteBuffer; + +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Bundle; +import android.widget.Toast; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; +import org.sufficientlysecure.keychain.ui.NfcOperationActivity; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +import org.sufficientlysecure.keychain.util.Iso7816TLV; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; +import org.sufficientlysecure.keychain.util.Preferences; + + +public abstract class BaseNfcActivity extends BaseActivity { + +    public static final int REQUEST_CODE_PASSPHRASE = 1; + +    protected Passphrase mPin; +    private NfcAdapter mNfcAdapter; +    private IsoDep mIsoDep; + +    private static final int TIMEOUT = 100000; + +    @Override +    protected void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        Intent intent = getIntent(); +        String action = intent.getAction(); +        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { +            throw new AssertionError("should not happen: NfcOperationActivity.onCreate is called instead of onNewIntent!"); +        } + +    } + +    /** +     * This activity is started as a singleTop activity. +     * All new NFC Intents which are delivered to this activity are handled here +     */ +    @Override +    public void onNewIntent(Intent intent) { +        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { +            try { +                handleNdefDiscoveredIntent(intent); +            } catch (IOException e) { +                Log.e(Constants.TAG, "Connection error!", e); +                toast("Connection Error: " + e.getMessage()); +                setResult(RESULT_CANCELED); +                finish(); +            } +        } +    } + +    /** +     * Called when the system is about to start resuming a previous activity, +     * disables NFC Foreground Dispatch +     */ +    public void onPause() { +        super.onPause(); +        Log.d(Constants.TAG, "NfcOperationActivity.onPause"); + +        disableNfcForegroundDispatch(); +    } + +    /** +     * Called when the activity will start interacting with the user, +     * enables NFC Foreground Dispatch +     */ +    public void onResume() { +        super.onResume(); +        Log.d(Constants.TAG, "NfcOperationActivity.onResume"); + +        enableNfcForegroundDispatch(); +    } + +    protected void obtainYubikeyPin(RequiredInputParcel requiredInput) { + +        Preferences prefs = Preferences.getPreferences(this); +        if (prefs.useDefaultYubikeyPin()) { +            mPin = new Passphrase("123456"); +            return; +        } + +        Intent intent = new Intent(this, PassphraseDialogActivity.class); +        intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, +                RequiredInputParcel.createRequiredPassphrase(requiredInput)); +        startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + +    } + +    protected void setYubikeyPin(Passphrase pin) { +        mPin = pin; +    } + +    @Override +    protected void onActivityResult(int requestCode, int resultCode, Intent data) { +        switch (requestCode) { +            case REQUEST_CODE_PASSPHRASE: +                CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_DATA); +                mPin = input.getPassphrase(); +                break; + +            default: +                super.onActivityResult(requestCode, resultCode, data); +        } +    } + +    /** 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 appropiate 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 handleNdefDiscoveredIntent(Intent intent) throws IOException { + +        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + +        // Connect to the detected tag, setting a couple of settings +        mIsoDep = IsoDep.get(detectedTag); +        mIsoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation +        mIsoDep.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 +        if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection +            toast("Opening Error!"); +            setResult(RESULT_CANCELED); +            finish(); +            return; +        } + +        if (mPin != null) { + +            byte[] pin = new String(mPin.getCharArray()).getBytes(); + +            // Command APDU for VERIFY command (page 32) +            String login = +                    "00" // CLA +                            + "20" // INS +                            + "00" // P1 +                            + "82" // P2 (PW1) +                            + String.format("%02x", pin.length) // Lc +                            + Hex.toHexString(pin); +            if (!nfcCommunicate(login).equals(accepted)) { // login +                toast("Wrong PIN!"); +                setResult(RESULT_CANCELED); +                finish(); +                return; +            } + +        } + +        onNfcPerform(); + +        mIsoDep.close(); +        mIsoDep = null; + +    } + +    protected abstract void onNfcPerform() throws IOException; + +    /** 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 = nfcGetFingerprint(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 = mIsoDep.transceive(Hex.decode(data)); + +        Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); +        Log.d(Constants.TAG, "nfc tlv data:\n" + tlv.prettyPrint()); + +        Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5); +        if (fptlv == null) { +            return null; +        } + +        return fptlv.mV; +    } + +    /** 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[] nfcGetFingerprint(int idx) throws IOException { +        byte[] data = nfcGetFingerprints(); + +        // 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 String nfcGetUserId() throws IOException { +        String info = "00CA006500"; +        String data = "00CA005E00"; +        return nfcGetHolderName(nfcCommunicate(info)) + " <" + (new String(Hex.decode(nfcGetDataField( +                nfcCommunicate(data))))) + ">"; +    } + +    /** +     * Calls to calculate the signature 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 { + +        // dsi, including Lc +        String dsi; + +        Log.i(Constants.TAG, "Hash: " + hashAlgo); +        switch (hashAlgo) { +            case HashAlgorithmTags.SHA1: +                if (hash.length != 20) { +                    throw new RuntimeException("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 RuntimeException("Bad hash length (" + hash.length + ", expected 20!"); +                } +                dsi = "233021300906052B2403020105000414" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA224: +                if (hash.length != 28) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 28!"); +                } +                dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA256: +                if (hash.length != 32) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 32!"); +                } +                dsi = "333031300D060960864801650304020105000420" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA384: +                if (hash.length != 48) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 48!"); +                } +                dsi = "433041300D060960864801650304020205000430" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA512: +                if (hash.length != 64) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 64!"); +                } +                dsi = "533051300D060960864801650304020305000440" + getHex(hash); +                break; +            default: +                throw new RuntimeException("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 ( ! "9000".equals(status)) { +            toast("Bad NFC response code: " + status); +            return null; +        } + +        // Make sure the signature we received is actually the expected number of bytes long! +        if (signature.length() != 256 && signature.length() != 512) { +            toast("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); +            return null; +        } + +        return Hex.decode(signature); +    } + +    /** +     * Calls to calculate the signature and returns the MPI value +     * +     * @param encryptedSessionKey the encoded session key +     * @return the decoded session key +     */ +    public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException { +        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]; +        } + +        String first = nfcCommunicate(firstApdu + getHex(one)); +        String second = nfcCommunicate(secondApdu + getHex(two) + le); + +        String decryptedSessionKey = nfcGetDataField(second); + +        Log.d(Constants.TAG, "decryptedSessionKey: " + decryptedSessionKey); + +        return Hex.decode(decryptedSessionKey); +    } + +    /** +     * Prints a message to the screen +     * +     * @param text the text which should be contained within the toast +     */ +    protected void toast(String text) { +        Toast.makeText(this, text, Toast.LENGTH_LONG).show(); +    } + +    /** +     * Receive new NFC Intents to this activity only by enabling foreground dispatch. +     * This can only be done in onResume! +     */ +    public void enableNfcForegroundDispatch() { +        mNfcAdapter = NfcAdapter.getDefaultAdapter(this); +        Intent nfcI = new Intent(this, NfcOperationActivity.class) +                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); +        PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, PendingIntent.FLAG_CANCEL_CURRENT); +        IntentFilter[] writeTagFilters = new IntentFilter[]{ +                new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) +        }; + +        // https://code.google.com/p/android/issues/detail?id=62918 +        // maybe mNfcAdapter.enableReaderMode(); ? +        try { +            mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null); +        } catch (IllegalStateException e) { +            Log.i(Constants.TAG, "NfcForegroundDispatch Error!", e); +        } +        Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!"); +    } + +    /** +     * Disable foreground dispatch in onPause! +     */ +    public void disableNfcForegroundDispatch() { +        mNfcAdapter.disableForegroundDispatch(this); +        Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!"); +    } + +    public String nfcGetHolderName(String name) { +        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); +    } + +    private String nfcGetDataField(String output) { +        return output.substring(0, output.length() - 4); +    } + +    public String nfcCommunicate(String apdu) throws IOException { +        return getHex(mIsoDep.transceive(Hex.decode(apdu))); +    } + +    public static String getHex(byte[] raw) { +        return new String(Hex.encode(raw)); +    } + +}  | 
