Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:tutorial@fidesmo.com";

// 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