android:allowBackup="true"
android:theme="@style/AppTheme">
- <activity android:name=".NFCAuthCREnroll"
+ <activity android:name=".Enroll"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <activity android:name=".NFCAuthCRCheck"
+ <activity android:name=".Check"
android:label="@string/app_name">
</activity>
--- /dev/null
+package org.average.nfcauthcr;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.nfc.tech.IsoDep;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.average.nfcauthcr.YkNeo;
+
+public class Check extends Activity {
+
+ private final String TAG = getClass().getName();
+
+ private AlertDialog swipeDialog;
+ private PendingIntent tagIntent;
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.v(TAG, "Starting the work");
+
+ Intent intent = getIntent();
+ setResult(RESULT_CANCELED);
+ if (swipeDialog != null) {
+ swipeDialog.dismiss();
+ swipeDialog = null;
+ }
+ int slot = intent.getIntExtra("slot", -1);
+ if (slot > 0) {
+ swipeDialog = makeDialog();
+ swipeDialog.show();
+ enableDispatch(slot);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.v(TAG, "Finished the work");
+
+ if(swipeDialog != null) {
+ swipeDialog.dismiss();
+ swipeDialog = null;
+ }
+ disableDispatch();
+ }
+
+ public void onNewIntent(Intent intent) {
+ Log.v(TAG, "NFC Intent arrived");
+ int slot = intent.getIntExtra("slot", -1);
+ byte[] challenge = intent.getByteArrayExtra("challenge");
+ if (slot <= 0) return;
+ Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+ if (tag == null) return;
+ IsoDep isoTag = IsoDep.get(tag);
+ byte[] response = YkNeo.doChallengeYubiKey(
+ isoTag, slot, challenge);
+ if (response != null) {
+ Intent data = getIntent();
+ data.putExtra("response", response);
+ setResult(RESULT_OK, data);
+ }
+ finish();
+ }
+
+ private AlertDialog makeDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.challenging);
+ builder.setMessage(R.string.swipe);
+ builder.setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
+ return builder.create();
+ }
+
+ private void enableDispatch(int slot) {
+ Intent intent = getIntent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra("slot", slot);
+ tagIntent = PendingIntent.getActivity(this, 0, intent, 0);
+ IntentFilter iso =
+ new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
+ if (adapter == null) {
+ Toast.makeText(this, R.string.no_nfc,
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ if (! adapter.isEnabled()) {
+ Toast.makeText(this, R.string.nfc_disabled,
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ adapter.enableForegroundDispatch(
+ this, tagIntent, new IntentFilter[] {iso},
+ new String[][] {new String[] {IsoDep.class.getName()}});
+ }
+
+ private void disableDispatch() {
+ if (tagIntent != null) {
+ tagIntent.cancel();
+ tagIntent = null;
+ }
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
+ if (adapter != null) {
+ adapter.disableForegroundDispatch(this);
+ }
+ }
+}
--- /dev/null
+package org.average.nfcauthcr;
+
+import java.util.Random;
+
+import android.os.Bundle;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.preference.PreferenceManager;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.DialogInterface;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.RadioButton;
+
+import org.average.nfcauthcr.Check;
+
+public class Enroll extends Activity {
+
+ private final String TAG = getClass().getName();
+
+ private static boolean waitingForResult = false;
+ private static SharedPreferences prefs;
+ private static int slot;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ Log.v(TAG, "Starting");
+ prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ setContentView(R.layout.main);
+ slot = prefs.getInt("slot_number", -1);
+ Log.v(TAG, "found saved slot value " + slot);
+ RadioButton btn = null;
+ switch (slot) {
+ case 1: btn = (RadioButton)findViewById(R.id.slot_1);
+ break;
+ case 2: btn = (RadioButton)findViewById(R.id.slot_2);
+ break;
+ }
+ if (btn != null) btn.setChecked(true);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.v(TAG, "Going inactive, try to stop");
+ if (!waitingForResult) { finish(); }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.v(TAG, "Stop requested");
+ }
+
+ public void onSlotSelectionClicked(View view) {
+ Log.v(TAG, "Radio Button selected");
+ if (! ((RadioButton) view).isChecked()) return;
+ switch(view.getId()) {
+ case R.id.slot_1: slot=1; break;
+ case R.id.slot_2: slot=2; break;
+ }
+ Editor editor = prefs.edit();
+ editor.putInt("slot_number", slot);
+ editor.commit();
+ Log.v(TAG, "stored slot number " + slot);
+ }
+
+ public void onEnrollClicked(View view) {
+ Log.v(TAG, "Enroll clicked");
+ if (slot > 0) {
+ runEnrollment(slot);
+ } else {
+ showEnrollResult("Must specify which slot to use");
+ }
+ }
+
+ public void onActivityResult(int requestCode, int resultCode,
+ Intent intent) {
+ Log.v(TAG, "Got activity result");
+ waitingForResult = false;
+
+ if (resultCode != RESULT_OK) {
+ Log.v(TAG, "Error result code " + resultCode);
+ return;
+ }
+ byte[] challenge = intent.getByteArrayExtra("challenge");
+ Log.v(TAG, "Challenge is \"" + hex(challenge) + "\"");
+ byte[] response = intent.getByteArrayExtra("response");
+ Log.v(TAG, "Response is \"" + hex(response) + "\"");
+ Editor editor = prefs.edit();
+ editor.putString("challenge", hex(challenge));
+ editor.putString("response", hex(response));
+ editor.commit();
+ Log.v(TAG, "stored new challenge and response");
+ }
+
+ private void showEnrollResult(final String msg) {
+ Log.v(TAG, "Show result: \"" + msg + "\"");
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.enrollresult);
+ builder.setMessage(msg);
+ builder.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ if (!waitingForResult) { finish(); }
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ private void runEnrollment(int slot) {
+ Random rng = new Random();
+ byte[] challenge = new byte[63];
+ rng.nextBytes(challenge);
+ Log.v(TAG, "Random challenge: " + hex(challenge));
+ Log.v(TAG, "Launching challenging activity");
+ Intent crIntent = new Intent(this, NFCAuthCRCheck.class);
+ crIntent.putExtra("slot", slot);
+ crIntent.putExtra("challenge", challenge);
+ this.startActivityForResult(crIntent, 0);
+ waitingForResult = true;
+ }
+
+ private String hex(byte[] a) {
+ StringBuilder sb = new StringBuilder();
+ if (a == null) return "<null>";
+ for (byte b: a) sb.append(String.format("%02x", b&0xff));
+ return sb.toString();
+ }
+}
+++ /dev/null
-package org.average.nfcauthcr;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.nfc.NfcAdapter;
-import android.nfc.Tag;
-import android.nfc.TagLostException;
-import android.nfc.tech.IsoDep;
-import android.util.Log;
-import android.widget.Toast;
-
-import org.average.nfcauthcr.NFCAuthCRYubiNeo;
-
-public class NFCAuthCRCheck extends Activity {
-
- private final String TAG = getClass().getName();
-
- private AlertDialog swipeDialog;
- private PendingIntent tagIntent;
-
- @Override
- protected void onResume() {
- super.onResume();
- Log.v(TAG, "Starting the work");
-
- Intent intent = getIntent();
- setResult(RESULT_CANCELED);
- if (swipeDialog != null) {
- swipeDialog.dismiss();
- swipeDialog = null;
- }
- int slot = intent.getIntExtra("slot", -1);
- if (slot > 0) {
- swipeDialog = makeDialog();
- swipeDialog.show();
- enableDispatch(slot);
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- Log.v(TAG, "Finished the work");
-
- if(swipeDialog != null) {
- swipeDialog.dismiss();
- swipeDialog = null;
- }
- disableDispatch();
- }
-
- public void onNewIntent(Intent intent) {
- Log.v(TAG, "NFC Intent arrived");
- int slot = intent.getIntExtra("slot", -1);
- byte[] challenge = intent.getByteArrayExtra("challenge");
- if (slot <= 0) return;
- Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
- if (tag == null) return;
- IsoDep isoTag = IsoDep.get(tag);
- byte[] response = NFCAuthCRYubiNeo.doChallengeYubiKey(
- isoTag, slot, challenge);
- if (response != null) {
- Intent data = getIntent();
- data.putExtra("response", response);
- setResult(RESULT_OK, data);
- }
- finish();
- }
-
- private AlertDialog makeDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.challenging);
- builder.setMessage(R.string.swipe);
- builder.setOnCancelListener(
- new DialogInterface.OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- });
- return builder.create();
- }
-
- private void enableDispatch(int slot) {
- Intent intent = getIntent();
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.putExtra("slot", slot);
- tagIntent = PendingIntent.getActivity(this, 0, intent, 0);
- IntentFilter iso =
- new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
- if (adapter == null) {
- Toast.makeText(this, R.string.no_nfc,
- Toast.LENGTH_LONG).show();
- finish();
- return;
- }
- if (! adapter.isEnabled()) {
- Toast.makeText(this, R.string.nfc_disabled,
- Toast.LENGTH_LONG).show();
- finish();
- return;
- }
- adapter.enableForegroundDispatch(
- this, tagIntent, new IntentFilter[] {iso},
- new String[][] {new String[] {IsoDep.class.getName()}});
- }
-
- private void disableDispatch() {
- if (tagIntent != null) {
- tagIntent.cancel();
- tagIntent = null;
- }
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
- if (adapter != null) {
- adapter.disableForegroundDispatch(this);
- }
- }
-}
+++ /dev/null
-package org.average.nfcauthcr;
-
-import java.util.Random;
-
-import android.os.Bundle;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.preference.PreferenceManager;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.content.DialogInterface;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.RadioButton;
-
-import org.average.nfcauthcr.NFCAuthCRCheck;
-
-public class NFCAuthCREnroll extends Activity {
-
- private final String TAG = getClass().getName();
-
- private static boolean waitingForResult = false;
- private static SharedPreferences prefs;
- private static int slot;
-
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- Log.v(TAG, "Starting");
- prefs = PreferenceManager.getDefaultSharedPreferences(this);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- setContentView(R.layout.main);
- slot = prefs.getInt("slot_number", -1);
- Log.v(TAG, "found saved slot value " + slot);
- RadioButton btn = null;
- switch (slot) {
- case 1: btn = (RadioButton)findViewById(R.id.slot_1);
- break;
- case 2: btn = (RadioButton)findViewById(R.id.slot_2);
- break;
- }
- if (btn != null) btn.setChecked(true);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- Log.v(TAG, "Going inactive, try to stop");
- if (!waitingForResult) { finish(); }
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- Log.v(TAG, "Stop requested");
- }
-
- public void onSlotSelectionClicked(View view) {
- Log.v(TAG, "Radio Button selected");
- if (! ((RadioButton) view).isChecked()) return;
- switch(view.getId()) {
- case R.id.slot_1: slot=1; break;
- case R.id.slot_2: slot=2; break;
- }
- Editor editor = prefs.edit();
- editor.putInt("slot_number", slot);
- editor.commit();
- Log.v(TAG, "stored slot number " + slot);
- }
-
- public void onEnrollClicked(View view) {
- Log.v(TAG, "Enroll clicked");
- if (slot > 0) {
- runEnrollment(slot);
- } else {
- showEnrollResult("Must specify which slot to use");
- }
- }
-
- public void onActivityResult(int requestCode, int resultCode,
- Intent intent) {
- Log.v(TAG, "Got activity result");
- waitingForResult = false;
-
- if (resultCode != RESULT_OK) {
- Log.v(TAG, "Error result code " + resultCode);
- return;
- }
- byte[] challenge = intent.getByteArrayExtra("challenge");
- Log.v(TAG, "Challenge is \"" + hex(challenge) + "\"");
- byte[] response = intent.getByteArrayExtra("response");
- Log.v(TAG, "Response is \"" + hex(response) + "\"");
- Editor editor = prefs.edit();
- editor.putString("challenge", hex(challenge));
- editor.putString("response", hex(response));
- editor.commit();
- Log.v(TAG, "stored new challenge and response");
- }
-
- private void showEnrollResult(final String msg) {
- Log.v(TAG, "Show result: \"" + msg + "\"");
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.enrollresult);
- builder.setMessage(msg);
- builder.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- if (!waitingForResult) { finish(); }
- }
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- private void runEnrollment(int slot) {
- Random rng = new Random();
- byte[] challenge = new byte[63];
- rng.nextBytes(challenge);
- Log.v(TAG, "Random challenge: " + hex(challenge));
- Log.v(TAG, "Launching challenging activity");
- Intent crIntent = new Intent(this, NFCAuthCRCheck.class);
- crIntent.putExtra("slot", slot);
- crIntent.putExtra("challenge", challenge);
- this.startActivityForResult(crIntent, 0);
- waitingForResult = true;
- }
-
- private String hex(byte[] a) {
- StringBuilder sb = new StringBuilder();
- if (a == null) return "<null>";
- for (byte b: a) sb.append(String.format("%02x", b&0xff));
- return sb.toString();
- }
-}
+++ /dev/null
-package org.average.nfcauthcr;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import android.nfc.NfcAdapter;
-import android.nfc.Tag;
-import android.nfc.TagLostException;
-import android.nfc.tech.IsoDep;
-import android.util.Log;
-
-import org.average.nfcauthcr.NFCAuthCRYubiNeo;
-
-public class NFCAuthCRYubiNeo {
-
- private static final String TAG = "NFCAuthCRYubiNeo";
-
- // Is it CCID APDU? ISO 7816-4?
-
- // 00 A4 04 00 xx AID - GlobalPlatform - SELECT
- // Le send data = 07: A0 00 00 05 27 20 01
- // Le recv data = 00
- private static final byte[] selectCommand =
- {0x00, (byte) 0xA4, 0x04, 0x00, 0x07, (byte) 0xA0,
- 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x00};
-
- private static final byte[] crCommand =
- {0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00};
-
- private static final byte SLOT_CHAL_HMAC1 = 0x30;
- private static final byte SLOT_CHAL_HMAC2 = 0x38;
-
- public static final byte[] doChallengeYubiKey(IsoDep isoTag, int slot,
- byte[] challenge) {
- try {
- isoTag.connect();
- byte[] resp = isoTag.transceive(selectCommand);
- int length = resp.length;
- Log.v(TAG, "response to select length is " + length);
- if (resp[length - 2] != (byte)0x90 ||
- resp[length - 1] != 0x00) {
- Log.v(TAG, "Wrong response to select");
- //Toast.makeText(this, R.string.tag_error,
- // Toast.LENGTH_LONG).show();
- return null;
- }
- byte[] apdu = new byte[69];
- apdu[0] = 0x00; // CLA
- apdu[1] = 0x01; // INS
- switch (slot) {
- case 1: apdu[2] = SLOT_CHAL_HMAC1; break; // P1
- case 2: apdu[2] = SLOT_CHAL_HMAC2; break; // P1
- }
- apdu[3] = 0x00; // P2
- apdu[4] = 63; // Lc
- System.arraycopy(challenge, 0, apdu, 5,
- challenge.length);
- apdu[apdu.length-1] = 22;
- resp = isoTag.transceive(apdu);
- length = resp.length;
- if (resp[length - 2] != (byte)0x90 ||
- resp[length - 1] != 0x00) {
- Log.v(TAG, "Wrong response to challenge");
- //Toast.makeText(this, R.string.tag_error,
- // Toast.LENGTH_LONG).show();
- return null;
- }
- Log.v(TAG, "response to challenge length is " + length);
- return Arrays.copyOf(resp, length-2);
- } catch (TagLostException e) {
- Log.v(TAG, e.getMessage());
- //Toast.makeText(this,
- // R.string.tag_lost, Toast.LENGTH_LONG).show();
- } catch (IOException e) {
- Log.v(TAG, e.getMessage());
- //Toast.makeText(this,
- // getText(R.string.tag_error) +
- // e.getMessage(),
- // Toast.LENGTH_LONG).show();
- }
- return null;
- }
-}
--- /dev/null
+package org.average.nfcauthcr;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.nfc.tech.IsoDep;
+//import android.util.Log;
+
+public class YkNeo {
+
+ // This is a CCID APDU, ISO 7816-4.
+ // 00 A4 04 00 xx AID - GlobalPlatform - SELECT
+ // Lc, send data = 07: A0 00 00 05 27 20 01
+ // Le, recv data = 00
+ private static final byte[] selectApdu =
+ {0x00, (byte) 0xA4, 0x04, 0x00, 0x07, (byte) 0xA0,
+ 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x00};
+
+ private static final byte SLOT_CHAL_HMAC1 = 0x30;
+ private static final byte SLOT_CHAL_HMAC2 = 0x38;
+
+ public static final byte[] doChallengeYubiKey(IsoDep isoTag, int slot,
+ byte[] challenge) {
+ try {
+ isoTag.connect();
+ byte[] resp = isoTag.transceive(selectApdu);
+ int length = resp.length;
+ //Log.v(TAG, "response to select length is " + length);
+ if (resp[length - 2] != (byte)0x90 ||
+ resp[length - 1] != 0x00) {
+ //Log.v(TAG, "Wrong response to select");
+ //Toast.makeText(this, R.string.tag_error,
+ // Toast.LENGTH_LONG).show();
+ return null;
+ }
+ byte[] crApdu = new byte[69];
+ crApdu[0] = 0x00; // CLA
+ crApdu[1] = 0x01; // INS
+ switch (slot) {
+ case 1: crApdu[2] = SLOT_CHAL_HMAC1; break; // P1
+ case 2: crApdu[2] = SLOT_CHAL_HMAC2; break; // P1
+ }
+ crApdu[3] = 0x00; // P2
+ crApdu[4] = 63; // Lc
+ System.arraycopy(challenge, 0, crApdu, 5,
+ challenge.length); // Payload
+ crApdu[crApdu.length-1] = 22; // Le
+ resp = isoTag.transceive(crApdu);
+ length = resp.length;
+ if (resp[length - 2] != (byte)0x90 ||
+ resp[length - 1] != 0x00) {
+ //Log.v(TAG, "Wrong response to challenge");
+ //Toast.makeText(this, R.string.tag_error,
+ // Toast.LENGTH_LONG).show();
+ return null;
+ }
+ //Log.v(TAG, "response to challenge length is " + length);
+ return Arrays.copyOf(resp, length-2);
+ } catch (TagLostException e) {
+ //Log.v(TAG, e.getMessage());
+ //Toast.makeText(this,
+ // R.string.tag_lost, Toast.LENGTH_LONG).show();
+ } catch (IOException e) {
+ //Log.v(TAG, e.getMessage());
+ //Toast.makeText(this,
+ // getText(R.string.tag_error) +
+ // e.getMessage(),
+ // Toast.LENGTH_LONG).show();
+ }
+ return null;
+ }
+}