</intent-filter>
</activity>
- <activity android:name=".TagEvent"
- android:label="@string/app_name">
+ <activity android:name=".QueryCrToken"
+ android:label="@string/app_name"
+ android:theme="@style/TransparentDialog">>
+ <intent-filter>
+ <action android:name="org.openintents.action.CHALLENGE_FOR_RESPONSE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
</activity>
</application>
<string name="need_slot">You must select which slot to use</string>
<string name="enrollresult">Enrollment result:</string>
<string name="enroll_success">Token successfully bound to the device</string>
- <string name="challenging">Ready to send challenge</string>
- <string name="swipe">Please touch the back of the phone with the NFC token to enroll it</string>
+ <string name="challenging">Ready to communicate</string>
+ <string name="swipe">Please touch the back of the phone with the NFC token</string>
<string name="no_nfc">Cannot reach the NFC adapter</string>
<string name="nfc_disabled">Need to enable NFC in Settings</string>
<string name="tag_lost">Communitation with the tag lost</string>
<resources>
- <style name="AppBaseTheme" parent="android:Theme.Light">
- </style>
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ </style>
- <style name="AppTheme" parent="AppBaseTheme">
- </style>
+ <style name="AppTheme" parent="AppBaseTheme">
+ </style>
+
+ <style name="TransparentDialog" parent="@android:style/Theme.Dialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
</resources>
import android.content.DialogInterface;
import android.util.Log;
import android.view.View;
-import android.widget.TextView;
import android.widget.RadioButton;
-import org.average.nfcauthcr.TagEvent;
+import org.average.nfcauthcr.QueryCrToken;
public class Enroll extends Activity {
if (btn != null) btn.setChecked(true);
}
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @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;
byte[] challenge = new byte[63];
rng.nextBytes(challenge);
Log.v(TAG, "Random challenge: " + hex(challenge));
- Intent crIntent = new Intent(this, TagEvent.class);
- crIntent.putExtra("yubikey_neo_slot", slot);
+ Intent crIntent = new Intent(this, QueryCrToken.class);
crIntent.putExtra("challenge", challenge);
waitingForResult = true;
this.startActivityForResult(crIntent, 0);
package org.average.nfcauthcr;
+import java.io.IOException;
+import java.util.ArrayList;
+
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
private Activity activity = null;
private PendingIntent tagIntent = null;
- private byte[] challenge;
+ private ArrayList<String> challenge;
NfcCRdispatch(Activity activity) {
Log.v(TAG, "new NfcCRdispatch, activity=" + activity);
this.activity = activity;
}
- public byte[] onNewIntent(Intent intent) {
+ public ArrayList<String> onNewIntent(Intent intent) {
Log.v(TAG, "NFC Intent arrived");
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag == null) return null;
IsoDep isoTag = IsoDep.get(tag);
try {
int slot = intent.getIntExtra("yubikey_neo_slot", -1);
+ isoTag.connect();
return YkNeo.doChalResp(isoTag, slot, challenge);
+ } catch (TagLostException e) {
+ Log.v(TAG, e.getMessage());
+ Toast.makeText(activity, e.getMessage(),
+ Toast.LENGTH_LONG).show();
+ } catch (IOException e) {
+ Log.v(TAG, e.getMessage());
+ Toast.makeText(activity, e.getMessage(),
+ Toast.LENGTH_LONG).show();
} catch (CRException e) {
Log.v(TAG, e.getMessage());
Toast.makeText(activity, e.getMessage(),
Toast.LENGTH_LONG).show();
- return null;
}
+ return null;
}
- public void onResume(byte[] challenge) {
+ public void onResume(ArrayList<String> challenge) {
this.challenge = challenge;
Intent intent = activity.getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
--- /dev/null
+package org.average.nfcauthcr;
+
+import java.util.ArrayList;
+
+import android.os.Bundle;
+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.content.SharedPreferences;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.nfc.tech.IsoDep;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.average.nfcauthcr.NfcCRdispatch;
+
+public class QueryCrToken extends Activity {
+
+ private final String TAG = getClass().getName();
+
+ private NfcCRdispatch dispatch = new NfcCRdispatch((Activity)this);
+ private SharedPreferences prefs;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.v(TAG, "onCreate get prefs");
+ prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.v(TAG, "Starting the work");
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.challenging)
+ .setMessage(R.string.swipe)
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ Log.v(TAG, "Cancel");
+ finish();
+ }
+ })
+ .create().show();
+
+ Intent intent = getIntent();
+ ArrayList<String> challenge =
+ intent.getStringArrayListExtra("challenge");
+ int slot = prefs.getInt("slot_number", -1);
+ intent.putExtra("yubikey_neo_slot", slot);
+ setResult(RESULT_CANCELED);
+ if (challenge != null) {
+ dispatch.onResume(challenge);
+ } else {
+ Log.e(TAG, "Challenge missing in the Intent");
+ finish();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.v(TAG, "Finished the work");
+
+ dispatch.onPause();
+ }
+
+ public void onNewIntent(Intent newintent) {
+ Log.v(TAG, "NFC Intent arrived");
+
+ ArrayList<String> response = dispatch.onNewIntent(newintent);
+ if (response != null) {
+ Intent masterintent = getIntent();
+ masterintent.putExtra("response", response);
+ setResult(RESULT_OK, masterintent);
+ finish();
+ }
+ }
+}
+++ /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.NfcCRdispatch;
-
-public class TagEvent extends Activity {
-
- private final String TAG = getClass().getName();
-
- private NfcCRdispatch dispatch = new NfcCRdispatch((Activity)this);
-
- @Override
- protected void onResume() {
- super.onResume();
- Log.v(TAG, "Starting the work");
-
- new AlertDialog.Builder(this)
- .setTitle(R.string.challenging)
- .setMessage(R.string.swipe)
- .setOnCancelListener(
- new DialogInterface.OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- Log.v(TAG, "Cancel");
- finish();
- }
- })
- .create().show();
-
- Intent intent = getIntent();
- setResult(RESULT_CANCELED);
- byte[] challenge = intent.getByteArrayExtra("challenge");
- dispatch.onResume(challenge);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- Log.v(TAG, "Finished the work");
-
- dispatch.onPause();
- }
-
- public void onNewIntent(Intent newintent) {
- Log.v(TAG, "NFC Intent arrived");
-
- byte[] response = dispatch.onNewIntent(newintent);
- if (response != null) {
- Intent masterintent = getIntent();
- masterintent.putExtra("response", response);
- setResult(RESULT_OK, masterintent);
- finish();
- }
- }
-}
package org.average.nfcauthcr;
-import java.lang.String;
import java.io.IOException;
+import java.lang.String;
+import java.util.ArrayList;
import java.util.Arrays;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
-import android.nfc.TagLostException;
import android.nfc.tech.IsoDep;
import org.average.nfcauthcr.CRException;
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
+ // http://forum.yubico.com/viewtopic.php?\
+ // f=26&t=1053&sid=2a11c0f97306e4b699bf67502b7a754a
+ // CCID APDU, ISO 7816-4.
+
+ // Start with Select APDU:
+ // CLA INS P1 P2 Lc AID Le
+ // 00 A4 04 00 - GlobalPlatform - Application Select
+ // Lc = 07
+ // A0 00 00 05 27
+ // Yubico's RID (Registered application provider IDentifier)
+ // 20 01
+ // PIX (Proprietary application Identifier eXtension)
+ // for the Yubikey2 applet
+ // Le = 00
private static final byte[] selectApdu =
{0x00, (byte) 0xA4, 0x04, 0x00, 0x07, (byte) 0xA0,
0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x00};
+ // Application APDUs:
+ // CLA INS P1 P2
+ // 00 01 YK 00
+ // YK is the one-byte command&slot code as in the USB interface
+ // Lc + data bytes follow
+ // Le is optional
private static final byte SLOT_CHAL_HMAC1 = 0x30;
private static final byte SLOT_CHAL_HMAC2 = 0x38;
- public static byte[] doChalResp(IsoDep isoTag, int slot,
- byte[] challenge) throws CRException {
- try {
- isoTag.connect();
- byte[] resp = isoTag.transceive(selectApdu);
- int length = resp.length;
- if (resp[length - 2] != (byte)0x90 ||
- resp[length - 1] != 0x00) {
- throw new CRException(String.format(
- "NFC select error code: %02x:%02x",
- resp[length - 2], resp[length - 1]));
- }
- 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) {
- throw new CRException(String.format(
- "NFC CR error code: %02x:%02x",
- resp[length - 2], resp[length - 1]));
- }
- if (length != 22) {
- throw new CRException(String.format(
- "NFC wrong response size: got %d, need 20",
- length-2));
- }
- return Arrays.copyOf(resp, length-2);
- } catch (TagLostException e) {
- throw new CRException("NFC connection lost", e);
- } catch (IOException e) {
- throw new CRException("NFC I/O: " + e.getMessage(), e);
+ public static ArrayList<String> doChalResp(IsoDep isoTag, int slot,
+ ArrayList<String> cset)
+ throws IOException, CRException {
+ byte[] challenge = unhex(cset.get(0));
+ if (challenge.length > 127) {
+ throw new CRException(String.format(
+ "NFC challenge size too big: %d",
+ challenge.length));
+ }
+ if (slot != 1 && slot != 2) {
+ throw new CRException(String.format(
+ "NFC Yubikey slot is %d, can be 1 or 2",
+ slot));
+ }
+ byte[] resp = isoTag.transceive(selectApdu);
+ int length = resp.length;
+ if (resp[length - 2] != (byte)0x90 ||
+ resp[length - 1] != 0x00) {
+ throw new CRException(String.format(
+ "NFC select error code: %02x:%02x",
+ resp[length - 2], resp[length - 1]));
+ }
+ byte[] crApdu = new byte[6+challenge.length];
+ 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] = (byte)challenge.length; // Lc
+ System.arraycopy(challenge, 0, crApdu, 5,
+ challenge.length); // Payload
+ crApdu[5+challenge.length] = 22; // Le
+ resp = isoTag.transceive(crApdu);
+ length = resp.length;
+ if (resp[length - 2] != (byte)0x90 ||
+ resp[length - 1] != 0x00) {
+ throw new CRException(String.format(
+ "NFC CR error code: %02x:%02x",
+ resp[length - 2], resp[length - 1]));
+ }
+ if (length <= 2) {
+ throw new CRException(String.format(
+ "NFC wrong response size: only %d bytes",
+ length-2));
+ }
+ ArrayList<String> rset = new ArrayList<String>();
+ rset.add(hex(Arrays.copyOf(resp, length-2)));
+ return rset;
+ }
+
+ private static 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();
+ }
+
+ private static byte[] unhex(String s) {
+ int len = s.length();
+ if ((len % 2) != 0) return null;
+ byte[] b = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ b[i / 2] = (byte)Integer.parseInt(
+ s.substring(i,i+2), 16);
}
+ return b;
}
}