Skip to content
Zion edited this page Sep 2, 2020 · 35 revisions

Zion Key Management API

Integration guide for creating a wallet using Zion Key Management APIs

1. Introduction

        ZKMA (Zion Key Management API) is a library which provides a way for developers to manage seed security built into HTC EXODUS devices, which integrates Zion protection. All secure operations (input pin, display seed, sign transaction…) will be performed by the trusted OS and no secure data exposed to the rest of Android.

Figure 1. ZKMA API call flow

        ZKMA APIs can be called directly or bound as ZKMS of HTC Zion Vault. For performance, our suggestion is you can call ZKMA by ZKMA.aar. Another way which is ZKMS can be used for reducing your APK size. This document will focus on ZKMA API call flow via ZKMA.aar.

ZKMA ZKMS
Library name ZKMA.aar ZKMS.aar
File Size 7 MB~ 20 KB~
Architecture Direct call API via ZKMA library Call API via ZKMS service
HTC Zion Vault App Unnecessary Mandatory

Table 1. A comparison between ZKMA and ZKMS libraries

1.1 Get ZKMA files at GitHub

Any collaborator can get the latest ZKMA .AAR library — HtcWalletSDK-Htc_partner1-release.aar — from https://github.com/htczion/ZKMA/releases .

1.2 API execution result

        Most functions in ZKMA return a value. Definitions are listed in the RESULT class. If a function doesn't seem to work, please check the return value in the RESULT class. Return value definitions can be found at RESULT.java

2. Environment setup

ZKMA is packaged as an AAR file that can be imported into any android app project.

2.1 Place ZKMA.AAR in lib folder

Copy this .AAR file to your app project lib path:

<Project Name>\app\libs\ZKMA-release.aar

2.2 Config and Sync your build.gradle

dependencies{
    
...
compile(name:'ZKMA-release', ext:'aar')
...
    
}

2.3 Build your wallet app

Press the “Make project” button or run the command below to build your app:

    ./gradlew assembleRelease

3. Build your wallet steps by steps

3.1 Acquire ZKMA manager instance

        The HTC wallet SDK manager is an agent which wraps all seed operations. You must get the instance (singleton) before performaning any operation related to seed access.

HtcWalletSdkManager mZKMA = HtcWalletSdkManager.getInstance();

3.2 Initialize ZKMA

        Initialization prepares all resources and verifies that API function calls work correctly. For example, RESULT.E_SDK_SERVICE_TOO_OLD during initialization means the system service is too old to run, and to prompt the user to do a ROM update. Otherwise all APIs will return a failure or RuntimeException.

  mZKMA = HtcWalletSdkManager.getInstance();
  int result = mZKMA.init(getApplicationContext());
  switch (result) {
    case RESULT.E_SDK_ROM_SERVICE_TOO_OLD:
    case RESULT.E_SDK_ROM_TZAPI_TOO_OLD:
      // App should prompt the user to update ROM
      showUpdateDialog(mActivity, "PLEASE UPDATE YOUR SYSTEM");
      break;
    case RESULT.E_TEEKM_TAMPERED:
      // App should prompt the user it’s rooted device
      showUpdateDialog(mActivity, "SDK can't support Rooted device");
      break;
    default:
      Log.w(TAG, "init() result="+intValue);
}

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

Compatibility:

        Compatibility between ZKMA versions and ROM ApiVersion is described in README.md in the following format.:

 1.1.0 (0.0003.01010001)
    
 1.1.1
    
 1.1.2
    
 1.1.3
    
 1.2.0 (0.0004.01010005)
    
 1.2.1 (0.0004.01000006)

2.0.0 , ZKMS_ver= 2.0.0

For example, 1.1.0 (0.0003.01010001) can be broken down like this:

    1.1.0 indicates ZKMA version based on the ZKMA.AAR version.

    0.0003.01010001 indicates the combined API version based on the ROM version.

    The combined API version number can be further divided into three numbers and read as follows:

        0: The HW wallet.

        0003: A hexadecimal number referring to the ROM currentServiceVer.

        01010001: A hexadecimal number referring to the ROM currentTzapiVer.

ZKMS_ver= 2.0.0 indicates the supported ZKMS version.

        If the combined API version field is empty, such as the listing for ZKMA 1.1.2, it means the ZKMA is compatible with the previous version, and no ROM update is necessary.

        ZKMS is a service based on ZKMA. As such, the ZKMS version shouldn't be lower than the ZKMA version otherwise ZKMS will return the E_ZKMA_TOO_OLD error.

Figure 4. Init API check sequence

        To check Trust Zone compatibility of the ROM, the init API sequence will check minServiceVer and minTzApiVer. If the ROM is too old, ZKMA will return either E_SDK_ROM_SERVICE_TOO_OLD or E_SDK_ROM_TZAPI_TOO_OLD to the caller.

3.3 Get ZKMA version and API version

The current ZKMA version of the HTC Zion Vault app can be gotten with getModuleVersion().

String SdkVersion= mZKMA.getModuleVersion();

        API version information is distinct, and refers to the API level supported by your current EXODUS device hardware. If the API version is lower than the minimum defined by ZKMA, the app should show a dialog to prompt the user to update ROM.

String apiVersion= mZKMA.getApiVersion();

        You can get more ZKMA information with getExportFields(). Support includes but is not limited to bTZ_support, minServiceVer, minTzApiVer, etc.

ExportFields mExportFields= mZKMA.getExportFields();

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.4 Check your device

        For security, ZKMA does not support rooted S-ON devices. If ZKMA detects this situation, it will return the error RESULT.E_TEEKM_TAMPERED to the developers. isRooted() allows the developer to check if the device rooted at any point.

int isRooted(); // 0: not root yet(RESULT.NOT_ROOTED), others: rooted or other errors

To prevent the Trust Zone blocked UI thread, the developer must call this API in a background thread.

3.5 Register your wallet

        ZKMA uses unique IDs to distinguish between callers of seed operations. These IDs are generated by register(). If there is any change in the parameters wallet_name or value, a new UID will be generated. This allows apps requiring multiple seeds to adjust the value parameter to get multiple IDs allowing access to different seeds.

long unique_id = mZKMA.register(wallet_name, sha256);

If unique_id returns 0, registration has failed. Additionally, a wallet_name cannot be more than 32 characters.

        To prevent the Trust Zone blocked UI thread, the developer must call this API in a background thread.

3.6 Creating a new seed for your wallet

        For security concern, all UIs,show by ZKMA, will disappear after 30sec. The only special case is the “12-word recovery phrase“ screen. In this case, the screen timeout will be 3 minutes for the user hand writing 12-word recovery phrase.

3.6.1 Set the input keyboard type for passcode

If no seed exists, you can set keyboard type as either qwertypad for a QWERTY layout or numberpad for a numeric layout.

// nType= 0:qwertypad(default), 1:numberpad
int result = mZKMA.setKeyboardType(unique_id, nType);

To prevent the Trust Zone blocked UI thread, the developer must call this API in a background thread.

Figure 5-1. two keyboard type

3.6.2 Creating a new seed

        If the user has not used cryptocurrency before, they will need to create a new wallet and generate new seeds. createSeed() has been provided for this purpose. After invoking this function, the user will be prompted to set a passcode. Any seed-related function will prompt the user for this passcode.

        Once passcode setup is complete, the user will be asked to write down 12 words that represent their cryptographic seed (we call this the “12-word recovery phrase”). To ensure the user has accurately recorded the 12-word recovery phrase, they will be prompted to re-enter for confirmation before continuing.

int result = mZKMA.createSeed(unique_id);

Figure 5. Seed creation process from the perspective of the user

To prevent the Trust Zone blocked UI thread, the developer must call this API in a background thread.

3.6.3 Change the keyboard type for a preexisting seed

If there is a preexisting seed, the keyboard can still be specified as qwertypad or numberpad with the function changePIN_v2. The first screen will instruct the user to input the old PIN, and then the following screen will show the new keyboard specified by the nType parameter.

// nType= 0:qwertypad(default), 1:numberpad
int result = mZKMA.changePIN_v2(unique_id, nType);

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

Figure 5-3. Change PIN V2

3.7 Restoring an existing seed for your wallet

        If the user has used cryptocurrency before, they may wish to restore an existing wallet instead of creating a new one. This capability is provided by restoreSeed(). The user need only enter their 12-word recovery phrase to restore their wallet. Users will also be prompted to set a passcode.

int result = mZKMA.restoreSeed(unique_id);

Prompt the user to enter their 12-word recovery phrase

Figure 6. Seed restoration process from the perspective of the user

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.8 Checking for an existing seed

        An app needs to be able to distinguish between use cases where the user needs to create a new wallet, restore an existing wallet, or let the user check current wallet details. This function can be used to check if the device has an existing seed and wallet. If there is no preexisting seed, the user has not completed wallet setup. Users should be prompted to create a wallet or restore an existing one.

int result = mZKMA.isSeedExists(unique_id);

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.9 Displaying the wallet seed

        When creating a new wallet, the user will be prompted to write down their 12-word recovery phrase. If the user has lost their recovery phrase, the wallet app can use this function to display the recovery phrase again so that the user can record it in a safe place.

int result = mZKMA.showSeed(unique_id);

Figure 7. Trusted UI for the user to check their seed

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.10 Retrieving the public key from a wallet seed to generate an account address

        Because different types of crypto currency have different algorithms to generate the wallet address, ZKMA does not provide a way to retrieve an account address. Instead, it provides a way to retrieve the public key. Once the key is obtained, apps can generate their own account addresses.

    // A holder for store your key
    PublicKeyHolder sendPublicKeyHolder, receivePublicKeyHolder;
    // You can get a public key for send or receive purpose, index can be increased by SDK or parameter.
    // coin_type: 0=BitCoin, 2=LiteCoin, 60=Ethereum, 145=Bitcoin Cash(BCH)
    sendPublicKeyHolder = mZKMA.getSendPublicKey(unique_id, coin_type);
    sendPublicKeyHolder = mZKMA.getSendPublicKey(unique_id, coin_type, keyIdx);
    receivePublicKeyHolder = mZKMA.getReceivePublicKey(unique_id, coin_type);
    receivePublicKeyHolder = mZKMA.getReceivePublicKey(unique_id, coin_type, keyIdx);

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.11 Signing your transaction

        Wallet apps can use this function to transfer crytocurrency to other account addresses. Users are prompted for their passcode before executing the sign operation. Note: Signing operation will not upload the transaction on chains. Your app must upload it on its own.

        With the seed and public key, you can sign transactions as appropriate for your coin type. To sign a transaction, you will need to pass rates,which is USD currency rate, and data in the JSON format - including all raw transaction data for signTransaction(). Signed raw transaction data bytes will be returned by the byteArrayHolder parameter.

    int result = mZKMA.signTransaction(unique_id, coin_type, rates, strJson, byteArrayHolder);

    // a byte array holder to receive the transaction data
public class ByteArrayHolderParcel implements Parcelable {
    private static final int DEFAULT_ARRAY_SIZE = 2 * 1024; // 2KB
    public int receivedLength;
    public byte[] byteArray;

    public ByteArrayHolderParcel() {
        receivedLength = 0;
        byteArray = new byte[DEFAULT_ARRAY_SIZE];
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(receivedLength);
        out.writeByteArray(byteArray);
    }

    private ByteArrayHolderParcel(Parcel in) {
        readFromParcel(in);
    }

    protected void readFromParcel(Parcel in) {
        try {
            receivedLength = in.readInt();
            byteArray = new byte[receivedLength];
            in.readByteArray(byteArray);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Implementation of Parcelable
    public static final Creator<ByteArrayHolderParcel> CREATOR = new Creator<ByteArrayHolderParcel>() {
        public ByteArrayHolderParcel createFromParcel(Parcel in) {
            return new ByteArrayHolderParcel(in);
        }

        public ByteArrayHolderParcel[] newArray(int size) {
            return new ByteArrayHolderParcel[size];
        }
    };
}

Figure 8. Show the security UI for sign a transaction by coin type

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.11.1 JSON format

JSON field definitions are set by each coin type. In case of Bitcoin, JSON format is as follows:

    // A bitcoin JSON sample
    {
           "tx_version": "01",
           "tx_inputs_count": "01",
           "tx_inputs": [
            {
               "path": "m/44'/0'/0'/0/0",
               "tx_id": "9c6700b994e811bc6c4ca6c83a3776fafa4f720f06a2240d3efb451791aa7b8b",
               "tx_index": "00",
               "scriptSig": "4104e0bf225f8998c7e3dc99899ce2f08d87969ee1bdc0f0759941134f375fd1e8eecba4db44e3f76dce7fd16262c2ca9e93628eafde3d5a6ffa99ae13e74bbc54f6ac",
               "sequence": "ffffffff",
               "amount": "e86f7932"
              }
             ],
             "tx_outputs_count": "01",
             "tx_outputs": [
              {
               "amount": "48bc4631",
               "address": "3P14159f73E4gFr7JterCCQh9QjiTjiZrG"
              }
             ],
             "tx_changes_count": "01",
             "tx_changes": [
                 {
                     "path": "m/44'/0'/0'/0/0",
                     "amount": "002d3101"
                 }
              ],
              "lock_time": "1234"
    }

The Transaction Fee will be calculated automatically by the formula as follows:

Fee = tx_inputs.amount - tx_outputs.amount - tx_changes.amount

3.11.2 JSON format examples:

A. BitCoin:

 // A litecoin JSON sample
 {
       "tx_version": "01",
       "tx_inputs_count": "01",
       "tx_inputs": [
         {
          "path": "m/44'/02'/0'/0/0",
          "tx_id": "9c6700b994e811bc6c4ca6c83a3776fafa4f720f06a2240d3efb451791aa7b8b",
          "tx_index": "00",
          "scriptSig": "76a914feb5f43851477b22f68db0523351c4debbc67a7588ac",
          "sequence": "ffffffff",
          "amount": "e86f7932"
         }
       ],
 "tx_outputs_count": "01",
      "tx_outputs": [
       {
            "amount": "48bc4631",
            "address": "n4jjuARyw1nnHhta1fR5khPFS5i1BDakX6"
       }
      ],
 "tx_changes_count": "01",
   "tx_changes": [
    {
      "path": "m/44'/02'/0'/0/0",
      "amount": "002d3101"
    }
   ],
 "lock_time": "00"
 }

B. Litecoin:

 // A ethereum JSON sample
 {
   "path": "m/44'/60'/0'/0/0",
      "tx": {
        "nonce": "01",
        "gas_price": "09184e72a000",
        "gas_limit": "493e0",
        "to": "d8A7297522A2e30bE59f66e8CB2B06c89a50490c",
        "value": "38d7ea4c68000",
        "erc_flag": "0",
        "data": "",
        "chain_id": "04"
      }
 }

C. Ethereum:

    // A ethereum JSON sample
    {
        "path": "m/44'/60'/0'/0/0",
        "tx": {
            "nonce": "01",
            "gas_price": "09184e72a000",
            "gas_limit": "493e0",
            "to": "d8A7297522A2e30bE59f66e8CB2B06c89a50490c",
            "value": "38d7ea4c68000",
            "erc_flag": "0",
            "data": "",
            "chain_id": "04"
        }
    }
 // A Ethereum ERC20 JSON sample
 {
    "path": "m/44'/60'/0'/0/0",
      "tx": {
          "nonce": "04",
          "gas_price": "0165a0bc00",
          "gas_limit": "928a",
          "to": "B8c77482e45F1F44dE1745F52C74426C631bDD52",
          "value": "",
          "erc_flag": "20",
          "data": "a9059cbb00000000000000000000000058a61a7144c8c7545794447a9a3e9a3d56066c200000000000000000000000000000000000000000000000001bc16d674ec80000",
          "chain_id": "04"
       }
 }

Signing a transaction for Ethereum ERC20:

        For the Ethereum ERC20 Trusted UI, the parameters of extra tag can be used for customizing the icons, background color, token name, full display name of token, and number of decimal places for the Trusted UI. The example below illustrates the extra tag with JSON data that will display corresponding content in the Trusted UI.

// A Ethereum Cash(ETH) JSON sample for ERC20
{
   "path":"m\/44'\/60'\/0'\/0\/0",
     "tx":{
        "nonce":"5",
        "gas_price":"12a05f200",
        "gas_limit":"5d82",
        "to":"0d8775f648430679a709e98d2b0cb6250d2887ef",
        "value":"",
        "erc_flag":"20",
"data":"a9059cbb000000000000000000000000c24c375a77baf82750c46f1c76c809d5ad4c6769000000000000000000000000000000000000000000000003db72f26a08530000",
        "chain_id":"1"
     },
"currency":"USD",
   "extra": {
        "erc20_icon": "**<146*146(Exodus) or 72*72(Breeze2) png image file raw data converted as HEX string>**",
        "erc20_symbol": "HAK",
        "erc20_displayname": "HAWK_TOKEN",
        "erc20_decimal": "18"
   }
}

The parameters of extra tag are specific as:

      erc20_icon: ERC20 Token icon binary data. The icon should be in PNG format, and no larger than 146x146(Exodus) or 72x72(Breeze2) pixels. Must be converted to HEX String before being passed to the JSON field.

      erc20_symbol: ERC20 token name

      erc20_displayname: full display name of ERC20 token

      erc20_decimal: Number of decimal places to display. The correct number of decimal places for the target token is required. All tokens have their own specifications for decimal places.

Figure 9. Show the security UI with ERC20 extra tag for sign a transaction

        For Ethereum ERC20 smart contract methods, ZKMA supports the ERC20 transfer function with data parsing. For other functions, the trusted UI will show an unknown function with raw HEX data for user confirmation.

contract ERC20Interface {
    function totalSupply() public view returns (uint);
    function balanceOf(address tokenOwner) public view returns (uint balance);
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success); // ZKMA support it now.
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

Signing Ethereum ERC721 transactions:

        For Ethereum ERC721, the parameters of extra tag can be used to customize the icon and the full name of ERC721 token in the Trusted UI. Unlike ERC20, the erc_flag and erc20_symbol for the ERC721 tag can’t be used for customization. For the Trusted UI, erc_flag must be set to “721” and erc20_symbol must be set to "ERC-721". The following example demonstrates parameters in JSON format corresponding to the Trusted UI output in the figure below.

// A Ethereum Cash(ETH) JSON sample for ERC721
{
   "path": "m/44'/60'/0'/0/0",
     "tx" : {
        "nonce": "04",
        "gas_price": "0165a0bc00",
        "gas_limit": "493e0",
        "to": "B8c77482e45F1F44dE1745F52C74426C631bDD52",
        "value": "",
        "erc_flag": "721",
        "data": "a9059cbb00000000000000000000000058a61a7144c8c7545794447a9a3e9a3d56066c200000000000000000000000000000000000000000000000001bc16d674ec80000",
        "chain_id": "03"
     },
   "extra": {
        "erc20_icon": "**<146*146(Exodus) or 72*72(Breeze2) png image file raw data converted as HEX string>**",
        "erc20_symbol": "ERC-721",
        "erc20_displayname": "Crypto Kitties"
   }
}

Figure 10. Trusted UI for signing an ERC721 transaction

        For Ethereum ERC721 smart contract methods, ZKMA only supports the ERC20 transfer method now. For data formats not supported by ZKMA, the Trusted UI signature screen shows an unknown method with raw HEX data for the user to confirm

D. Bitcoin Cash(BCH):

// A Bitcoin Cash(BCH) JSON sample
{
  "tx_version": "02",
  "tx_inputs_count": "01",
  "tx_inputs": [
    {
      "path": "m/44'/145'/0'/0/0",
      "tx_id": "42a9afa7d0be8aa574ba9e5fd27f07c31610469eb86395de6f69386a259d43aa",
      "tx_index": "00",
      "scriptSig": "76a914c6ab328b8bd42e6986948b3e0206e2a33c5ff0ee88ac",
      "sequence": "FEFFFFFF",
      "amount": "80841e"
    }
  ],
  "tx_outputs_count": "01",
  "tx_outputs": [
    {
      "amount": "40420f",
      "address": "bitcoincash:qppfx64ss3k5smqj8v8yua28amsysdhdhv5ymjlr64"
    }
  ],
  "tx_changes_count": "01",
  "tx_changes": [
    {
      "path": "m/44'/145'/0'/0/0",
      "amount": "d63d0f"
    }
  ],
  "lock_time": "0"
}

E. Binance Coin(BNB):

Signing transaction for BNB send:

// BNB send
{
  "path": "m/44'/714'/0'/0/0",
  "type": "send",
  "fee": "37500",
  "bnb": {
    "account_number":"147862",
    "chain_id":"Binance-Chain-Tigris",
    "data":null,
    "memo":"0to1",
    "msgs":[
      {
        "inputs":[
          {
            "address":"bnb1l5vpsz20c7h8kt6xk6rsdsa3z7c7swrqq6ze04",
            "coins":[
              {
                "amount":10000000,
                "denom":"BNB"
              }
            ]
          }
        ],
        "outputs":[
          {
            "address":"bnb1rd6sm3msuskvac59du4ms7z2777smvk4rknzdf",
            "coins":[
              {
                "amount":10000000,
                "denom":"BNB"
              }
            ]
          }
        ]
      }
    ],
    "sequence":"1",
    "source":"2"
  }
}

Signing transaction for BNB buy order:

// BNB buy order
{
	"path": "m/44'/714'/0'/0/0",
	"type": "order",
	"fee": "0",
    "bnb": {
		"account_number":"147862",
		"chain_id":"Binance-Chain-Tigris",
		"data":null,
		"memo":"",
		"msgs":[
			{
				"id":"FD1818094FC7AE7B2F46B68706C3B117B1E83860-3",
				"ordertype":2,
				"price":341124,
				"quantity":200000000,
				"sender":"bnb1l5vpsz20c7h8kt6xk6rsdsa3z7c7swrqq6ze04",
				"side":1,
				"symbol":"LTO-BDF_BNB",
				"timeinforce":3
			}
		],
		"sequence":"2",
		"source":"0"
	},
	"extra": {
		"displayname": "HAWK TOKEN",
		"dst_icon": "**<146*146(Exodus) or 72*72(Breeze2) png image file raw data converted as HEX string>**"
	}
}

Signing transaction for BNB sell order:

// BNB sell order
{
	"path": "m/44'/714'/0'/0/0",
	"type": "order",
	"fee": "0",
    "bnb": {
		"account_number":"147862",
		"chain_id":"Binance-Chain-Tigris",
		"data":null,
		"memo":"",
		"msgs":[
			{
				"id":"FD1818094FC7AE7B2F46B68706C3B117B1E83860-4",
				"ordertype":2,
				"price":309825,
				"quantity":100000000,
				"sender":"bnb1l5vpsz20c7h8kt6xk6rsdsa3z7c7swrqq6ze04",
				"side":2,
				"symbol":"LTO-BDF_BNB",
				"timeinforce":3
			}
		],
		"sequence":"3",
		"source":"2"
	},
	"extra": {
		"displayname": "HAWK TOKEN",
		"dst_icon": "**<146*146(Exodus) or 72*72(Breeze2) png image file raw data converted as HEX string>**"
	}
}

3.12 Clearing the current seed from your wallet

        If the user wants to create a new wallet, an app needs to clear current wallet first. Clearing the current wallet completely is necessary for its continued security. To that end, clearSeed() will clear all secure data related to the current wallet.

int result = mZKMA.clearSeed(unique_id);

Figure 11. Trusted UI for clearing an existing seed.

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.13 Changing the passcode

        When you create a new wallet or restore an existing wallet, you will be asked to create the passcode to protect seed access. If it is decided that a passcode is inadequate or otherwise needs to be changed, the following function can be used:

int result = mZKMA.changePIN(unique_id);

Figure 12. Trusted UI prompting the user to change their passcode

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.14 Passcode confirmation

confirmPIN() can be used to prompt for passcode confirmation at any time through the Trusted UI.

int result = mZKMA.confirmPIN(unique_id, resId = 0);

Figure 13. Show the security UI for user to confirm their PIN code

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.15 Unregistering your wallet

        unregister() releases the unique id, and it will also release seed data stored by ZKMA. If this function is called, the seed data will also be cleared.

int result = mZKMA.unregister(wallet_name, sha256, unique_id);

To prevent the blocked Trust Zone UI thread, the developer must call this API in a background thread.

3.16 De-Initializing

This function releases all resources which allocated by ZKMA, and no future ZKMA functions will be executed.

int result = mZKMA.deinit();

3.17 Error Handling

        Error codes are delivered by return values, and are handled by ZKMA in three different types. Type1 refers to a normal behavior, like E_TEEKM_UI_BACK, E_TEEKM_UI_CANCEL and so on, and can be safely ignored. Type2 is a silent error, and will not show any UI prompt by ZKMA, and apps are required to handle this without a ZKMA error prompt, such as E_TEEKM_SEED_NOT_FOUND, E_TEEKM_TIME_TIMEOUT, E_SDK_ROM_SERVICE_TOO_OLD, E_SDK_ROM_TZAPI_TOO_OLD and so on. If the error code is neither type1 nor type2, ZKMA will handle it through the default error dialog on the screen.

Figure 14. Default error dialogs if an error is detected by ZKMA

All error codes are defined in RESULT.java and are named accordingly.

3.18 Signing a Message

        ZKMA supports message signing. If a message contains non-ASCII characters, the Trusted UI will show hex data instead of a readable string.

int result = mZKMA.signMessage(unique_id, coin_type, strJson, ByteArrayHolder);

A. Ethereum coin

        A Ethereum sample JSON message format for signing, the data field in JSON can only support ASCII string. In the sample JSON data field, “48656c6c6f” is HEX string data of “Hello”. In other word, you must convert your ASCII string to HEX string data first, then put it into the data TAG for composing the input strJson parameter. About version TAG, this is implemented by EIP191 and must set to 0x45.

{
   "path": "m/44'/60'/0'/0/0",
   "message" : {
      "version": "45",
      "data": "48656c6c6f"
   }
}

Message data in JSON should contain version and data tags.

Figure 15. Trusted UI message signing confirmation

4. Demo

This is a simple demo for API usage.

public class MainActivity extends AppCompatActivity {
    public static final String TAG = "MainActivity";
    private static Activity sActivity;
    HtcWalletSdkManager mZKMA;
    int intValue = RESULT.UNKNOWN;
    String mZKMA_version;
    String mApi_Version;
    long uid;
    String wallet_name = "testZKMA";
    String sha256 = "24681012579";
    Handler mHandler;
    HandlerThread mHandlerThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sActivity = this;
        setContentView(R.layout.activity_main);
        mZKMA_version = com.htc.htcwalletsdk.BuildConfig.VERSION_NAME;
        ((TextView)findViewById(R.id.text1)).setText(mZKMA_version);
    }

    public void demoAPIs(View v){ // 1. call ZKMA APIs in background thread
        if( mZKMA == null ) {
            mZKMA = HtcWalletSdkManager.getInstance();
        }
        if( mHandlerThread == null ) {
            mHandlerThread = new HandlerThread("ZKMA_BackgroundThread");
        }
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        mHandler.post(apiRunnable);
    }

    final Runnable apiRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                // 1-1. init
                intValue = mZKMA.init(getApplicationContext());
                // 1-2. getApiVersion
                mApi_Version = mZKMA.getApiVersion();
                // 1-3. register
                uid = mZKMA.register(wallet_name, sha256);
                // 1-4. call ZKMA APIs, ex: create Seed
                intValue = mZKMA.createSeed(uid);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
}

Simple Test App