Wednesday, April 16, 2014

Host Card Emulation Series: Google Cloud Messaging

Notifications can be an integral part of securing cloud based payment transactions.  Most mobile OS platforms support notification resources that can be leveraged by the platform developers for different reasons.  In the case of cloud based payments, notifications can be used to help secure and enforce authentic transactions at the POS.

The only relevant mobile OS platform at the time of this publication that supports payments that are accepted by existing mainstream acquirers is the AOSP or Android.  HCE (Host Card Emulation) was released inside Android 4.4 last October, so this blog post will focus on using Google Cloud Messaging or GCM.

An overview of the GCM architecture:

As you can see above the GCM and mobile device form an independent channel from the application itself.  This is done because Android OS manages registration of any particular application and therefore managing the communication with that registration which includes OS and device level security precautions.  

For a third party system to send a notification using GCM, the system follows step (a) above and that notification message is expected to be delivered using GCM channels, that is it.

This architecture can be used as a security measure to protect data that is ultimately relayed to a POS from a mobile application.  This simple diagram can indicate how payment transactional data can be protected using 2 different delivery channels
As you see above, the message that is to be delivered to the mobile application is split and actually delivered over 2 different channels.  So now with contactless or EMV (Cloud Based) credentials, payment information can be protected by these means:

1)  Dynamic Transaction Data:  each transaction data delivered to the POS is dynamic or changing from transaction to transaction so that a single transaction data can't be used more than one time

2)  2 Channel Delivery:  each dynamic transaction data above is split and delivered to the mobile device using SSL as one channel and GCM as a separate channel


Using GCM with SimplyTapp Platform

The example below provides a hands-on approach to accomplishing these tasks on the SimplyTapp platform.  The platform has Notification messaging built into it so that you do not have to concern yourself with the technical details of delivering messages over the network.  The interface abstracts the complexity and simply offers a "send to agent" api taking a single message as a string from the card applet service api framework.  This message has a known destination of the matching card agent for the card applet service.  The SimplyTapp platform handles all the routing and delivery to the proper place.

First things first, After downloading the IssuerSdk, unpack it, and go into IssuerSdkBundle and edit CardAgentTesterApp/build.gradle directory and make sure the agentToTest flag is uncommented for "CardAgent-GCM":

//
//  Swap the line below if you wish to test CardAgent-PayPass or
//  CardAgent-VisaMSD-SwipeYours instead of the CardAgent directory's code
// 
//def agentToTest = "CardAgent"
//def agentToTest = "CardAgent-PayPass"
//def agentToTest = "CardAgent-VisaMSD-SwipeYours"
def agentToTest = "CardAgent-GCM"


then build for eclipse:
> gradle eclipse

CardApplet service:

Import CardApplet-GCM into eclipse as a java project.  In the CardApplet.java file look at the code:

private short ATC = 0;

public void process(APDU apdu) {
  // Good practice: Return 9000 on SELECT
  if (selectingApplet()) {
    //no perso required for this card, so enable on first select
    Calendar exp = Calendar.getInstance();
    exp.set(Calendar.YEAR, 2014);
    exp.set(Calendar.MONTH, 4);
    try {
      setStatePersonalized("5413123456784800", exp, """");
    } catch (IOException e) {
    }
    return;
  }

  byte[] buf = apdu.getBuffer();
  switch (buf[ISO7816.OFFSET_INS]) {
  case (byte) 0x01:  //command to send a message via GCM
    short len = apdu.setIncomingAndReceive();
    //convert bytes to ASCII
    byte[] bytes = new byte[len];
    Util.arrayCopy(apdu.getBuffer(), (short)5, bytes, (short)0, len);
    String msg = "";
    try {
      msg = new String(bytes, "UTF-8");
      //echo back the Google Cloud Messaging Notification
      this.sendToAgent("Applet Message No.: "+ATC+"\nData: " + msg);
      ATC++;
    } catch (IOException e) {
    }
    break;
  default:
    // good practice: If you don't know the INStruction, say so:
    ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
  }
}

The code above in the applet is pretty simple.  upon select, it switches the state of the card to personalized if it isn't personalized already.  Also it supports only one command (0x01) that is a simple echo of an incoming data message to the GCM service.  The key method to send a message via GCM is :

sentToAgent("this is a string destined to my agent over GCM");

An IOException() may be thrown in the event that the card agent has not yet been loaded by the mobile application.


CardAgent:

Import the CardAgent-GCM into eclipse as a java project.  In the CardAgent.java file look at the code:

@Override
public void messageFromRemoteCard(String msg)
{
  try {
    //post the message to the app, get the response back to message approval
    postMessage("GCM message from applet:\n"+msg+"\n\nTry Again?"truenull);
  } catch (IOException e) {
  }

}

when the card agent receives a message from the GCM server, this method posts the message to the mobile application.

the next code effectively collects a message from the application user and relays that message to the remote card applet service :

public void post()
{
  //post a message to the app, get a response back to message approval with approval data
  //the message must be less than 32 bytes as defined by the 32
  ApprovalData.StringData stringData = new ApprovalData.StringData((short)0,(short)32);
  ApprovalData approvalData = new ApprovalData(stringData);
  try {
    postMessage("Enter A GCM Message"false, approvalData);
  } catch (IOException e) {
  }
}

@Override
public void messageApproval(boolean approval, ApprovalData approvalData)
{
  if(approvalData!=null && approvalData.getApprovalData()!=null)
  {
    ApprovalData.StringData data = (ApprovalData.StringData)approvalData.getApprovalData();
    if(data!=null && data.getAnswer()!=null)
    {
      byte[] msg = data.getAnswer().getBytes();
      try {
        connect();
      } catch (IOException e) {
      }
      try {
        TransceiveData batchCommands = new TransceiveData(TransceiveData.NFC_CHANNEL);
        batchCommands.setTimeout((short) 5000);

        // In this example we just pack a single APDU command to send a message after card reset
        // and select of applet
        batchCommands.packCardReset(false);
        // select applet
        byte[] apduData = new byte[10];
        apduData[0] = 0x00;
        apduData[1] = (byte)0xa4;
        apduData[2] = 0x04;
        apduData[3] = 0x00;
        apduData[4] = 0x05;
        apduData[5] = 0x00;
        apduData[6] = 0x01;
        apduData[7] = 0x02;
        apduData[8] = 0x03;
        apduData[9] = 0x04;
        batchCommands.packApdu(apduData, false);
        //send message to applet to relay over GCM
        short len = (short)msg.length;
        apduData = new byte[5+len];
        apduData[0] = 0x00;
        apduData[1] = 0x01;
        apduData[2] = 0x00;
        apduData[3] = 0x00;
        apduData[4] = (byte)len;
        System.arraycopy(msg, 0, apduData, 5, msg.length);
        batchCommands.packApdu(apduData, true);  //make sure this completes before disconnecting
        transceive(batchCommands);
      } catch (IOException e) {
      }
      try {
        disconnect();
      } catch (IOException e) {
      }
    }
  }
  else if(approval)
    post();
}

@Override
public void create() {
  post();

}

the transceive function sends apdu commands to the card applet service for processing and the payload of the second APDU indicates the message to send in ascii format from  data.getAnswer().getBytes();


Running a test:

Now, let's try it out.  Import CardAgentTesterApp into eclipse as an android project.  After import is completed, browse to the com.simplytapp.config.Constants.java file and adjust the contents to match your PC settings that are running the CardApplet simulator when you start the card applet:

package com.simplytapp.config;

public class Constants {

    /*setup to communicate to the remoteSE simulator
     * make sure that you have the IsserSdk simulator 
     * running in order for the cardAgent to connect to it.
     * It is important that if you are using the same eclipse
     * client to run the SESDK as this card agent project that
     * you run the SESDK NOT in debug mode as it can tend to
     * slow the response from the SESDK down to non-realistic
     * latencies.  anyway, adjust the ipaddress and port 
     * for the running SESDK below accordingly for your environment
     */
  //address of a running SE simulator
  final public static String ip="192.168.1.66"
  //port address of a running SE simulator
  final public static int port=3000;            


}

Also, make sure your mobile device has WIFI on and is connected to your internal network so that it can reach the simulator config as defined above.

Next, you start the CardApplet project inside eclipse which will prompt you to enter commands in the command window.  First highlight the project "CardApplet-GCM" and click the debug button.  You may have to select the main class for the project.  if so select "com.simplytapp.cardwrapper.CardWrapper".  You should see this in the command window:

# SimplyTapp simulator running on port 3000
# gpjNG connected on port 3000
# Connected to card NFC interface
# using gpjNG!
# type: help
# to get started
#
Found card in terminal: SimplyTapp
ATR: 3B 00 
>

at the command prompt enter:

>/card
ATR: 3B 00 
Command  APDU: 00 A4 04 00 07 A0 00 00 01 51 00 00 
Response APDU: 6F 0F 84 08 A0 00 00 01 51 00 00 A5 04 9F 65 01 FF 90 00 
(16 ms)
Successfully selected Security Domain GP211 A0 00 00 01 51 00 00 
>auth
Command  APDU: 80 50 00 00 08 F4 AA A8 1A ED CB 4C 84 
Response APDU: 00 00 00 00 00 00 00 00 00 00 FF 02 00 00 6C 55 44 79 7A 91 94 AC C7 A2 F3 8D E7 1B 90 00 
(27 ms)
Command  APDU: 84 82 00 00 10 10 2F AA 11 12 B3 0C 93 52 3C 41 C3 46 65 5C 92 
Response APDU: 90 00 
(6 ms)
>install -i 0001020304 |com.st |CardApplet
Command  APDU: 80 E6 0C 00 1E 06 63 6F 6D 2E 73 74 0A 43 61 72 64 41 70 70 6C 65 74 05 00 01 02 03 04 01 00 02 C9 00 00 
Response APDU: 00 90 00 
(17 ms)
>exit-shell
exiting shell, leaving port open

this installs the new applet as AID 0001020304 which is the proper AID for this demo.  after installation you will see that we exit-shell which will leave the simulator running and ready to connect up to the card on the port 3000 as shown above in this configuration.

after the simulator is running the card applet service, you can then run the card applet tester app on your device.  

Once the app starts, you should get a prompt like this:  


After clicking the "Ok" button, the text will go to the card agent which will connect to the remote card applet and send the message to the remote card applet.  The card applet will then, in turn, add the message counter information setup in the example code to the message and send the message to GCM for delivery back to the card agent in the mobile application.  So you should end up seeing a full circle message delivery and notification from your mobile device that looks like this:


This test can be repeated as long as you like and it's only purpose is to demonstrate how to use GCM to transport information from the remote card applet service to its card agent.