Skip to content

Commit

Permalink
Setup NFC, select applet, read TOTP code
Browse files Browse the repository at this point in the history
  • Loading branch information
Franzaine committed Jan 8, 2018
1 parent 1322ebc commit 436e9b0
Showing 1 changed file with 101 additions and 1 deletion.
102 changes: 101 additions & 1 deletion src/main/java/com/fidesmo/tutorials/otp/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import nordpol.android.OnDiscoveredTagListener;

@EActivity(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
public class MainActivity extends AppCompatActivity implements OnDiscoveredTagListener {

private static final String TAG = "MainActivity";

Expand All @@ -28,6 +28,9 @@ public class MainActivity extends AppCompatActivity {
//Hardcoded TOTP key put onto the OTP applet beforehand
private static final String TOTP_CODE_NAME = "FidesmoOTPTutorial:[email protected]";

// The TagDispatcher is responsible for managing the NFC for the activity
private TagDispatcher tagDispatcher;

// UI elements
@ViewById
TextView mainText;
Expand All @@ -47,16 +50,113 @@ void setMainMessage(String text) {
@Override
protected void onResume() {
super.onResume();
// The first argument is the activity for which the NFC is managed
// The second argument is the OnDiscoveredTagListener which is also
// implemented by this activity
// This means that the method tagDiscovered will be called whenever a new tag appears
tagDispatcher = TagDispatcher.get(this, this);
// Start listening on the NFC interface when the app gains focus.
tagDispatcher.enableExclusiveNfc();
}

@Override
public void onPause() {
super.onPause();
// Stop listening on the NFC interface if the app loses focus
tagDispatcher.disableExclusiveNfc();
}

/**
* This method is called when a contactless device is detected at the NFC interface
* @param intent the PendingIntent declared in onResume
*/
@Override
protected void onNewIntent(Intent intent) {
tagDispatcher.interceptIntent(intent);
}

//Called whenever a tag is discovered on the NFC interface
@Override
public void tagDiscovered(Tag tag) {
setMainMessage("Reading card...");
/**
* As the user might remove the card before we are done communicating with it we have to
* make sure to catch that exception if it happens. We do this with a
* try { ... } catch { ... }
*/
try {
/**
* Create an IsoCard that we can communicate with. Nordpols version of IsoCard
* AndroidCard has a get method to create an IsoCard from a tag.
*/
IsoCard isoCard = AndroidCard.get(tag);
//Send on the IsoCard
communicateWithCard(isoCard);
} catch(IOException e) {
e.printStackTrace();
}
}

protected void communicateWithCard(IsoCard isoCard) {
/**
* The user might still remove the card before we are done with it, remember? Try catch
* that functionality!
*/
try {
//Open a connection to the card
isoCard.connect();
/**
* Send the first command to the card. This command has to be a select command to ensure
* that we are communicating with the correct applet on the card. The OTP_AID is
* the identifier of the OTP applet installed on the card. The Nordpol method
* transceiveAndRequireOk() ensures that the command specified returned
* success (OK_APDU) otherwise we continue to catch {}.
*/
Apdu.transceiveAndRequireOk(Apdu.select(OTP_AID), isoCard);
setMainMessage("Select OK");

//Get a time stamp for creating a TOTP code APDU from
long timeStamp = getTimeStamp();

/**
* Get the APDU command copied from Yubicos open source app for generating TOTP codes.
* Specify that the TOTP code with the name TOTP_CODE_NAME is fetched. This "name"
* should ideally by generated and not hardcoded and supplied when the TOTP token is
* input into the OTP applet. Knowing this, let's hardcode it for the sake of this
* tutorial with the value that we scanned/input into the applet before using some
* other app like Yubico Authenticator.
*/
byte[] totpCodeApdu = Otp.totpCodeApdu(TOTP_CODE_NAME, timeStamp);

/**
* Send the command to the IsoCard using Nordpol method transceiveAndGetResponse.
* This method returns the response and automatically batches long messages.
* The first parameter is the APDU command. The second parameter is the IsoCard to
* communicate with. The third parameter is an optional and applet specific APDU
* command for requesting more content from the applet. I found this on in the open
* source code of Yubico Authenticator
*/
byte[] rawTotpCode = Apdu.transceiveAndGetResponse(totpCodeApdu, isoCard, Otp.SEND_REMAINING_APDU);
/**
* Check the result of the communication with the Nordpol method hasStatus. Supply the
* the response APDU and the expected APDU as parameters. If the ending of the first
* parameter matches the second parameter it is considered a match.
*/
if(Apdu.hasStatus(rawTotpCode, Apdu.OK_APDU)){
/**
* Decipher the TOTP code from the gibberish the response consists of (using
* Yubicos decipher methods).
*/
String totpCode = Otp.decipherTotpCode(rawTotpCode);
//Append the TOTP code to the TextView
setMainMessage("TOTP code is: " + totpCode);
}

//Close the card communication.
isoCard.close();
} catch (IOException e) {
e.printStackTrace();
}
}

private static long getTimeStamp() {
Expand Down

0 comments on commit 436e9b0

Please sign in to comment.