Android TOTP NFC tutorial - Using Nordpol

Introduction

This tutorial shows how to implement basic TOTP over NFC with cards and other NFC devices (such as USB dongles) for your own app on Android. TOTP means Time-based One-time Password algorithm and is an algorithm that computes a one-time password from a shared secret key and the current time. You can read about the TOTP technology here. After the tutorial you will be familiar with advanced NFC handling on Android and know how to implement hardware TOTP in your own Android app. For the full and commented source code visit this GitHub repo.

To make the setup easier we use the open source library Nordpol for our NFC interfacing. We also use APDU commands specified in the open source Android app Yubico Authenticator source code. APDU commands are commands for communicating with cards. As Yubico has made the applet running on the card as well they are surely a good source for the necessary commands for communicating with it.

Goal

This tutorial will leave us at the point where we can generate TOTP codes by tapping a token with the applet installed and into which the secret seed information has been provisioned.

Tools that will be used

  • The Android Studio development environment set up and parts of the Android SDK.
  • For readability we will use the library Android Annotations but it is not necessary.
  • We will also use the open source library Nordpol to simplify our NFC handling. It is created by us at Fidesmo but already has quite a following with cool NFC companies doing awesome stuff!
  • A Fidesmo card with the OTP applet installed (how-to will come later) or a Yubikey Neo.

Setup

Clone the starting repository and open it up on Android Studio by choosing "Import project" in the startup-menu and choose the repository folder.

Install applet (Only if you have a Fidesmo device)

  1. If you have not downloaded the Fidesmo Android app, please do so now.
  2. Open the Fidesmo app and choose Fidesmo OTP among the applets avaiable
  3. Install the applet by choosing the service 'Install' and follow the instructions

Get Yubico Authenticator Android app

Download the Yubico Authenticator Android app from Google Play here

Save the OTP on the applet

  1. Open up Yubico Authenticator and choose the option 'Scan account QR-code' in the overflow menu in the top right corner.
  2. Scan the below code

  3. Tap the card to your phone to finish adding the key to the applet

The code actually contains a static key that the app that we are building has been hard coded with. This is far from optimal but keeps the tutorial short and to the point. The QR code should of course be generated automatically and not be the same for all if actually used in a critical situation. A real-life example where a dynamic code is generated is explained here. This is the string the QR contains: otpauth://totp/FidesmoOTPTutorial:tutorial@fidesmo.com?secret=NBSWY3DP

Permissions

The repository has been prepared with the necessary permissions: NFC. This permission is not considered a dangerous permission by Google and will not show up in the 'dangerous permissions' part of the app installation flow on Google Play Store. It is also not a runtime permission which means that on Marshmallow (Android 6.0) the user will not be bothered by the permission upon install and will not be able to remove the permission from the app after install either. This code is available in the project specific AndroidManifest.xml file. This file also contains a uses-feature tag for making sure no devices without NFC install the app (through Google Play Store) and that in turn has a required parameter which you may toggle as necessary. Below is the code of interest:

Dependencies

The app is set up with Android Annotations but as that is not the goal of this tutorial we wont go into depth on how to set it up more than that it is done in the repository already.

The second dependency we need is the Nordpol library which will help us interface with NFC easily. The import is already set up and is avaiable to look at in the project specific build.gradle file. Below are the important parts:

Prepared Java files

As noted before: Several methods have been copied from the open source app Yubico Authenticator on GitHub. These methods allow us to send the right commands to the applet on the card for generating the OTP codes that we want. These methods and their licence are available in the Otp.java file.

MainActivity.java has been prepared with a simple layout containing a TextView and the TextView has been set up. The defined method setMainMessage appends any provided text to the TextView. There is also a method for getting the current time plus 10 seconds to ensure that the generated TOTP code is valid at least that long.

We also have two constants available:

  1. OTP_AID: The identifier of the applet on the card
  2. TOTP_CODE_NAME: The key that our QR-code from before contained. This is the identifier we use when asking the card for a TOTP code.

Running

Now that we all know what the code contains let us test it out before we begin!

Press the run button in Android Studio and make sure to choose a physical Android device to run it on. You should see a very simple layout with a text prompting you to tap your card. If you tap your card now our app won't do anything at all and either Android will play a sound (indicating that NFC input was discovered and you have no apps that use it) or another app/a prompt about other apps will appear. This is the default behavior if you don't claim NFC interactions for yourself. Let's do that next!

Claiming NFC with TagDispatcher, brought to you by Nordpol

The rest of the tutorial will be completely in the MainActivity.java file. Open that file in Android Studio now!

Start by declaring a TagDispatcher up top in our class, call it tagDispatcher and make it private. Like so:

Set the value of our TagDispatcher variable with the Nordpol provided TagDispatcher.get(Activity activity, OnDiscoveredTagListener onDiscoveredTagListener) method. Supply the current Activity as the first parameter to allow Nordpol to attach itself to the Activity lifecycle. The second parameter should be the callback/interface Nordpol uses to alert of any NFC interactions. Instead of a variable we can instead implement OnDiscoveredTagListener in our activity and simply supply the Activity as the second paramenter as well. OnDiscoveredTagListener requires us to override the Nordpol callback method tagDiscovered(Tag tag){ /* ... */ }. This method will be called whenever NFC interactions happen.

Leaving the tagDiscovered method empty, implementing OnDiscoveredTagListener in the Activity and setting the value of tagDispatcher in onResume() leaves us with the following:

Using Nordpol to enable and disable exclusive NFC for our app should be done in onResume() and onPause(). For Nordpol to function properly on all supported versions of Android you also have to pass on any received Intents in onNewIntent(Intent intent) { /* ... */ } to the library. The methods should look like this when we are done:

In the tagDiscovered method call the pre-defined setMainMessage() method and pass "Reading card..." as the parameter. Leaving the tagDiscovered method looking like this:

Now when running the code and tapping a token to the phone no other apps should pop up and the main TextView should add another line that says "Reading card...". If this is the case: Great! We have now successfully claimed exclusive access to the devices NFC while our app is running in foreground! Let us move on and actually send some commands.

Communicating with the card

Continue in the tagDiscovered() method, just below our setMainMessage() call, create a new IsoCard variable called isoCard. IsoCard is an interface that defines basic operations to the card. Use Nordpol's AndroidCard.get() method to create the IsoCard from the Tag that we got as a parameter in the tagDiscovered method declaration. To produce the IsoCard, Nordpol communicates with the card. If the token is removed before the library is done the generation might fail in which instance Nordpol will throw an IOException. Make sure to catch this exception by doing the call inside of a try{ /* .... */ } catch { /* ... */ }. This leaves the tagDiscovered method declaration looking like this:

To ensure legibility pass the generated IsoCard on as the sole parameter to another method called something like "communicateWithCard". Call this method just below we generated the IsoCard inside of our try-catch.

In this new method we will first open a connection to the card and then close it. Do this by calling isoCard.connect(); and then isoCard.close(); This too has to be done in a try-catch as the card might still be removed prematurely.

Between these two calls we need to select the applet that we want to talk to (the OTP applet) before actually doing any communication with it. We can do this very simply by using Nordpols provided Apdu.select(String AppletId) method and passing the AID of the applet as parameter. The AID of the applet is found in the the Yubico Authenticator source code and and set to the variable OTP_AID in MainActivity.java. It has only been transformed from a byte array to a string.

By using the Nordpol method Apdu.transceiveAndRequireOk(byte[] apduCommand, IsoCard isoCard); we can know for sure that the applet selection went fine. This is because it checks the status bytes of the response APDU received from the card for us after finishing the communication. Otherwise it would have thrown an IOException and landed us in catch{}. Pass the previously explained Select command as the first parameter in the Apdu.transceiveAndRequireOk method and our generated IsoCard as the second parameter.

Finally adding a setMainMessage("Select OK"); call just below leaves us with the following tagDiscovered and communicateWithCard method declarations:

Now, when running this and tapping a card to the back of the phone our app will generate an IsoCard from the received Tag, so we can communicate with it. After this we open up the connection to the IsoCard, select the OTP applet and finally close the connection again. The main message should now also print "Select OK" to indicate the progress so far. Brilliant! This far everything has been fairly generic and should not change too much between different applets, except of course for different identifiers. Next we will dig deep into the OTP applet, do some pretty applet specific stuff and generate our TOTP code!

Generating a TOTP code

Start by declaring a long called timeStamp and give it the value provided by the pre-defined method getTimeStamp(); This will yield the current time plus ten seconds. This ensures that when we pass this time to the OTP applet for generating a TOTP code it will be valid at least for that long. Declare the variable between the call to setMainMessage and the closing of our connection to the card. Continue by declaring a byte array called totpCodeApdu. This variable will hold the command that we send to the card for generating the OTP code. In the Otp.java file there is a method with code created by Yubico and used in the Yubico Authenticator app for the same purpose called totpCodeApdu. The method takes two parameters:

  1. String otpKeyName
  2. long timeStamp

The first parameter is the identifier of the specific OTP code that we want to generate. In this case it is hardcoded as the variable TOTP_CODE_NAME in the Activity. You should recognize it as the same code we used before when we input the QR code into the applet using the Yubico Authenticator app. The second parameter is the timeStamp variable we just declared. This should leave us with the following communicateWithCard method:

Continue where we left off in the communicateWithCard method and declare a new byte array called rawTotpCode. This variable will hold the response from the applet when we send it the TOTP code generating command. Use the Nordpol method Apdu.transceiveAndGetResponse(byte[] apduCommand, IsoCard isoCard, String sendRemainingApdu); with the following parameters:

  1. The TOTP generating APDU command we set to the variable totpCodeApdu
  2. Our isoCard variable
  3. The applet specific command for asking the OTP applet to continue in case there is any additional information remaining. This has been picked from the source code of the Yubico Authenticator app too and set it to the variable SEND_REMAINING_APDU in the Otp.java class.

This should leave communicateWithCard looking like this:

Now that we have the command for generating a TOTP code, sent it to the card and have the cards response we only need to do something with it! Check the response with the by Nordpol supplied method Apdu.hasStatus(byte[] checkApdu, byte[] expectedApdu) which returns true if the values match and false if not. (Sidenote: It is not actually comparing the full values, it only compares the the expectedApdu with the end of the checkApdu. This is because the status is supplied in the end of the APDU response.) Pass the rawTotpCode as the first parameter and check it against Apdu.OK_APDU which is the standard OK APDU response. Now put this in the condition of an if statement and let's next put some code inside its body.

In the if statement's body declare a new String variable called totpCode which will hold our generated TOTP code once we have deciphered it from the response we got from the card. (Once again) we are using a method from the Yubico Authenticator app for doing this. The method is declared in the Otp.java class and called decipherTotpCode. Calling this method with the rawTotpCode response as sole parameter will return a readable TOTP code.

Finally call setMainMessage again with the parameter "TOTP code is: " + totpCode and run the app! The communicateWithCard method should now look like this:

When the app is running and the card is tapped to the phone the app should now print a valid TOTP code! We got a response from our TOTP generating APDU command, then found the response to be positive before deciphering and printing it to our TextView.

Success!

While generating TOTP codes from hardware via NFC on Android was pretty simple with Nordpol this was not a full blown implementation yet. Automatic code generation, not relying on another app for inputting the tokens, server-side software to check the TOTP codes and such are all good next steps for going forward with this project. However to many this should only serve as a starting point for other integrations with different applets. There are thousands of applets out there and you can write your own if you need. However, anytime you want to do some applet integration you now know that you should look for Nordpol to help you out. Changes, suggestions, questions or the like are welcome in the Nordpol repo on GitHub.

Thanks for following along and see you in the next tutorial!