aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-03-21 17:13:31 +0100
committerVincent Breitmoser <valodim@mugenguild.com>2015-03-21 17:13:35 +0100
commit147003123fffc84b1d658f78d0a888479ce4ff35 (patch)
tree5082c02f6907d36a3f3c2f31d1f93e5a6c629274 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
parent9ef2795fcd04fc228b4e3f30342f9eb79bab59fc (diff)
downloadopen-keychain-147003123fffc84b1d658f78d0a888479ce4ff35.tar.gz
open-keychain-147003123fffc84b1d658f78d0a888479ce4ff35.tar.bz2
open-keychain-147003123fffc84b1d658f78d0a888479ce4ff35.zip
first steps toward yubikey activity
- move BaseActivity into new package - extract BaseNfcActivity from NfcOperationsActivity
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java446
1 files changed, 7 insertions, 439 deletions
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));
- }
}