Skip to content
htczion edited this page Sep 25, 2019 · 8 revisions

1 Introduction

This document explains how your application can request operations from Zion Vault. This SDK has two primary functions:

  1. Retrieve addresses from Zion Vault.
  2. Request signatures from Zion Vault.

Zion SDK offers two approaches to integrate Zion Vault with the transaction service provided by cryptocurrency exchange service providers:

  1. Web-based integration: Zion Vault loads a mobile web page provided by an exchange service provider through Zion Remote.
  2. Application-based integration: The exchange service provider implements an Android application to work with Zion Vault.

Complete explanations and usage examples can be found below.

2 Web-based integration

2.1 Environment preparation

HTC partners should prepare a backend server with an appropriate Application Development Environment (ADE) and provide the following information:

  1. URL of a mobile web page used for transaction-related operations.
  2. IP list for partner backend server.

2.2 JSON definition

The data payload between HTC Zion Vault and HTC partner exchange services must be wrapped in the JSON format. In order to transmit the JSON string, all binary data must be converted to Base64 strings (such as encrypted address and digital signature). Below is the class definition of the deserialized JSON string:

public class JsonObject {
    
    private String apiVer;
    public String getApiVer() { return this.apiVer; }
    public void setApiVer(String apiVer) { this.apiVer = apiVer; }
        
    private String cmd;
    public String getCmd() { return this.cmd; }
    public void setCmd(String cmd) { this.cmd = cmd; }
        
    private Map<String, Object> params;
    public Map<String, Object> getParams() { return this.params; }
    public void setParams(Map<String, Object> params) { this.params = params; } 
        
    private Map<String, Object> result;
    public Map<String, Object> getResult() { return this.result; }
    public void setResult(Map<String, Object> result) { this.result = result; }
}

apiVer: API version number
cmd: Command used for request or response
params: Key/value pair for request parameters
result: Key/value pair of the result for response

2.3 Buying process

Buying process breakdown
B1. Zion Remote loads the web page through the URL provided by exchange service provider, allowing the user to buy cryptoassets.
String constant
  cmd:getEncAddr
  params key:currencyType
  params value of currencyType:[btc | eth | ltc]
Example
  {
    “apiVer”: “1”,
    "cmd": "getEncAddr",
    "params":
    {
      "currencyType": "btc"
    }
  }

B2. Zion Remote requests the public address of the hardware wallet.
B3. TrustZone (TZ) encrypts the public address with SrvPub, and then signs it with TZPriv_x:(SrvPub (public address) + TZPriv_x (SHA-256 (SrvPub (public address)))):ACCADDR
B4. Zion Main responds to B2 with (DIDH_x + ACCADDR), where DIDH_X is the hashed device ID.
String constant
  result key:encAddr, encAddrSigned and secretIdHash
Example
  {
    “apiVer”: “1”,
    "cmd": "getEncAddr",
    "result":
    {
      "resultCode ": 0,
      "errorMessage": "Operation success.",
      "encAddr": "SrvPub(public address)",
      "encAddrSigned": "TZPriv_x(SHA-256(SrvPub(public address)))",
      "secretIdHash": "DIDH_x"
    }
  }

B5. Zion Remote sends the payload from B4 to the exchange server for the buying process.
B6. The exchange server sends a request to the HTC server for verification and decryption of the payload from B4.
Web interface
  Method:POST
  URL:/exodus/kws/v1/verifyAddr
  Header name:Exodus-Auth;Header value:Cognito token
  ContentType:application/json
Example
  POST /exodus/kws/v1/verifyAddr
  {
    "provider":"providerId",
    "params":
    {
      "encAddr":"SrvPub(public address)",
      "encAddrSigned":"TZPriv_x(SHA-256(SrvPub(public address)))",
      "secretIdHash":"DIDH_x"
    }
  }

B7. The HTC server looks up TZPub_x with the hashed device ID, DIDH_x, to verify ACCADDR. If a valid ACCADDR is found, the HTC server will decrypt it then respond to the exchange server with the decrypted public address.
Http Status Code:200
Body:
  {
    "result":
    {
      "addr": "public address"
    }
  }
Code Body Description
200 Operation done
404 This device is not found secretIdHash not registered
400 Message string Bad Request
400 Message string Verify encAddrSigned signature failed with ServerPrivateKey
400 Message string Hash(encAddr) and signature of encAddrSigned do not match
500 Exception message string Server exception

B8. The exchange server updates the web page with detailed purchase information. Once the user confirms the transaction, the exchange server will bind the DIDH_x and public address to the user.

NOTE:
  • After the public address is bound to the user, steps B2-B4 and B6-B7 can be skipped in future transactions.

2.4 Selling process

Steps
S1. Zion Remote loads the web page through the URL provided by exchange service provider, allowing the user to sell cryptoassets through the exchange.
String constant
  cmd:getSecretIdHash
Example
  {
    “apiVer”: “1”,
    "cmd": "getSecretIdHash"
  }

S2. Zion Remote requests the hashed device ID (DIDH_x) from Zion Main.
S3. Zion Main responses the DIDH_x to Zion Remote.
String constant
  result key:secretIdHash
Example
  {
    “apiVer”: “1”,
    "cmd": "getSecretIdHash",
    "result":
    {
      "resultCode ": 0,
      "errorMessage": "Operation success.",
      "secretIdHash": "DIDH_x"
    }
  }

S4. Zion Remote passes the hashed device ID DIDH_x to the exchange server to use in the selling process.
S5. The exchange server sends the hashed device ID DIDH_x and public address to HTC Server for encryption and signing.
Web interface
  Method:POST
  URL:/exodus/kws/v1/encryptAddr
  Header name:Exodus-Auth;Header value:Cognito token
  ContentType:application/json
Example
  POST /exodus/kws/v1/encryptAddr
  {
    "provider":"providerId",
    "params":
    {
      "secretIdHash":"DIDH_x",
      "addr":"public address"
    }
  }

S6. The HTC server encrypts the public address with TZPub_x, and then signs it with SrvPriv:(TZPub_x (public address) + SrvPriv (SHA-256 (TZPub_x (public address)))):ACCADDR
Http Status Code:200
Body:
{
  "result":
  {
    "encAddr":"TZPub_x(public address)",
    "encAddrSigned":"SrvPriv(SHA-256(TZPub_x(public address)))"
  }
}
Code Body Description
200 Operation done
404 This device is not found secretIdHash not registered
400 Message string Bad Request
500 Exception message string Server exception

S7. The exchange server responds with ACCADDR, cryptocurrency type, sale amount, DIDH_x (optional), and an error handling URL for the request from S4.
S8. Zion Remote passes the payload from S7 to Zion Main.
String constant
  cmd:requestTransaction
  params key:currencyType, amount, encAddr, encAddrSigned and errorHandling
  params value of currencyType:[btc | eth | ltc]
Example
  {
  “apiVer”: “1”,
  "cmd": "requestTransaction",
  "params":
  {
    "currencyType": "btc",
    "amount": "10.1",
    "encAddr": "TZPub_x(public address)",
    "encAddrSigned": "SrvPriv(SHA-256(TZPub_x(public address)))",
    "secretIdHash": "DIDH_x",
    "errorHandling": "https://error_handling"
  }
}

S9. Zion Main will verify ACCADDR with SrvPub, and then decrypt it with TZPriv_x. After user confirmation, the transaction will be signed in the TrustZone, and then will be committed to blockchain.


NOTE:
  • The exchange server may bind the hashed device ID DIDH_x to the user after a successful sell transaction. When user attempts a sell operation in the future, the exchange server can pass the hashed device ID DIDH_x to Zion Vault (step S7) to check DIDH_x (DIDH_x might have changed if the user changes devices). If the DIDH_x is no longer available, Zion Vault will open the web page following the error handling URL. The user can then bind the new DIDH_x through the web page.
  • Once the DIDH_x has been bound to the user, steps S1-S3 can be skipped in future transactions.

2.5 Communication between HTML and web view

There may be a need for communication between the mobile web page and Android web view (B1, B4b, S1, S3b and S7b) to facilitate the buying and selling process. postMessage() is used to handle that communication. See the below sample code for HTML with embedded JavaScript.

<html>
<p>
    <b><a onClick="sendRequest()">Click me to get user account</a>: </b><br>
    <span id="response">*</span>
</p>
<script type ='text/javascript'>

var port;
onmessage = function (e) {
    port = e.ports[0];
    port.onmessage = function (f) {
        receiveResponse(f.data);
    }
}

function receiveResponse(json) {
    document.getElementById("response").innerHTML = json;
}

function sendRequest() {

    var request = new Object();
    request.apiVer = "1.0";
    request.cmd = "getEncAddr";

    request.params = new Object();
    request.params.currencyType = "btc";
    port.postMessage(JSON.stringify(request));
}

</script>
</html>

NOTE:

  • sendRequest: This function can be used to post a message to a web view activity.
  • receiveResponse: Calling postmessage() in a web view activity will trigger the onmessage event in JavaScript to allow the HTML side to receive the response from the web view activity with receiveResponse() when onmessage was triggered.

3. Application-based integration

3.1 Precondition

1. APIs caller authentication

All functions exported from Zion Vault are Android interprocess communication (IPC) calls. For security, Zion Vault authenticates IPC callers and rejects all feature calls from unauthorized callers. Exchange applications must provide SHA-256 key hashes to HTC. The following command can be used to find the SHA-256 key hashes of the key used to sign exchange applications:

keytool -list -v -keystore "your key  store file" -alias "alias name of key store file"

Then find the SHA-256 key hash in the output. For example:

AB:60:AB:0B:EB:C9:A0:68:A0:6A:C2:4A:EF:65:48:2D:90:3C:60:4A:AA:D8:28:D2:D5:C2:D5:66:3B:5F:74:99

2. Android permission

If the exchange application will not access high risk APIs, an alternative way to get Zion Vault authentication is to grant a dangerous permission - "com.htc.wallet.permission.ACCESS_ZION".


3. Binding Zion Vault service

Include IZionWalletServiceAPIs.aidl in your application project, and then add binding service code in your application.

//sample code to bind the explicit service.
Intent intent = new Intent();
intent.setAction("com.htc.wallet.server.ZionService");
intent.setPackage("com.htc.wallet");
context.bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE);

private IZionWalletServiceAPIs mService;
ZionServiceConnection mServiceConnection = new ZionServiceConnection();

private class ZionServiceConnection implements ServiceConnection {

    @Override
    public void onServiceConnected(ComponentName name, IBinder binder) {
        try {
            mService = IZionWalletServiceAPIs.Stub.asInterface(binder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName)
    {}
};

4. Use a working thread to invoke the Zion Vault service API

As Zion Vault service APIs may call native code for intensive tasks, make sure to use a working thread to invoke target functions of the Zion Vault service to avoid ANR issues.

//Example : create a thread to invoke service API
String mSecretIdHash;
public void getSecretIdHash() {
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                mSecretIdHash = mService.getSecretIdHash();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    t.start();
}

Android AsyncTask is recomended for call flow management. Invoke AsyncTask from the mainflow, which wraps the service API call. After AsyncTask is complete, the result can be returned through onPostExecute().

3.2 JSON definition

JSON definitions match those in section 2.2.

3.3 Buying process

Steps
B1. The user buys cryptoassets through the exchange app.
B2. The exchange App requests the public address.
String JsonPayload = mService.getEncAddr(WvHtmlConst.VAL_BTC);

B3. The TrustZone (TZ) encrypts the public address with SrvPub, then signs it with TZPriv_x. (SrvPub (public address) + TZPriv_x (SHA-256 (SrvPub (public address)))):ACCADDR B4. Zion Main responds to B2 with (DIDH_x + ACCADDR), where DIDH_X is the hashed device ID.

String constant
  result key:encAddr, encAddrSigned and secretIdHash
Example
  {
    “apiVer”: “1”,
    "cmd": "getEncAddr",
    "result":
    {
      "resultCode ": 0,
      "errorMessage": "Operation success.",
      "encAddr": "SrvPub(public address)",
      "encAddrSigned": "TZPriv_x(SHA-256(SrvPub(public address)))",
      "secretIdHash": "DIDH_x"
    }
  }

B5. The exchange app sends the payload from B4 to the exchange server for the buying process.
B6. The exchange server sends a request to the HTC server for verification and decryption of the payload from B4.
Web interface
  Method:POST
  URL:/exodus/kws/v1/verifyAddr
  Header name:Exodus-Auth;Header value:Cognito token
  ContentType:application/json
Example
  POST /exodus/kws/v1/verifyAddr
  {
    "provider":"providerId",
    "params":
    {
      "encAddr":"SrvPub(public address)",
      "encAddrSigned":"TZPriv_x(SHA-256(SrvPub(public address)))",
      "secretIdHash":"DIDH_x"
    }
  }

B7. HTC server looks up TZPub_x with the hashed device ID, DIDH_x, to verify ACCADDR. If a valid ACCADDR is found, the HTC server will decrypt it, and then respond to the exchange server with the decrypted public address.
Http Status Code:200
Body:
{
  "result":
  {
    "addr": "public address"
  }
}
Code Body Description
200 Operation done
404 This device is not found secretIdHash not registered
400 Message string Bad Request
400 Message string Verify encAddrSigned signature failed with ServerPrivateKey
400 Message string Hash(encAddr) and signature of encAddrSigned do not match
500 Exception message string Server exception

B8. The exchange server updates the exchange app with detailed purchase information. Once the user confirms the transaction, the exchange server will bind the DIDH_x and public address to the user.

NOTE:
  • After the public address is bound to the user, steps B2-B4 and B6-B7 can be skipped in future transactions.

3.4 Selling process

Steps
S1. The user sells cryptoassets through the exchange app.
S2. The exchange app requests the hashed device ID (DIDH_x) from Zion Main.
String DIDH_x = mService.getSecretIdHash();

S3. Zion Main responds with the hashed device ID (DIDH_x). S4. The exchange app passes DIDH_x to the exchange server to start the selling process. S5. The exchange server sends the DIDH_x and public address to the HTC server for encryption and signature.

Web interface
  Method:POST
  URL:/exodus/kws/v1/encryptAddr
  Header name:Exodus-Auth;Header value:Cognito token
  ContentType:application/json
Example
  POST /exodus/kws/v1/encryptAddr
  {
    "provider":"providerId",
    "params":
    {
      "secretIdHash":"DIDH_x",
      "addr":"public address"
    }
  }

S6. The HTC server encrypts the public address with TZPub_x, and then signs it with SrvPriv:(TZPub_x (public address) + SrvPriv (SHA-256 (TZPub_x (public address)))):ACCADDR
Http Status Code:200
Body:
{
  "result":
  {
    "encAddr":"TZPub_x(public address)",
    "encAddrSigned":"SrvPriv(SHA-256(TZPub_x(public address)))"
  }
}
Code Body Description
200 Operation done
404 This device is not found secretIdHash not registered
400 Message string Bad Request
500 Exception message string Server exception

S7. The exchange server responds with ACCADDR, cryptocurrency type, sale amount, DIDH_x (optional), and an error handling URL for S4.
S8. The exchange app requests the transaction from Zion Main.
int nRet = mService.requestTransaction(json);

A non-zero value of nRet indicates something wrong with that transaction. Check the following example of the string parameter "json":

String constant
  cmd:requestTransaction
  params key:currencyType, amount, encAddr, encAddrSigned and errorHandling
  params value of currencyType:[btc | eth | ltc]
Example
  {
  “apiVer”: “1”,
  "cmd": "requestTransaction",
  "params":
  {
    "currencyType": "btc",
    "amount": "10.1",
    "encAddr": "TZPub_x(public address)",
    "encAddrSigned": "SrvPriv(SHA-256(TZPub_x(public address)))",
    "secretIdHash": "DIDH_x",
    "errorHandling": "https://error_handling"
  }
}

S9. Zion Main will verify ACCADDR with SrvPub, and then decrypt it with TZPriv_x. Once the user confirms the transaction, it will be signed in the TrustZone, and then committed to the blockchain.

3.5 Getting Zion Vault wallet ID

You can also use the same wallet ID as the Zion Vault app with your wallet app with "getZionWalletId", letting you do seed-related operations by calling ZKMA (see “Zion Key Management API” for details). For example: using the wallet ID to get the Zion Vault address and requesting Zion sign a transaction.
Usage:

String result = mService.getZionWalletId();

Example return value:

{
  "apiVer": "1",
  "cmd": "getZionWalletId",
  "result":
  {
    "resultCode": 0,
    "errorMessage": "Operation success.",
    "walletId": 12345678
  }
}

Non-zero resultCode means the operation failed:
resultCode errorMessage
-3002 Zion Vault not created.
-3001 Terminated by user.
-3000 General failure.

The API call will cause a Zion Vault dialog to pop-up asking the user to confirm sharing the Zion Vault ID with the caller app. If the Zion Vault has been already created, the caller app will receive the unique ID after user confirmation.


If Zion Vault has not yet been created, the dialog will inform the user that the wallet ID is not available because the target wallet has not yet been created.

NOTE:
  • getZionWalletId is a high risk API, and requires you to provide the SHA-256 hash of the key used to sign your app to HTC. See 3.1.1 for details.

3.6 Obtaining public keys from Zion Vault

Specific public keys can be retrieved directly from Zion Vault. However, the public key is sensitive information. It must not be stored in app space once operations are complete.
Usage:

String result = mService.getPublicKey(json_payload);

json_payload example:

{
  "params":
  {
    "currencyType": "xlm"
  }
}

Example return value:
{
  "result":
  {
    "resultCode": 0,
    "errorMessage": "Operation success.",
    "publicKey": "GCTRXDS4F23NDBWKDQLEGXQN4DYN5CY2JXFY3M3PHPLU5I7ZGNE62J7Z"
  }
}

A dialog will be triggered the first time a caller app attempts to get the public key for a specific crypto currency. Once the user agrees to share that key with that app, it will not be shown again and the caller app will be able to retrieve the key directly.


Non-zero resultCode means the operation failed:
resultCode errorMessage
-3009 Invalid parameters.
-3005 Coin type is not supported.
-3002 Zion Vault not created.
-3001 Terminated by user.
-3000 General failure.

3.7 Signing a message

Zion Vault provides an API that allows the signing of ethereum messages with the private key. The message must be in HEX.
Converting the message to HEX:

public String byteArrayToHex(byte[] a) {  
    StringBuilder sb = new StringBuilder(a.length * 2);
    for(byte b: a)
        sb.append(String.format("%02x", b));
    return sb.toString();
}

String message = "Hello world";  
String data = byteArrayToHex(message.getBytes());

Usage:

String result = mService.signMessage(json_payload);

json_payload example:

{
  "params":
  {
    "signMessage":
    {
      "path": "m\/44'\/60'\/0'\/0",
      "message":
      {
        "version": 45,
        "data": "48656c6c6f20776f726c64"
      }
    }
  }
}

Example of the return value:
{
  "result":
  {
    "resultCode": 0,
    "errorMessage": "Operation success.",
    "signedMessage": "b4vIBNKAkk6aJkxNM44tVyN1+/XtZpo+VkARCLC8+30+Jas4U7EuvwL+9auu7LnIYwWJwJClRZkN\nyDJTnBJbURs\u003d\n"
  }
}

Non-zero resultCode means the operation failed:
resultCode errorMessage
-3009 Invalid parameters.
-3002 Zion Vault is not yet created.
-3001 Terminated by user.
-3000 General failure.

3.8 Get balance

You can get the currency balance by calling getBalance API with currency type and public key.
Usage:

String result = mService.getBalance(json_payload);

json_payload example:

{
  "params":
  {
    "currencyType": "xlm",
    "publicKey": "GCTRXDS4F23NDBWKDQLEGXQN4DYN5CY2JXFY3M3PHPLU5I7ZGNE62J7Z"
  }
}

Example of the return value:
{
  "result":
  {
    "resultCode": 0,
    "errorMessage": "Operation success.",
    "balance": "0.11112345"
  }
}

Non-zero value resultCode means the operation failed:
resultCode errorMessage
-3009 Invalid parameters.
-3008 Public key mismatch.
-3005 Coin type is not supported.
-3000 General failure.

3.9 Specific operations for Stellar

There are two APIs can be used to manage Stellar tokens if your app supports the Stellar chain.

3.9.1 Availability of Stellar tokens

Use hasAsset to check Stellar token availability.
Usage:

String result = mService.hasAsset(json_payload);

json_payload example:

{
  "params":
  {
    "publicKey": "GCTRXDS4F23NDBWKDQLEGXQN4DYN5CY2JXFY3M3PHPLU5I7ZGNE62J7Z",
    "assetCode": "LOVE",
    "issuerAccountId": "GCJIE4PJSFAMLNN7JZJ4J4AC5LZCF6WLN6S6GAWWVF7O3OYI2LGTLOVE"
  }
}

Example of the return value:
{
  "result":
  {
    "resultCode": 0,
    "errorMessage": "Operation success.",
    "hasAsset": true
  }
}

Non-zero resultCode means the operation failed:
resultCode errorMessage
-3012 Connection to server failed.
-3009 Invalid parameters.
-3008 Public key mismatch.
-3000 General failure.

3.9.2 Activating a Stellar token

addAsset requests Zion Vault to add a Stellar token.
Usage:

String result = mService.addAsset(json_payload);

json_payload example:

{
  "params":
  {
    "publicKey": "GCTRXDS4F23NDBWKDQLEGXQN4DYN5CY2JXFY3M3PHPLU5I7ZGNE62J7Z",
    "assetCode": "LOVE",
    "issuerAccountId": "GCJIE4PJSFAMLNN7JZJ4J4AC5LZCF6WLN6S6GAWWVF7O3OYI2LGTLOVE"
  }
}

Example return value:
{
  "result":
  {
    "resultCode": 0,
    "errorMessage": "Operation success.",
  }
}

Non-zero resultCode means the operation failed:
resultCode errorMessage
-3012 Connect to server failed.
-3011 Operation failed.
-3009 Invalid parameters.
-3008 Public key mismatch.
-3002 Zion Vault not created.
-3000 General failure.

3.10 Get property

For the flexibility of function expansion, Zion Vault SDK provides an API – getProperty - that can be used to get more non-sensitive wallet information. Currently it only supports one property – isWalletExist. If your app is tightly coupled with Zion Vault, make sure to update your app UI according to the value of isWalletExist when your app resumes.
Usage:

String result = mService.getProperty("isWalletExist");

if (null != result) {
    if (result.equals("1")) {
    // wallet exist
    } else {
    // wallet dosen't exist
    }
}

A result of "1" means the wallet exists, while any other value means the wallet doesn't exist.

3.11 Zion Vault initialization

If Zion Vault hasn't previously been initialized, the caller app can trigger the initialization process. The intent of BackToCaller is to let Zion invoke the caller app after wallet setup has been completed.
Usage:

private String START_ZION_OOBE = "com.htc.wallet.StartZionOOBE";

private void launchZionOobe(Context context, Intent BackToCaller) {

    Intent intent = new Intent(START_ZION_OOBE);
    intent.putExtra("BackToCaller", BackToCaller);
    intent.setPackage("com.htc.wallet");
    context.sendBroadcast(intent);
}