From 0c96e6ff75abc6adfa713c00963fd14f5de2f34c Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Fri, 10 May 2013 13:07:28 +0400 Subject: [PATCH] begin converting to multiple ops per request --- AndroidManifest.xml | 9 +- res/values/strings.xml | 4 +- res/values/styles.xml | 16 +- src/org/average/nfcauthcr/Enroll.java | 17 +-- src/org/average/nfcauthcr/NfcCRdispatch.java | 20 ++- .../{TagEvent.java => QueryCrToken.java} | 29 +++- src/org/average/nfcauthcr/YkNeo.java | 137 ++++++++++++------ 7 files changed, 153 insertions(+), 79 deletions(-) rename src/org/average/nfcauthcr/{TagEvent.java => QueryCrToken.java} (63%) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e9e69e0..97bd259 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -23,8 +23,13 @@ - + > + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index aadb0f4..15a4fc0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8,8 +8,8 @@ You must select which slot to use Enrollment result: Token successfully bound to the device - Ready to send challenge - Please touch the back of the phone with the NFC token to enroll it + Ready to communicate + Please touch the back of the phone with the NFC token Cannot reach the NFC adapter Need to enable NFC in Settings Communitation with the tag lost diff --git a/res/values/styles.xml b/res/values/styles.xml index d48e975..3855bba 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -1,7 +1,15 @@ - + - + + + diff --git a/src/org/average/nfcauthcr/Enroll.java b/src/org/average/nfcauthcr/Enroll.java index 6c104c4..96fa314 100644 --- a/src/org/average/nfcauthcr/Enroll.java +++ b/src/org/average/nfcauthcr/Enroll.java @@ -12,10 +12,9 @@ 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.TagEvent; +import org.average.nfcauthcr.QueryCrToken; public class Enroll extends Activity { @@ -49,17 +48,6 @@ 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; @@ -123,8 +111,7 @@ public class Enroll extends Activity { 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); diff --git a/src/org/average/nfcauthcr/NfcCRdispatch.java b/src/org/average/nfcauthcr/NfcCRdispatch.java index bfacfa9..25198be 100644 --- a/src/org/average/nfcauthcr/NfcCRdispatch.java +++ b/src/org/average/nfcauthcr/NfcCRdispatch.java @@ -1,5 +1,8 @@ package org.average.nfcauthcr; +import java.io.IOException; +import java.util.ArrayList; + import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; @@ -20,30 +23,39 @@ public class NfcCRdispatch { private Activity activity = null; private PendingIntent tagIntent = null; - private byte[] challenge; + private ArrayList challenge; NfcCRdispatch(Activity activity) { Log.v(TAG, "new NfcCRdispatch, activity=" + activity); this.activity = activity; } - public byte[] onNewIntent(Intent intent) { + public ArrayList 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 challenge) { this.challenge = challenge; Intent intent = activity.getIntent(); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); diff --git a/src/org/average/nfcauthcr/TagEvent.java b/src/org/average/nfcauthcr/QueryCrToken.java similarity index 63% rename from src/org/average/nfcauthcr/TagEvent.java rename to src/org/average/nfcauthcr/QueryCrToken.java index febfa63..903aefb 100644 --- a/src/org/average/nfcauthcr/TagEvent.java +++ b/src/org/average/nfcauthcr/QueryCrToken.java @@ -1,25 +1,38 @@ 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 TagEvent extends Activity { +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() { @@ -39,9 +52,17 @@ public class TagEvent extends Activity { .create().show(); Intent intent = getIntent(); + ArrayList challenge = + intent.getStringArrayListExtra("challenge"); + int slot = prefs.getInt("slot_number", -1); + intent.putExtra("yubikey_neo_slot", slot); setResult(RESULT_CANCELED); - byte[] challenge = intent.getByteArrayExtra("challenge"); - dispatch.onResume(challenge); + if (challenge != null) { + dispatch.onResume(challenge); + } else { + Log.e(TAG, "Challenge missing in the Intent"); + finish(); + } } @Override @@ -55,7 +76,7 @@ public class TagEvent extends Activity { public void onNewIntent(Intent newintent) { Log.v(TAG, "NFC Intent arrived"); - byte[] response = dispatch.onNewIntent(newintent); + ArrayList response = dispatch.onNewIntent(newintent); if (response != null) { Intent masterintent = getIntent(); masterintent.putExtra("response", response); diff --git a/src/org/average/nfcauthcr/YkNeo.java b/src/org/average/nfcauthcr/YkNeo.java index 39c760b..9c19135 100644 --- a/src/org/average/nfcauthcr/YkNeo.java +++ b/src/org/average/nfcauthcr/YkNeo.java @@ -1,71 +1,112 @@ 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 doChalResp(IsoDep isoTag, int slot, + ArrayList 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 rset = new ArrayList(); + rset.add(hex(Arrays.copyOf(resp, length-2))); + return rset; + } + + private static String hex(byte[] a) { + StringBuilder sb = new StringBuilder(); + if (a == null) return ""; + 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; } } -- 2.39.2