Friday, October 10, 2014

The Absolute Simplest HCE "Pay" Application Enablement


Most of the code below relates to GUI messaging,  but this Android app is full HCE for a Cloud Based Payments card.  It is a total of 201 lines of code including comments, and line spaces. It is a single class application for simplicity.
The goal is to show that the libraries for managing CBP on the mobile app are lightweight for developers to use.
The libs are designed to be placed into ANY application that may want to enable CBP.  The one below is about the simplest integration.
The full application is called SimplestTapp and can be downloaded and tested here:
http://wiki.simplytapp.com/home/self-driven-hce-pilot/make-a-payment-card/simplytapp-s-vcbp-changelog/software-dev-kits/mobile-sdk

it also may be good to view this help resource for creating and managing the card perso and lifecycle in the cloud:
http://wiki.simplytapp.com/home/using-admin-api-console

The core of the card download and enablement is really contained in this section.  This section instantiates a VirtualCard that will be loaded from the remote server.  In order to instantiate the CBP card, you will see that there are some required credential information because a request will be made to the CBP server to retrieve the card that will be used.
The data in the snippet is sample data only.

The AsyncTask is used because the load() method makes network calls and can't be run on the UI thread.

        // Create a card with data from AdminApi console
        // See wiki for help:
        // http://wiki.simplytapp.com/home/using-admin-api-console
        //
        // Values are found within your AdminApi Console. Click Get APP
        // Credentials.
        //
        // card agent code hash is found inside the Issuer Entity portion of the
        // web portal
        //
        final VirtualCard card = VirtualCardBuilder
                .cardId("5901")
                .mobileAppConsumerKey(
                        "oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU")
                .mobileAppConsumerSecret(
                        "ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he")
                .walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax")
                .walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3")
                .cardAgentCodeHash("aa4185c2364048447a38aa02499a9897")
                .context(this).build();

        // now load the card...
        // this requires network connectivity and is blocking
        // so we must use AsyncTask to avoid running this in UI thread
        //
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    myActivity.postUi("=================================="
                            + "\n" + "=====Loading Card From Server====="
                            + "\n" + "=================================="
                            + "\n");
                    card.load();
                } catch (IOException ex) {
                    Log.e(TAG, "Failed to load", ex);
                }
                return (Void) null;
            }
        }.execute();

 Once the VirtualCard successfully loads, it has a mind of it's own and will perform all activities related to making a successful payment when requested to do so.  For this, the feedback mechanism from the card to the mobile application is a messaging interface and should be defined in a block similar to the one below.  You will notice that the block below is responsible for handling feedback from the card and also notifying the card to activate after it was successfully created:

    // define the message default card message handler
    // and set it in the lib
    static {
        VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() {
            @Override
            public void virtualCardMessage(VirtualCardMessaging.Message message) {
                switch (message.getCode()) {
                case VirtualCardMessaging.CARD_CREATION_COMPLETED:
                    try {
                        // when card is created, then activate the card agent,
                        // then connect it to the ApduService,
                        // then ask the user if they want this app the default
                        // app
                        // for servicing the reader
                        message.getVirtualCard().activate();
                        ApduService.setVirtualCard(message.getVirtualCard());
                        myActivity.postUi("========================"
                                + "\n"
                                + "=====Card Created======="
                                + "\n"
                                + "========================"
                                + "\n"
                                + "==Card Id: "
                                + message.getVirtualCard().getVirtualCardId()
                                + "\n"
                                + "==Num: "
                                + message.getVirtualCard()
                                        .getVirtualCardNumber()
                                + "\n"
                                + "==Card Exp: "
                                + message.getVirtualCard()
                                        .getVirtualCardExpDate() + "\n"
                                + "==Card Type: "
                                + message.getVirtualCard().getVirtualCardType()
                                + "\n" + "==Card Logo: "
                                + message.getVirtualCard().getVirtualCardLogo()
                                + "\n");
                        myActivity.postUi("========================" + "\n"
                                + "=====Card Activating====" + "\n"
                                + "========================" + "\n");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "=====Card Activated=====" + "\n"
                            + "=====Ready To Tap=======" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                case VirtualCardMessaging.TRANSACTION_ENDED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "===Ready To Tap Again====" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                default:
                    break;
                }

                // log the message
                Log.d(TAG,
                        message.getMessage() + " cardId="
                                + message.getVirtualCardId() + " code="
                                + message.getCode());
                myActivity.postUi(message.getMessage() + " cardId="
                        + message.getVirtualCardId() + " code="
                        + message.getCode() + "\n");
            }

        };

        // set the default messenger
        VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger);
    }


You will notice above that the messaging loop is logging all messages from the card and printing them to the screen of the app and the debugger.  This can give you an idea of the events the card notifies the app.  As far as API requests to the card from the app, the VirtualCard library contains many methods.  Here is the JavaDoc:
http://simplytapp.github.io/android/virtualcard/doc/

Full activity class code for this example:

package com.simplytapp.example.simplesttapp;

import java.io.IOException;

import com.simplytapp.virtualcard.ApduService;
import com.simplytapp.virtualcard.VirtualCard;
import com.simplytapp.virtualcard.VirtualCardBuilder;
import com.simplytapp.virtualcard.VirtualCardMessaging;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.cardemulation.CardEmulation;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static MainActivity myActivity = null;
    private TextView tv = null;

    // this method posts strings to the textview box in the UI thread
    private void postUi(final String t) {
        new Runnable() {
            public void run() {
                runOnUiThread(new Runnable() {
                    public void run() {
                        tv.append(t);
                        if (tv.getLayout() != null) {
                            // auto-scroll vertically
                            final int scrollAmount = tv.getLayout().getLineTop(
                                    tv.getLineCount())
                                    - tv.getHeight();
                            // if there is no need to scroll, scrollAmount will
                            // be <=0
                            if (scrollAmount > 0)
                                tv.scrollTo(0, scrollAmount);
                            else
                                tv.scrollTo(0, 0);
                        }
                    }
                });
            }
        }.run();
    }

    // define the message default card message handler
    // and set it in the lib
    static {
        VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() {
            @Override
            public void virtualCardMessage(VirtualCardMessaging.Message message) {
                switch (message.getCode()) {
                case VirtualCardMessaging.CARD_CREATION_COMPLETED:
                    try {
                        // when card is created, then activate the card agent,
                        // then connect it to the ApduService,
                        // then ask the user if they want this app the default
                        // app
                        // for servicing the reader
                        message.getVirtualCard().activate();
                        ApduService.setVirtualCard(message.getVirtualCard());
                        myActivity.postUi("========================"
                                + "\n"
                                + "=====Card Created======="
                                + "\n"
                                + "========================"
                                + "\n"
                                + "==Card Id: "
                                + message.getVirtualCard().getVirtualCardId()
                                + "\n"
                                + "==Num: "
                                + message.getVirtualCard()
                                        .getVirtualCardNumber()
                                + "\n"
                                + "==Card Exp: "
                                + message.getVirtualCard()
                                        .getVirtualCardExpDate() + "\n"
                                + "==Card Type: "
                                + message.getVirtualCard().getVirtualCardType()
                                + "\n" + "==Card Logo: "
                                + message.getVirtualCard().getVirtualCardLogo()
                                + "\n");
                        myActivity.postUi("========================" + "\n"
                                + "=====Card Activating====" + "\n"
                                + "========================" + "\n");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "=====Card Activated=====" + "\n"
                            + "=====Ready To Tap=======" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                case VirtualCardMessaging.TRANSACTION_ENDED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "===Ready To Tap Again====" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                default:
                    break;
                }

                // log the message
                Log.d(TAG,
                        message.getMessage() + " cardId="
                                + message.getVirtualCardId() + " code="
                                + message.getCode());
                myActivity.postUi(message.getMessage() + " cardId="
                        + message.getVirtualCardId() + " code="
                        + message.getCode() + "\n");
            }

        };

        // set the default messenger
        VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myActivity = this;
        tv = (TextView) findViewById(R.id.textView1);
        tv.setMovementMethod(new ScrollingMovementMethod());

        // try to set this app as the default HCE application app
        CardEmulation cardEmulationManager = CardEmulation
                .getInstance(NfcAdapter.getDefaultAdapter(this));
        ComponentName paymentServiceComponent = new ComponentName(
                getApplicationContext(), ApduService.class.getCanonicalName());

        if (!cardEmulationManager.isDefaultServiceForCategory(
                paymentServiceComponent, CardEmulation.CATEGORY_PAYMENT)) {
            Intent intent = new Intent(CardEmulation.ACTION_CHANGE_DEFAULT);
            intent.putExtra(CardEmulation.EXTRA_CATEGORY,
                    CardEmulation.CATEGORY_PAYMENT);
            intent.putExtra(CardEmulation.EXTRA_SERVICE_COMPONENT,
                    paymentServiceComponent);
            startActivityForResult(intent, 0);
        }

        // Create a card with data from AdminApi console
        // See wiki for help:
        // http://wiki.simplytapp.com/home/using-admin-api-console
        //
        // Values are found within your AdminApi Console. Click Get APP
        // Credentials.
        //
        // card agent code hash is found inside the Issuer Entity portion of the
        // web portal
        //
        final VirtualCard card = VirtualCardBuilder
                .cardId("5901")
                .mobileAppConsumerKey(
                        "oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU")
                .mobileAppConsumerSecret(
                        "ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he")
                .walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax")
                .walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3")
                .cardAgentCodeHash("aa4185c2364048447a38aa02499a9897")
                .context(this).build();

        // now load the card...
        // this requires network connectivity and is blocking
        // so we must use AsyncTask to avoid running this in UI thread
        //
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    myActivity.postUi("=================================="
                            + "\n" + "=====Loading Card From Server====="
                            + "\n" + "=================================="
                            + "\n");
                    card.load();
                } catch (IOException ex) {
                    Log.e(TAG, "Failed to load", ex);
                }
                return (Void) null;
            }
        }.execute();

    }

}

Friday, October 3, 2014

Cryptogram calculation clarity for CBP



This is the key concept and change between EMV and what is required for CBP cryptograms.
Effectively the cryptogram in the CBP payment is created half in the cloud and then the other half when you tap the phone.

The reason for this is because the network connectivity may not allow a scenario like the left side of the diagram below as the UN would get cut off from transmitting from the POS to the calculation space, or it would be subject to network latency and create a "Tap and Hover" effect that would be an awful user experience.

The CBP cryptogram allows all components to be used as before and exposes a "relay threat" only to the android app after the SUK is received, but not to the POS terminal.   It also allows the Phase 2 calculation to happen independent of cloud availability at tap time.  The Phase 1 calculation and delivery to the phone from the cloud is expected to be preformed prior to tapping the phone, but not during the tap itself.

The thought is to offset the relay threat with in app security and sensor rich android OS.

It becomes a net neutral from a security standpoint really, IMO.