API

Zebra VAS SDK 2.0

Overview

Zebra VAS SDK is an Android library built on top of the existing Android NFC APIs and the third party VAS protocol, such as Apple VAS or Google Smart Tap. It offers streamlined configuration and management of the necessary procedures involved in accessing and processing data from an NFC pass.

The main SDK components are:

  1. ZebraNfcVas - The main class that manages all interaction with NFC passes, configures the NFC reader, and reads the pass data, whether or not from the mobile wallet.
  2. VasConfig - A base class object used to provide an interface for storing and accessing merchant parameters required to access an NFC pass. Two types of configuration objects are automatically generated when creating a VasConfig object:
    • AppleVasConfig - A VASConfig object that contains information specific to Apple VAS, used for accessing and processing passes from Apple Wallet.
    • SmartTapVasConfig - A VASConfig object that contains information specific to Google Smart Tap, used for accessing and processing passes from Google Wallet.
  3. Pass object - Encapsulates the information about the NFC pass that is read, including the payload data and supplementary metadata. The Pass object enables developers to validate the pass type and ensure that it is not a replay attack (i.e. the date and time is within 10 seconds of the actual date and time), adhering to Apple requirements.

This section offers guidance on developing an application to read NFC passes using the Zebra VAS SDK. A single application can be built to access and read Apple Wallet, Google Wallet, or other NFC passes.


Install SDK

Perform the steps below to install the SDK and prepare to run the developed application on the device:

  1. Download Zebra VAS SDK from Zebra Support Portal and extract the files from the download.
  2. Import the Android .AAR file into the "libs" folder of the Android application project.
  3. Install the Zebra VAS Service .APK on the supported Zebra mobile computer. This is required to establish communication between the SDK and the Zebra device via the Zebra VAS Service.

Upgrade from Previous SDK

For apps developed with prior version VAS SDK v1.0, there are some APIs that have been updated or removed. Refer to the changes in the table below:

Original API
(VAS SDK v1.0)
Replacement API
(VAS SDK v2.0)
Description
createAppleVasConfig() AppleVasConfig avc = new AppleVasConfig; Create an AppleVasConfig object.
setPassTypeIds() AppleVasConfig avc = new AppleVasConfig();
avc.setPassTypeIds();
This method is moved to AppleVasConfig class. The AppleVasConfig object must be created before calling this method.
setPrivateKey() AppleVasConfig avc = new AppleVasConfig();
avc.setPrivateKey();
This method is moved to AppleVasConfig class. The AppleVasConfig object must be created before calling this method.
setFilter() AppleVasConfig avc = new AppleVasConfig();
avc.setFilter();
This method is moved to AppleVasConfig class. The AppleVasConfig object must be created before calling this method.
setMerchantURL() AppleVasConfig avc = new AppleVasConfig();
avc.setMerchantURL();
This method is moved to AppleVasConfig class. The AppleVasConfig object must be created before calling this method.
setVasConfig() AppleVasConfig avc = new AppleVasConfig();
ZebraNfcVas VasSdk = new ZebraNfcVas(Context);
VasSdk.setVasConfig(avc);
VasConfig is an abstract class extended by AppleVasConfig. A ZebraNfcVas object must be created, then pass the AppleVasConfig object.
setVasConfigs() ZebraNfcVas VasSdk = new ZebraNfcVas(Context);
VasSdk.setVasConfigs(Arrays.asList(new VasConfig[]{az_cfg, z_cfg}));
VasConfig is an abstract class extended by AppleVasConfig. A ZebraNfcVas object must be created, then pass the list of AppleVasCOnfig objects. This allows multiple objects to be set at the same time.
getType() AppleVasConfig avc = new AppleVasConfig();
avc.getType();
This method is moved to AppleVasConfig class. The AppleVasConfig object must be created before calling this method.
getMerchantUrl() AppleVasConfig avc = new AppleVasConfig();
Avc.getMerchantURL()
This method is moved to AppleVasConfig class. The AppleVasConfig object must be created before calling this method.
getTerminalMode() AppleVasConfig avc = new AppleVasConfig();
Avc.getTerminalMode()
This method is moved to AppleVasConfig class. The AppleVasConfig object must be created before calling this method.

Xamarin Support

To use Zebra VAS SDK in Xamarin, convert "zebranfcvas-release.aar" to a .DLL file. Refer to instructions from Microsoft to bind an .AAR. If .DLL creation fails with errors during the build, resolve the issue by removing TerminalMode with the following change to Metadata.xml, then binding the library:

    <remove-node
        path="/api/package[@name='com.zebra.nfcvas']/class[@name='ZebraNfcVas.TerminalMode']">
    </remove-node>

Terminal Mode is reserved for further implementation and future expansion. Removing it does not cause any harm. Zebra mobile computers are VAS_Only terminals; payment is not supported.


Developer Guide

Zebra VAS SDK is capable of reading Apple Wallet passes (Apple VAS) and Google Wallet (Google Smart Tap) passes. This section provides guidance for developers to build apps based on the type of pass being read. Refer to the sample demo app and corresponding source code.

Apple VAS

The following provides guidance to develop an application that reads an Apple Wallet NFC pass:

  1. Implement IZebraServiceConnection Interface - It is required to implement IZebraServiceConnection Interface to receive callback methods from the Zebra NFC VAS service on the service connection/disconnection and NFC pass object. The 3 callback methods are: onServiceConnected, onServiceDisconnected, and onPassDetected.

    public class AppleVASActivity extends AppCompatActivity implements IZebraServiceConnection {
        ...
    }
    

    Replace "AppleVASActivity" with the name of the class implemented within your application.

  2. Create a ZebraNFCVAS object and intialize the object - ZebraNfcVas object should be declared in onCreate() and initialized by calling init().

    private ZebraNfcVas vasSDK;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init(); //UI Initialization
    
    
    vasSdkInit();
    
    } private void vasSdkInit() { Log.e(TAG, "vasSdkInit"); vasSDK = new ZebraNfcVas(this); vasSDK.init(); }
  3. After initialization, a callback to onServiceConnection is available. Proceed with the subsequent steps for implementation of this method.

    @Override
    public void onServiceConnection() {
        Log.e(TAG,"onServiceConnection");
        //Start with the app implemenation
    }
    

    a. Create an AppleVasConfig object - This is the object that the SDK uses to interact with a presented NFC pass.

    AppleVasConfig av_cfg = new AppleVasConfig();
    

    b. Set the appropriate configuration values in the AppleVasConfig object by using the following APIs:

    • av_cfg.setPassTypeIds(String[] ptis)
    • az_cfg.setPrivateKey(PrivateKeyStr)
    • av_cfg.setFilter(byte[] filter, ZebraNfcVas.FilterType)
    • av_cfg.setMerchantURL(String url)

    Sample code (Note: Replace Merchant URL, Pass Type ID, and private key with your specific values):

    az_cfg.setPrivateKey(ZebraPrivateKeyStr);
    az_cfg.setFilter(new byte[]{(byte) 0xaa, 0x55, (byte) 0xaa, 0x55}, ZebraNfcVas.FilterType.ExactMatch);
    az_cfg.setMerchantURL("https://www.zebra.com/vas-signup");
    az_cfg.setPassTypeIds(new String[]{"pass.com.pronto.zebra-wallet-pass.demo"});
    

    c. Create the ZebraNFCVAS object. This holds the instance of a VasConfig object for pass detection operation.

    vasSDK.createZebraNfcVasConfig();
    

    d. Set the AppleVasConfig object into the ZebraNFCVAS object - This informs the SDK about the configuration data to use when reading the NFC pass. Multiple VasConfigs or a single VasConfig can be provided.

    Multiple VasConfigs:

    vasSDK.setVasConfigs(List<VasConfig>);
    

    Single VasConfig:

    vasSDK.setVasConfig(av_cfg);
    

    e. Call connectToReader - This establishes the connection with the NFC reader.

    vasSDK.connectToReader();
    

    f. Perform one of the following based on your requirement. Only read capability is enabled and polling is configured to read NfcA, NfcB, NfcV, and IsoDep tags:

    • If no additional tags are required to be read, call enableReadPassMode():

      vasSDK.enableReadPassMode();
      
    • If NfcF or Ndef tags are additionally required to be read, call enableReadPassMode() with parameters:

      enableReadPassMode(boolean doSkipNdef, boolean doEnableNFC_F);
      

      For NfcF tags, set "doEnableNFC_F" to "true."
      For Ndef tags, set "doSkipNdef" to "false." Note: Performance could be affected since an NDEF check is performed along with additional checks from the OS after initialzing the tag communication. This affects the processing time of the NFC pass. The advantage of this is the OS reads the NDEF message and it is be enumerated in the Tag object, which can be easily be accessed.

    g. When an NFC pass is read, onPassDetected() is called - This method receives the Pass object when a pass is read. Process the data read from the pass based on business needs.

    public void onPassDetected(Pass pass) {
        Log.v(TAG, ">>> PASS DETECTED <<<");
        // Process data read from the pass
    }
    

    h. Call disableReadPassMode before the app goes to the background or when the activity ends - This returns the device to the default NFC Forum polling mode.

    @Override
    public void onPause() {
        super.onPause();
        Log.i(TAG, "onPause called & Reader Mode Deactivated .....");
        vasSDK.disableReadPassMode();
    }
    

Google Smart Tap

The following provides guidance to develop an application that reads a Google Wallet NFC pass:

  1. Implement IZebraServiceConnection Interface - It is required to implement IZebraServiceConnection Interface to receive callback methods from the Zebra NFC VAS service on the service connection/disconnection and NFC pass object. The 3 callback methods are: onServiceConnected, onServiceDisconnected, and onPassDetected.

    public class AppleVASActivity extends AppCompatActivity implements IZebraServiceConnection {
        ...
    }
    

    Replace "AppleVASActivity" with the name of the class implemented within your application.

  2. Create a ZebraNFCVAS object and intialize the object - ZebraNfcVas object should be declared in onCreate() and initialized by calling init().

    private ZebraNfcVas vasSDK;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init(); //UI Initialization
    
    
    vasSdkInit();
    
    } private void vasSdkInit() { Log.e(TAG, "vasSdkInit"); vasSDK = new ZebraNfcVas(this); vasSDK.init(); }
  3. After initialization, a callback to onServiceConnection is available. Proceed with the subsequent steps for implementation of this method.

    @Override
    public void onServiceConnection() {
        Log.e(TAG,"onServiceConnection");
        //Start with the app implemenation
    }
    

    a. Create an SmartTapVasConfig object - This is the object that the SDK uses to know how to interact with a presented NFC pass.

    SmartTapVasConfig g_cfg = new SmartTapVasConfig();
    

    b. Set the appropriate configuration values in the AppleVasConfig object by using the following APIs:

    • g_cfg.setCollectorID(String collID)
    • g_cfg.setPrivateKey(String PrivateKeyStr, int PrivateKeyVersion);

    Optionally, additional values can be configured:

    • g_cfg.setMerchantNameNdefRecord(NdefRecord record)
    • g_cfg.setStoreLocationIdNdefRecord(NdefRecord record)
    • g_cfg.setServiceTypes(ServiceType[] type)

    Sample code (Note: Replace Collector ID, NDEF record, store location ID NDEF record, and private key with your specific values):

    g_cfg.setCollectorID("12703788");    // decimal value
    g_cfg.setPrivateKey(DemoPrivateKeyStr, DemoPrivateKeyVersion);//smartPrivateKey
    g_cfg.setMerchantNameNdefRecord(rec);
    g_cfg.setStoreLocationIdNdefRecord(rec);
    g_cfg.setServiceTypes(new SmartTapVasConfig.ServiceType[]{SmartTapVasConfig.ServiceType.AllExceptPPSE});
    g_cfg.setPrivateKey(ZebraPrivateKeyStr, ZebraPrivateKeyVersion);
    

    c. Create the ZebraNFCVAS object. This holds the instance of a VasConfig object for pass detection operation.

    vasSDK.createZebraNfcVasConfig();
    

    d. Set the AppleVasConfig object into the ZebraNFCVAS object - This informs the SDK about the configuration data to use when reading the NFC pass. For Google, only a single configuration is supported at a time.

    vasSDK.setVasConfig(VasConfig);
    
    • Sample code to configure only Google Smart Tap:

      vasSDK.setVasConfig(g_cfg);

    Since Apple supports multiple configurations, the following applies if configuring both Apple VAS and Google Smart Tap at the same time:

    vasSDK.setVasConfigs(List<VasConfig>);
    
    • Sample code to configure both Apple VAS and Smart Tap configurations at the same time:

      vasSDK.setVasConfigs(Arrays.asList(new VasConfig[]{az_cfg, g_cfg}));

    e. Call connectToReader - This establishes the connection with the NFC reader.

    vasSDK.connectToReader();
    

    f. Perform one of the following based on your requirement. Only read capability is enabled and polling is configured to read NfcA, NfcB, NfcV, IsoDep, and GOOGLE_SMART_TAP tags:

    • If no additional tags are required to be read, call enableReadPassMode():

      vasSDK.enableReadPassMode();
      
    • If NfcF or Ndef tags are additionally required to be read, call enableReadPassMode() with parameters:

      enableReadPassMode(boolean doSkipNdef, boolean doEnableNFC_F);
      

      For NfcF tags, set "doEnableNFC_F" to "true."
      For Ndef tags, set "doSkipNdef" to "false." Note: Performance could be affected since an NDEF check is performed along with additional checks from the OS after initialzing the tag communication. This affects the processing time of the NFC pass. The advantage of this is the OS reads the NDEF message and it is be enumerated in the Tag object, which can be easily be accessed.

    g. When an NFC pass is read, onPassDetected() is called - This method receives the Pass object when a pass is read. Process the data read from the pass based on business needs.

    public void onPassDetected(Pass pass) {
        Log.v(TAG, ">>> PASS DETECTED <<<");
        // Process data read from the pass
    }
    

    h. Call disableReadPassMode before the app goes to the background or when the activity ends - This returns the device to the default NFC Forum polling mode.

    @Override
    public void onPause() {
        super.onPause();
        Log.i(TAG, "onPause called & Reader Mode Deactivated .....");
        vasSDK.disableReadPassMode();
    }
    

ZebraNfcVAS

ZebraNfcVAS is the main class of the SDK that establishes a connection with the Zebra NFC VAS service, handles all the communication with the NFC passes, configures the NFC reader, and reads the pass from the Apple or Google Wallet (if in use). This object must be explicitly created and initialized. After the pass is successfully read, ZebraNfcVas decrypts the data (given that a private key is provided and all information is correct and intact) and returns the Pass object back to the caller. If a private key is not provided, the SDK returns the encrypted data. The following subsections detail the available methods.

init

    public void init()

Initializes and binds to the Zebra NFC VAS Service.

Parameter: None

Return value: None

Throws: RuntimeException if the service is not available or the security/signature does not match.

onServiceConnected

This is a callback method invoked when Zebra NFC VAS service is connected. IZebraServiceConnection interface must be implemented. When init() is called, it tries to connect to the Zebra NFC VAS service. If successful, it calls onServiceConnected.

onServiceDisconnected

This is a callback method invoked when Zebra NFC VAS service is disconnected. IZebraServiceConnection interface must be implemented. When init() is called, it tries to connect to the Zebra NFC VAS service. If unsuccessful, it calls onServiceDisconnected.

setVasConfig

    public void setVASConfig()

Specifies the VASConfig object to be used for interacting with the passes. Prior to using this method, createAppleVasConfig() must be called to create the VasConfig object. To retrieve or pass data, call createZebraVasConfig().

Parameter: None

Return value: None

Throws: InvalidParameterException or RuntimeException if VasConfig object is null.

setVasConfigs

    public void setVASConfigs()

Specifies the VASConfig objects to be used for interacting with the passes. Prior to using this method, createAppleVasConfig() must be called to create the VasConfig object. To retrieve or pass data, call createZebraVasConfig().

Parameter: None

Return value: None

Throws: InvalidParameterException or RuntimeException if VasConfig object is null.

clearConfigs

    public void clearConfigs()

Clears the VASConfig object that is used for interacting with passes. After clearing the object, data from the NFC passes cannot be retrieved until calling: setPrivateKey(), setFilter(), setPassIds(), and setVasConfig().

Parameter: None

Return value: None

Throws: RuntimeException if VasConfig object is null.

createZebraVasConfig

    public void createZebraVasConfig()

Creates the ZebraConfig object and holds the instance of a VasConfig object for pass detection operation.

Parameter: None

Return: None

connectToReader

    pubilc void connectToReader()

Obtains a connection to the NFC adapter.

Parameter: None

Return value: Returns error code indicating success or failure:

  • ERROR_NFC_IS_DISABLED - the NFC adapter is disabled
  • ERROR_NO_NFC_SUPPORT - the hardware does not have NFC support
  • ERROR_SUCCESS - connection is successful

enableReadPassMode

    pubilc void enableReadPassMode()

Limits the NFC controller to reader mode, disabling card emulation. Polling is configured for the Zebra NFC reader to read NFC Type A and Type B cards.

IMPORTANT NOTE: Since the NFC resources are fully allocated, no other application (running on the foreground or background) can request for NFC resources.

Parameter: None

Return value: None

disableReadPassMode

    public void disableReadPassMode()

Returns to default NFC Forum polling mode. This method should be called whenever the app moves to the background or when the activity is terminated.

Parameter: None

Return value: None

onPassDetected

    public void onPassDetected(Pass pass)

The interface method onPassDetected is a callback that gets invoked when an NFC pass is read. The SDK passes the NFC pass and identifies whether the pass is encrypted or decrypted. Pass is a class where all Pass data can be accessed via getter methods:

  • public String getStatusWord() – Returns the StatusWord of the NFC pass detected
  • public Result getResultCode() - Returns the ResultCode of the NFC pass detected
  • public byte[] getMerchantId() - Returns the MerchantID (Pass Type Identifier converted to SHA256) of the NFC pass detected
  • public String getPassTypeId() - Returns the PassTypeID of the NFC pass detected
  • public Type getPassType() - Returns the PassType of the NFC pass detected
  • public byte[] getPayloadMessage() - Returns the PayloadMessage of the NFC pass detected
  • public Date getPayloadDate() - Returns the PayloadDate of the NFC pass detected
  • public boolean isDecrypted() – Returns the status of the payload message: decrypted or encrypted

Parameter: Pass object

Return value: None

getSdkVersion

    public String getSdkVersion()

Returns the version of the SDK formatted as MAJ.MIN.BUG.

Parameter: Activity activity - This is the activity that registered for the NFC intent Response resp, the class that has implemented the response interface and receives the pass data.

Return value: String - the SDK version in MAJOR.MINOR.BUG_FIX format.


VasConfig

This is the abstract base class (interface) for a VAS configuration object and is not instantiated by itself. It contains information for accessing and interpreting a NFC pass. The configuration object contains one or more Pass Type Identifier(s), which identifies the pass to be returned, and may contain an associated private key, used to decrypt data from the pass. The following subsections detail the available methods.

NOTE: Google Smart Tap supports only 1 configuration object. Apple VAS supports 1 or more configuration objects.

enum VasType 

Identifies the type of VasConfig object as one of two possible values:

  • APPLE_VAS - Apple VAS configuration object
  • GOOGLE_SMART_TAP - Google Smart Tap configuration object
  • UNKNOWN - Other NFC-type configuration object, including ISO or NDEF-formatted

getType

    public VasType getType() 

Returns the type of the VASConfig object.

Return value: One of the following:

  • VasType.APPLE_VAS - Apple VAS configuration object
  • VasType.GOOGLE_SMART_TAP - Google Smart Tap configuration object
  • UNKNOWN - Other NFC-type configuration object, including ISO or NDEF-formatted

setPrivateKey (Apple VAS)

    public void setPrivateKey(String key) 

This method only applies to Apple VAS. Sets the EC private key in PEM format. The private key must be properly formatted or decrypted and cannot be null or an empty string. For example:

    -----BEGIN EC PARAMETERS-----\n 
    alkidnklde==\n 
    -----END EC PARAMETERS-----\n 
    -----BEGIN EC PRIVATE KEY——\n 
    aslkdflaksdfjfkk328klxld…\n 
    ak7ujvbn9b8o3bv9hae98fn2…\n 
    9asjh90fuh34n2uh33==\n 
    -----END EC PRIVATE KEY-----\n 

Parameters: String key

Return values: None

Throws: InvalidParameterException if the parameter is null or the string is empty.


setPrivateKey (Google Smart Tap)

     public void setPrivateKey(String key, int Version) 

This method only applies to Google Smart Tap. Sets the EC private key in PEM format. The private key must be properly formatted or decrypted and cannot be null or an empty string. For example:

    -----BEGIN EC PARAMETERS-----\n 
    alkidnklde==\n 
    -----END EC PARAMETERS-----\n 
    -----BEGIN EC PRIVATE KEY——\n 
    aslkdflaksdfjfkk328klxld…\n 
    ak7ujvbn9b8o3bv9hae98fn2…\n 
    9asjh90fuh34n2uh33==\n 
    -----END EC PRIVATE KEY-----\n 

Parameters:

  • String key
  • Int Version 

Return values: None

Throws: InvalidParameterException if the parameter is null or the empty string.

See best practices for Google Smart Tap Private Key Management.


AppleVASConfig

AppleVASConfig is the configuration class object for Apple NFC passes. It is inherited from VasConfig and contains parameters specific to Apple VAS for accessing and processing Apple Wallet passes. This configures the criteria used to choose which passes to access and read. The passes that are read must match the configuration parameters specified by the AppleVASConfig object. The following subsections detail the available methods.

constructor

    public void AppleVASConfig()

Creates and initializes a new AppleVasConfig object containing details specific to Apple VAS.

setPassTypeIds

    public void setPassTypeIds(String[] passTypeIds)

Sets one or more Pass Type Identifiers in the app prior to reading the NFC pass. This overwrites any pre-existing values. The Pass Type Identifier is provided by Apple, Google, or the issuer of the NFC pass. The app uses these unique string values as identifiers to ensure that when it reads the NFC pass, it can verify whether the pass type identifier matches the one that was used during the NFC pass creation process. These identifiers are typically in the form 'com.companyname.pass' or other similar reverse domain string. When an NFC reader specifies one or more Pass Type Identifiers, only passes that match those identifiers are retrieved.

Parameters: String[] passTypeIds
This is an array of one or more Pass Type IDs. For example, {“pass.com.zebra”} or {“pass.com.zebra”, “pass.com.xyz.somethingspecific”}. When attempting to read a pass, the pass being read is checked against each Pass Type ID in the array.

Return value: None

Throws:

  • InvalidParameterException when there are null or empty strings in the array, invalid characters, or the array is empty in any of the Pass Type IDs.
  • UnsupportedEncodingException when a UTF-8 conversion encoding error occurs.
  • NoSuchAlgorithmException when a SHA256 conversion error occurs.

setFilter

    public void setFilter(byte[] filter, FilterType type)

Optional: Sets the filter information to be read for the NFC pass. The filter is supplied by the merchant of the NFC pass. This overwrites any pre-existing value. A company may issue passes for different reasons and/or uses. The filter offers the ability to issue numerous similar passes with distinguiahsable variations.

A sample use case could involve a company issuing a season of tickets to a sporting event. In this case, all the tickets originate from the same company, with each ticket sharing similarities except for access date, which serves as the sole distinguishing factor. This distinguishing factor is used as the filter to identify any combination of dates. For example, consider a scenario where there are 4 games taking place on different dates. The developer could assign each game a specific within the date filter. The Zebra VAS SDK SDK can support all 4 games by determining which bit is set. When using the NFC reader, a filter can be specified with one bit set for the current game, and it can be designated as a bit-mask. By doing so, any pass with the corresponding bit set for the present date can be read, while passes without the bit set can be skipped.

Parameters:

  • byte[] filter - This is a 4-byte array that is the filter value used during the pass retrieval process.
  • FilterType type - This is one of two constants:
    • FILTER_TYPE_EXACT_MATCH - When the specified filter value exactly matches the merchant pass filter value, the match is successful.
    • FILTER_TYPE_BIT_MASK - When the specified filter value is bitwise ANDed with the merchant pass filter value and the result equals the merchant pass filter value, the match is successful.

Return value: None

Throws: InvalidParameterException if the filter value is not exactly 4 bytes in size.

setFilterNone

    public void setFilterNone()

Clears the filter for any filter value in a pass (including no filter value) to be accepted.

setMerchantURL

    public void setMerchantURL(String url)

Optional: Sets the VAS Merchant URL where the Apple device should be directed if no pass is currently available on the device. This overwrites any pre-existing value.

Passes are often used as loyalty cards. In situations where a customer is not yet a loyalty member, the user could simply tap the reader, and the Merchant URL can be transmitted to the device, guiding the user to a sign-up page. This implementation requires specific server APIs and processes that are beyond the scope of this document.

Parameter: String url
This is the merchant URL string for the associated pass, which is generally used to sign-up for a pass. It must include "https://". The maximum string length is 64 bytes.

Return value: None

Throws: InvalidParameterException if the length is too long or does not contain a valid "https://" header.

getPassTypeIds

    public String[] getPassTypeIds()

Gets the list of currently configured Pass Type IDs in string format.

Parameters: None

Return values:

  • String[] of Pass Type Ids.
  • null, if no passTypeIds have been set.

getMerchantIds

    public byte[][] getMerchantIds()

Get the list of currently configured Merchant IDs as byte arrays, where the merchant ID is the the Pass Type ID converted to SHA256 format.

Parameters: None

Return values:

  • byte[][] of Merchant Ids
  • null, if no passTypeIds have been set.

getFilter

    public byte[] getFilter()

Get the currently configured filter

Parameters: None

Return values:

  • byte[] of the filter in exactly 5 bytes
  • null, if no filter has been set

The filter[5] contains the filter type (0x01 is exact match, 0x02 is bit_mask).

getMerchantURL

    public String getMerchantURL()

Gets the Merchant URL string which the Apple device should be directed if no pass is currently available on the device. Generally this is provided for the user to sign-up for a pass.

Parameter: None

Return value:

  • If successful, returns the string of the merchant URL.
  • If unsuccessful and no merchant URL has been set, returns null.

getTerminalMode

    public AppleVasConfig.TerminalMode getTerminalMode()

Returns the private key currently configured, in PEM format.

Parameters: None

Return values: This always returns TerminalMode.VasOnly

getProtocol

    public AppleVasConfig.VasProtocol getProtocol()

Returns the protocol currently configured, in PEM format

Parameters: None

Return values: This always returns VasProtocol.FullVASProtocol


SmartTapVasConfig

SmartTapVasConfig is the configuration class object for Google Wallet passes. It is inherited from VASConfig and contains the parameters specific to Google Smart Tap for accessing and processing a Google Wallet passes. This configures the criteria used to choose which passes to access and read. The passes that are read must match the configuration parameters specific by the SmartTapVasConfig object. See Google Wallet documentation for reference. The following subsections detail the available methods.

constructor

    public void SmartTapVasConfig()

Creates and initializes a new VAS config object containing details specific to Google Smart Tap.

setCollectorID

    public void setCollectorID(String collId)

Specifies the single Collector ID associated with the pass(es) to be read. This is mandatory and serves as the primary value that is used to match with a NFC pass on the Android device. The pass on the Android device must contain a matching Collector ID. Decimal number identifiers are assigned to each pass issuer for identification purposes. Only passes that match the Collector ID specified are accepted. This overwrites any pre-existing values.

Parameters: String collectorId This is one Collector ID.  Only one Collector ID is allowed

Return values: None

Throws:

  • InvalidParameterException if the array is empty, there are null or empty strings in the array, or invalid characters in any of the pass Type Ids.
  • UnsupportedEncodingException if the UTF-8 conversion encoding is not available. UTF-8 conversion encoding is required to convert the PassTypeID string into byte encoding for the MerchantID calculation.
  • NoSuchAlgorithmException if the SHA-256 algorithm is not available.  This is required to automatically convert the PassTypeID to the equivalent MerchantID.

setServiceTypes

    public void setServiceTypes(ServiceType[] types)

Optional: Specifies an array of pass types that shall be accepted. One or more types may be specified, for example: Loyalty and/or GiftCard. The default value is "AllExceptPPSE". This overwrites any pre-existing values. See enum ServiceType for all possible values.

Parameters: ServiceType[] types Array of one or more ServiceTypes. This must not be null and or empty.

Return values: None

Throws: InvalidParameterException if the array is empty, there are null or empty strings in the array, or there are invalid characters in any of the pass Type Ids.

Sample code:

    // Create a GoogleSmartTap Config object
    SmartTapVasConfig z_cfg = new SmartTapVasConfig();    
          
    // Set the service type. This is optional - if not specified, AllExceptPPSE is used (method example for this setServiceTypes)
    z_cfg.setServiceTypes(new SmartTapVasConfig.ServiceType[]{SmartTapVasConfig.ServiceType.AllExceptPPSE});

enum ServiceType

    public enum ServiceType{
        AllServices("All Services", 0x00),
        AllExceptPPSE("All Excpet PPSE", 0x01),
        Loyalty("Loyalty", 0x03),
        Offer("Offer", 0x04),
        GiftCard("Gift Card", 0x05),
        PrivateLabelCard("Private Label Card", 0x06),
        EventTicket("Event Ticket", 0x07),
        Flight("Flight", 0x08),
        Transit("Transit", 0x09),
        CloudBasedWallet("Cloud Based Wallet", 0x10),
        MobileMarketingPlatform("Mobile Marketing Platform", 0x11),
        Generic("Generic", 0x12),
        GenericPrivatePass("Generic Private Pass", 0x13),
        WalletCustomer("Wallet Customer", 0x40);

    }

setPosCapabilitiesArray

    public void setPosCapabilitiesArray(byte[] posCapabilities)

Optional: Specifies a 5-byte array that indicates the POS capabilities. Data read from the NFC pass must match this data for validation. This overwrites any pre-existing values.

Parameters: byte[] posCapabilities (must be 5 bytes)

Return values: None

Throws: InvalidParameterException if the array is null or not 5 bytes long.

setStoreLocationIdNdefRecord

    public void setStoreLocationIdNdefRecord(NdefRecord storeLocationIdRecord)

Optional: Specifies the store location ID NDEF record to be placed into the Merchant data based on Google specifications. Data read from the NFC pass must match this data for validation. This overwrites any pre-existing values.

Parameters: NdefRecord storeLocationIdRecord

Return values: None

Throws: InvalidParameterException if the NDEF record is NULL.

Sample code:

    // Create a GoogleSmartTap Config object
    SmartTapVasConfig z_cfg = new SmartTapVasConfig();
    z_cfg.setCollectorID("95011179");

    // EXAMPLE - OPTIONAL FIELD - create an NDEF Record for the location ID in the Merchant NDEF Record
    ByteBuffer bb = ByteBuffer.allocate(4);
    byte [] locId = bb.putInt(12345).array();
    rec = new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, new byte[]{(byte) 0x6c, (byte) 0x69, (byte) 0x64}, // 'lid' in byte form
        null, concatenateByteArrays(new byte[]{(byte) 0x04}, // payload format byte
        locId ));
    z_cfg.setStoreLocationIdNdefRecord(rec);

setTerminalIdNdefRecord

    public void setTerminalIdNdefRecord(NdefRecord terminalIdRecord)

Optional: Specifies the terminal ID NDEF record to be placed into the Merchant data based on Google specifications. Data read from the NFC pass must match this data for validation. This overwrites any pre-existing values.

Parameters: NdefRecord terminalIdRecord

Return values: None

Throws: InvalidParameterException if the NDEF record is NULL.

setMerchantNameNdefRecord

    public void setMerchantNameNdefRecord(NdefRecord merchantNameRecord)

Optional: Specifies the merchant name NDEF record to be placed into the Merchant data based on Google specifications. Data read from the NFC pass must match this data for validation. This overwrites any pre-existing values.

Parameters: NdefRecord merchantNameRecord

Return values: None

Throws: InvalidParameterException if the NDEF record is NULL.

Sample code:

    // Create a GoogleSmartTap Config object
    SmartTapVasConfig z_cfg = new SmartTapVasConfig();
    z_cfg.setCollectorID("95011179");

    // EXAMPLE - OPTIONAL FIELD - create an NDEF Record for the Merchant name in the Merchant NDEF Record
    byte[] name = new String("Sample Merchant").getBytes(StandardCharsets.UTF_8);
    byte[] strHeader = new byte[] {(byte) 0x02, (byte) 0x65, (byte) 0x6E};
    NdefRecord rec = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[]{(byte) 0x6d,(byte) 0x6e, (byte) 0x72}, // 'mnr' in byte form
    concatenateByteArrays(strHeader, name));
    z_cfg.setMerchantNameNdefRecord(rec);

setMerchantCategoryNdefRecord

    public void setMerchantCategoryIdNdefRecord(NdefRecord merchantCategoryRecord)

Optional: Specifies the merchant category NDEF record to be placed into the Merchant data based on Google specifications. Data read from the NFC pass must match this data for validation. This overwrites any pre-existing values.

Parameters: NdefRecord merchantCategoryRecord Return values: None Throws: InvalidParameterException if the NDEF record is NULL.

getCollectorIDHexStr

    public String getCollectorIDHexStr()

Returns the Collector ID as a string in hex format.

Parameters: None

Return values:

  • String collectorID as a hex string.
  • null, if no Collector IDs have been set.

getCollectorIDInt

    public int getCollectorIDInt()

Returns the Collector ID in integer format.

Parameters: None

Return values: Integer format of Collector ID. Null if no Collector IDs have been set.

getPosCapabilitiesArray

    public byte[] getPosCapabilitiesArray()

Optional: Returns the 5-byte array that indicates the POS capabilities. This is set from setPosCapabilitiesArray() method.

Parameters: none

Return values: byte[] - 5 byte array with the POS capability masks

getStoreLocationIdNdefRecord

    public NdefRecord getStoreLocationIdNdefRecord()

Optional: Returns the store location ID NDEF record. This is set from setStoreLocationIdNdefRecord() method.

Parameters: none

Return values: NdefRecord storeLocationIdNdefRecord

getTerminalIdNdefRecord

    public void getTerminalIdNdefRecord()

Optional: Returns the terminal ID NDEF record. This is set from setTerminalIdNdefRecord() method.

Parameters: none

Return values: NdefRecord terminalIdNdefRecord

getMerchantNameNdefRecord

    public void getMerchantNameNdefRecord()

Optional: Returns the merchant name NDEF record. This is set from setMerchantNameNdefRecord() method.

Parameters: none

Return values: NdefRecord merchantNameNdefRecord

getMerchantCategoryNdefRecord

    public void getMerchantCategoryIdNdefRecord()

Optional: - Returns the merchant category NDEF record. This is set from setMerchantCategoryIdNdefRecord().

Parameters: none

Return values: NdefRecord merchantCategoryRecord


Security

Zebra VAS SDK implemented advanced security features to help protect the private key operation and communication between the authorized Zebra NFC VAS service and the line-of-business application. Since the Zebra NFC VAS service requires installation via the supplied .APK file, the enhanced security mechanism is designed to ensure the application connects with a genuine Zebra NFC VAS service.

The Zebra VAS SDK incorporates security measures to ensure the authenticity of the Zebra NFC VAS service. This includes but is not limited to the following:

  • Since the Zebra VAS Service is not part of the device system software (OS), the Zebra NFC VAS service is signed by a Zebra key (unprivileged) to ensure it is obtained from a trusted source.
  • The Zebra VAS SDK validates the Zebra NFC VAS service by checking the signature of the service.
  • The Zebra NFC VAS service source code and binary are secured with an obfuscation tool.

Considering the SDK is an Android .AAR file that is imported into the application project, it is possible to decompile the .AAR file and regenerate it into a modified version that connects to a malicious service. This could compromise the security and encryption of the private key, as well as other essential data, introducing potential security vulnerabilities.

Zebra recommends application developers to implement the following integrity check to ensure the authenticity of Zebra VAS SDK: To ensure the authenticity of the Zebra VAS SDK, check whether the .AAR file checksum matches the checksum of the SDK provided by Zebra. The SDK is posted on the Zebra support portal along with its file checksum details. To run the checksum of the SDK, Zebra recommends using any free “sha512sum” verification tool. Compare the result checksum with the checksum for Zebra VAS SDK (.AAR), which is the following for version 2.0.1:

    VAS SDK (.aar) version: 2.0.1
    sha512 check sum:
    a4089c74612aacd5b5cb45ac380d1d5bd53e0e6e9f4923f2dca50fcf61c14411123df67ffcdcf203bea1ab91ff38d7cd4f61243ee31945682a60a6818d0925e7

Best Practices

Google Smart Tap

Private Key Management

Zebra recommends to follow Google’s specified guidelines for private key management. Effective key management is crucial to ensure the security of the Smart Tap protocol and business application. This guide includes the following sections:

  • Key Generation
  • Key Injection
  • Key Versioning
  • Key Rotation
  • Key Storage

Note: The term “Terminal vendor” used within this section refers to any entity that purchases and sells Zebra devices.

ALL INFORMATION IN THIS SECTION IS SOURCED BY GOOGLE, WITH MINOR MODIFICATIONS FROM ZEBRA


Key Generation

Google recommends the long-term key pair to be generated outside of the Zebra terminal and then injected into the terminal. This is because the same key is shared by all the terminals for a given merchant. The key pair can be generated by the merchant or by the terminal vendor on behalf of the merchant. The private key should always be kept secret using standard best practices. If the terminal vendor generates the key pair and injects the key on behalf of the merchant, only the public key should be shared with the merchant.


Key Injection

The long-term private key can be injected into the Zebra terminal in multiple ways:

  • The terminal vendor can inject the private key using remote key injection.
  • The terminal vendor can encrypt the private key using a key encryption key, provide the encrypted private key to the merchant, and the merchant can configure the terminal with the encrypted private key as a parameter. The terminal can use a decryption key that's already in the terminal to decrypt the encrypted private key.
  • The private key is within an encrypted file that can be securely loaded onto the terminal.
  • The private key is injected within a secure facility and the terminals are hot-swapped.


Key Versioning

To aid in key rotation, a key version must be defined with the long-term public key. Multiple long-term public keys can be in production at the same time. The key version is sent as a binary hex value during Smart Tap authentication.


Key Rotation

Key rotation involves the following steps:

  1. Upload a new public key to Google Wallet
  2. Allow 24 hours for the new public key to be synced to the users' mobile devices
  3. Update all terminals to use the new private key
  4. Revoke the old public key from the Google Wallet platform

Key rotation requires that some terminals use old keys while newer terminals are using new keys. Both long-term public keys (old and new) are uploaded to Google Wallet. Both keys are then synced to the users' mobile devices. A user can tap at a terminal using an old key or a new key while the rotation is in-progress. Once the final terminal has been updated, the old long-term public key can be removed from Google Wallet.

Note: To maintain speedy turnarounds with key rotations, especially when an unexpected compromise occurs, Google strongly recommends merchants have a system in place that allows them to update the private key used on all terminals in less than 24 hours.

Note: Zebra's MX platform exposes functionality to install certificates, see Certificate Manager. To deploy a public certificate along with a private key, Zebra recommends using a certificate container (.PFX) file.

Recommended Rotations

Google recommends having three valid public keys on the Google Wallet platform at any time and suggests performing a daily rotation. The following steps outline an example rotation scenario based on these recommendations. First, create the following initial configuration:

  • Three public keys are uploaded to Google Wallet: A, B, and C.
  • Terminals are configured to use A's private key.
  • Private keys B and C are stored securely outside the terminals.

Next, perform a rotation:

  1. Generate a new key pair, D.
  2. Upload D's public key to Google Wallet.
  3. Store D's private key securely outside the terminals.
  4. Configure terminals to use B's private key.
  5. Remove A's public key from Google Wallet.

Assume that less than 24 hours after this rotation is complete, private key B (currently in use) has become compromised. The following steps outline an example rotation scenario for compromised keys:

  1. Configure terminals to use C's private key.
  2. Revoke B's public key from Google Wallet.
  3. Generate a new key pair, E.
  4. Upload E's public key to Google Wallet.
  5. Store E's private key securely outside the terminals.

The terminals must first be configured to use a new private key before an existing public key is revoked from Google Wallet. Otherwise, the mobile devices would no longer accept the previous key even though there are still terminals that are attempting to use it for authentication.

In this scenario, less than 24 hours had elapsed since key B was "rotated in" (being used on terminals). Within this time period, the newly-created key, D, would not have sufficient opportunity to propagate to all of the users' mobile devices. By having 3 public keys in Google Wallet, a potential outage was avoided when rotating to key C.

Minimum Rotations

Google recommends to have at least one valid public key stored in Google Wallet at any time, and suggests to perform key rotations on a monthly basis. The following steps outline an example rotation scenario based on these recommendations.

First, create the following initial configuration:

  • One public key is uploaded to Google Wallet: X.
  • Terminals are configured to use X’s private key.

Next, perform a rotation:

  1. Generate a new key pair, Y.
  2. Upload Y's public key to Google Wallet.
  3. Wait 24 hours for Y's public key to sync to users' mobile devices.
  4. Configure terminals to use Y’s private key.
  5. Remove X's public key from Google Wallet.

Assume that less than 24 hours after this rotation is complete, private key Y (currently in use) has become compromised. The following steps outline an example rotation scenario for compromised keys:

  1. Generate a new key pair, Z.
  2. Upload Z's public key to Google Wallet.
  3. Configure terminals to use Z's private key.
  4. Revoke Y’s public key from Google Wallet.

When the public key Z is uploaded to Google Wallet, it may take up to 24 hours to sync to users' mobile devices. A merchant in this case must decide how much time, if any, to wait before revoking public key Y. A longer period of time allows more devices to sync to the new public key, reducing the size of an outage when the terminals start using the new key or when users' devices start rejecting the old key. However, it also increases the time in which the compromised private key is valid and accepted as authentication for attacks.

If key Y is revoked before the terminals are configured to use key Z, there would be an outage because there would be no public key available for authentication.


Key Storage

Public Keys

Public keys shouldn't be considered a security mechanism. For the purpose of security modeling, the public key should always be considered as compromised and publicly available. In actuality, the public key shouldn't be made publicly available. Limiting access to the public key increases the difficulty of a potential attack but doesn't eliminate the possibility. Limiting access also limits the potential for a rogue device to emulate the Google Wallet application.

Private Keys

The private key is used to authenticate the merchant terminal. If compromised, a malicious terminal can use the long-term private key to impersonate a legitimate merchant’s terminal until the key is rotated.

While NFC traffic can always be sniffed, it can only ever be decrypted by the initiating terminal. Compromised private keys can never be used to decrypt sniffed traffic from another terminal, even if the same private key was used for authentication.

Relay attacks are possible, but unlikely given the required proximity and collision avoidance mechanisms of the lower-level NFC protocol.

On The Terminal

The private key should be stored and used only in a secure enclave of the terminal. Zebra recommends using Secure Element or Qualcomm TE where available on Zebra products. Refer to Zebra product documentation for more information.

Minimum Requirements

The private key can be stored in the main file system but should be encrypted at rest using a key encryption key (KEK). The KEK used to decrypt the private key should be stored in the secure enclave of the terminal. During a transaction, the encrypted private key is first decrypted in the secure enclave. The decrypted private key should only be used in the main CPU/RAM.

For more information, contact Google or refer to their relevant documentation.


Apple VAS

For optimal performance of the Apple VAS solution, set the Card Detection Mode (polling mode) to Standard in the Enterprise NFC Settings. To access these settings, go to device Settings > Connected Devices. For more information, refer to the product documentation posted on the Zebra support portal.

Card Detection Mode in Enterprise NFC Settings


See Also