From: Eugene Crosser Date: Fri, 10 May 2013 09:07:28 +0000 (+0400) Subject: begin converting to multiple ops per request X-Git-Url: http://www.average.org/gitweb/?p=YkNeoCR.git;a=commitdiff_plain;h=0c96e6ff75abc6adfa713c00963fd14f5de2f34c;ds=sidebyside begin converting to multiple ops per request --- 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/QueryCrToken.java b/src/org/average/nfcauthcr/QueryCrToken.java new file mode 100644 index 0000000..903aefb --- /dev/null +++ b/src/org/average/nfcauthcr/QueryCrToken.java @@ -0,0 +1,87 @@ +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 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 response = dispatch.onNewIntent(newintent); + if (response != null) { + Intent masterintent = getIntent(); + masterintent.putExtra("response", response); + setResult(RESULT_OK, masterintent); + finish(); + } + } +} diff --git a/src/org/average/nfcauthcr/TagEvent.java b/src/org/average/nfcauthcr/TagEvent.java deleted file mode 100644 index febfa63..0000000 --- a/src/org/average/nfcauthcr/TagEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -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(); - } - } -} 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; } }