Since: API level 28
public abstract class

DualDARClient

extends Service
java.lang.Object
   ↳ android.content.Context
     ↳ android.content.ContextWrapper
       ↳ android.app.Service
         ↳ com.samsung.android.knox.ddar.DualDARClient

Class Overview

This class is used by privileged application to invoke Dual DAR platform apis and receive platform callbacks. Dual DAR Client application extends this class and overrides the abstract callback methods.

NOTE: All the cryptographic key usages and operations are to be done using Android Key Store (AKS) as the keys are bound to the secure hardware and is never exposed to the outside world even in case of Android OS compromise.

Usage

This class is a Service that helps platform discover and bind to the client. Once the class has been extended in client application, declare the class as a service with the following intent filter in AndroidManifest.xml -

     com.samsung.android.knox.ddar.BIND_DUAL_DAR_CLIENT
 

The client application also needs to specify the supported DUAL DAR SDK version in the meta-data tag of the AndroidManifest.xml with name as supportedDualDarSdkVersion and value being the valid DUAL DAR SDK version as String literal. Refer to DualDARPolicy.DUAL_DAR_VERSION_CODES for DUAL DAR SDK version information.

Sample Dual DAR Client AndroidManifest.xml:
     <?xml version="1.0" encoding="utf-8"?>
     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.samsung.android.knox.test">
         <application>
         <meta-data android:name="supportedDualDarSdkVersion"
             android:value="1.1" />
         <service
             android:name="com.samsung.android.knox.test.MyDualDARClient">
             <intent-filter>
                 <action android:name="com.samsung.android.knox.ddar.BIND_DUAL_DAR_CLIENT" />
             </intent-filter>
         </service>
         </application>
     </manifest>
 

Sample Dual DAR Client implementation:

For Container:
     public class MyDualDARClient extends DualDARClient {

          public boolean isSupported(int feature) {
              if(feature == FEATURE_RESET_PASSWORD) {
                  return true;
              }
              return false;
          }

          public boolean onClientBringup() {
              // Install new version of dual dar client library and other files if version mismatch
              if(!getInstalledClientLibraryVersion().equals(CLIENT_VERSION)){
                  List<String> secondaryLibs = new ArrayList<>();
                  secondaryLibs.add(PROP_FILE);
                  if (installLibrary(CLIENT_LIB_FILE_NAME, secondaryLibs, true) != ERROR_NONE) {
                      Log.e(TAG, "Error installing Dual DAR client lib" + RELAY_FILE_NAME);
                      return false;
                  }
              }

              if(!getInstalledClientLibraryVersion().equals(CLIENT_VERSION)){
                  Log.e(TAG, "Version mismatch for Dual DAR client lib. Expected " + CLIENT_VERSION + " but installed " + getInstalledClientLibraryVersion());
                  return false;
              }
              return true;
         }

         public boolean onDualDARSetupForUser(int userId) {
              // Generate Secret (Master Key) and set it to platform for (de)encrypting filesystem
              byte[] masterKey = generateSecretKey();
              Map<String, Byte[]> secrets = new HashMap<>();
              Byte[] secretData = new Byte[masterKey.length];
              for(int i=0;i<masterKey.length;i++) {
                  secretData[i] = masterKey[i];
              }
              secrets.put(MASTER_KEY_ALIAS, secretData);
              setSecret(userId, secrets);

              // Generate and save salt
              byte[] salt = generateAndSaveSalt();

              // Generate and save default auth token
              byte[] defAuthToken = generateAndSaveDefAuthToken();

              // Encrypt master key and save
              saveEncryptedMasterKey(encryptMasterKeyWithPassword(masterKey, defAuthToken, salt));

              return true;
         }

      public boolean onPasswordAuth(int userId, byte[] password) {
          byte[] salt = getSalt();

          if (password == null) {
              //get default password
              password = getDefaultAuthToken();
          }

          // Generate KEK using given auth token
          SecretKey kek = generateKek(password, salt);

          // Decrypt the encrypted master key on disk with KEK and set to platform
          byte[] masterKey = getSecret(kek, getEncryptedMasterKey());

          // Decrypt the encrypted master key on disk with KEK and set to platform
          Map<String, Byte[]> secrets = new HashMap<>();
          Byte[] secretData = new Byte[masterKey.length];
          for(int i=0;i<masterKey.length;i++) {
              secretData[i] = masterKey[i];
          }
          secrets.put(MASTER_KEY_ALIAS, secretData);
          setSecret(userId, secrets);

          return true;
      }

      public boolean onPasswordChange(int userId, byte[] oldPassword, byte[] newPassword) {
          byte[] salt = getSalt();
          if(oldPassword == null) {
              oldPassword = getDefaultAuthToken();
          }
          saveEncryptedMasterKey(reEncryptMasterKeyWithNewPassword(
              getEncryptedMasterKey(), derivedOldSystemToken, derivedNewSystemToken, salt));
          return true;
      }

      public void onDataLockStateChange(int userId, boolean locked) {
          Log.d(TAG, " onDataLockStateChange " + userId + " locked: " + locked);
      }

      public boolean onDualDARDestroyForUser(int userId) {
          destroyKeysAndTokens(userId);
          return true;
      }

      public boolean onSetResetPasswordToken(int userId, byte[] password, long tokenHandle, byte[] token) {
          // Decrypt Master Key using password and re-encrypt with token
          EncryptedMasterKey reEncryptedMasterKey = null;
          byte[] salt = getSalt();
          if(password == null) {
              password = getDefaultAuthToken();
          }

          reEncryptedMasterKey = reEncryptMasterKeyWithNewPassword(
              getEncryptedMasterKey(), password, token, salt);

          // Save token encrypted Master Key
          saveTokenEncryptedMasterKey(reEncryptedMasterKey, userId, tokenHandle);

          // Save token handle
          addTokenHandleAndSave(userId, tokenHandle);
          return true;
      }

      public void onClearResetPasswordToken(int userId, long tokenHandle) {
          // Clear token encrypted Master Key
          removeTokenEncryptedMasterKey(userId, tokenHandle);

          // Clear token handle
          removeTokenHandleAndSave(userId, tokenHandle);
      }

      public boolean onResetPasswordWithToken(int userId, byte[] newPassword, long tokenHandle, byte[] token) {
          // Decrypt Master Key using token and re-encrypt with new password
          EncryptedMasterKey reEncryptedMasterKey = null;
          byte[] salt = getSalt();

          if(newPassword == null) {
              newPassword = getDefaultAuthToken();
          }

          reEncryptedMasterKey = reEncryptMasterKeyWithNewPassword(
          getTokenEncryptedMasterKey(userId, tokenHandle), token, newPassword, salt);

          saveEncryptedMasterKey(reEncryptedMasterKey);
          return true;
      }
 }
 

Since
API level 28
KNOX 3.3

Summary

Constants
int ERROR_FAILURE Signifies FAILURE
int ERROR_NONE Signifies SUCCESS
int FEATURE_RESET_PASSWORD Signifies feature Reset Password
[Expand]
Inherited Constants
From class android.app.Service
From class android.content.Context
From interface android.content.ComponentCallbacks2
Public Constructors
DualDARClient()
Public Methods
List<Integer> getDualDARUsers()
API to get the list of Dual DAR enabled users
String getInstalledClientLibraryVersion()
API to get the installed client crypto library version
int installLibrary(String clientLibPath, List<String> secondaryFilePaths, boolean isAsset)
API to install Dual DAR Client's crypto library and supporting files to Dual DAR Client Daemon.
boolean isSupported(int feature)
API callback from DualDAR platform to check if DualDAR Client App supports a certain feature.
abstract void onClearResetPasswordToken(int userId, long tokenHandle)
API callback to Dual DAR client to clear the reset password token which was provisioned earlier using onSetResetPasswordToken(int, byte[], long, byte[]).
abstract boolean onClientBringup()
API callback for Dual DAR bring up event to inform DualDAR Client App to initiate Dual DAR related activities.
abstract void onDataLockStateChange(int userId, boolean locked)
API callback for Dual DAR workspace(container) Data Lock State change event to inform Dual DAR Client App.
abstract boolean onDualDARDestroyForUser(int userId)
API callback for Dual DAR workspace(container) destroy event to inform Dual DAR Client App.
abstract boolean onDualDARSetupForUser(int userId)
API callback for Dual DAR workspace(container) creation event to inform Dual DAR Client App.
abstract boolean onPasswordAuth(int userId, byte[] password)
API callback for Dual DAR workspace(container) authentication event to inform Dual DAR Client App.
abstract boolean onPasswordChange(int userId, byte[] oldPassword, byte[] newPassword)
API callback for Dual DAR workspace(container) password change event to inform Dual DAR Client App.
abstract boolean onResetPasswordWithToken(int userId, byte[] newPassword, long tokenHandle, byte[] token)
API callback to Dual DAR client to reset password using the token which was provisioned earlier using onSetResetPasswordToken(int, byte[], long, byte[]).
abstract boolean onSetResetPasswordToken(int userId, byte[] password, long tokenHandle, byte[] token)
API callback to Dual DAR client to provision the reset password token which can later be used to reset the workspace(container) password via onResetPasswordWithToken(int, byte[], long, byte[]) .
void setSecret(int userId, Map<StringByte[]> secrets)
API to set secrets for client lib

The secret that is set is passed to the native client library for (en)decrypt operation of filesystem.
[Expand]
Inherited Methods
From class android.app.Service
From class android.content.ContextWrapper
From class android.content.Context
From class java.lang.Object
From interface android.content.ComponentCallbacks
From interface android.content.ComponentCallbacks2

Constants

public static final int ERROR_FAILURE

Since: API level 28

Signifies FAILURE

Since
API level 28
Constant Value: -1 (0xffffffff)

public static final int ERROR_NONE

Since: API level 28

Signifies SUCCESS

Since
API level 28
Constant Value: 0 (0x00000000)

public static final int FEATURE_RESET_PASSWORD

Since: API level 28

Signifies feature Reset Password

Since
API level 28
Constant Value: 1000 (0x000003e8)

Public Constructors

public DualDARClient ()

Since: API level 28

Public Methods

public List<Integer> getDualDARUsers ()

Since: API level 28

API to get the list of Dual DAR enabled users

Returns
  • list of users
Usage
This API will return the list of DualDAR users available in device.
Since
API level 28
KNOX 3.3

public String getInstalledClientLibraryVersion ()

Since: API level 28

API to get the installed client crypto library version

Returns
  • Returns the installed client crypto library version
Since
API level 28
KNOX 3.3

public int installLibrary (String clientLibPath, List<String> secondaryFilePaths, boolean isAsset)

Since: API level 28

API to install Dual DAR Client's crypto library and supporting files to Dual DAR Client Daemon.
Client App's crypto library and supporting files are copied to the the /data/misc/knoxcore/dualdar/ system directory.

Parameters
clientLibPath Path to the client crypto library that is to be installed
secondaryFilePaths Paths to the secondary files that are to be installed. Can be null.
isAsset true if client lib and secondary files reside in assets directory, else false
Returns
Usage
If the secondary file is a shared object library, then compile the client crypto library by setting the RPATH variable to the /data/misc/knoxcore/dualdar/ system directory.

If client lib and files are not in assets directory, then paths are relative to the application's base directory.
Else, paths are relative paths to assets directory

If library and secondary files are in assets directory
     List<String> secondaryLibs = new ArrayList<>();
     secondaryLibs.add("libsecondary.so");
     if (installLibrary("libclient.so", secondaryLibs, true) != ERROR_NONE) {
         Log.e(TAG, "Error installing Dual DAR client lib" + RELAY_FILE_NAME);
         return false;
     }
 


If library and secondary files are in a non-assets directory.
Below example demonstrates when library and secondary files are part of lib folder in the compiled apk.
     String filePrefix = getApplicationInfo().nativeLibraryDir + "/";
     List<String> secondaryLibs = new ArrayList<>();
     secondaryLibs.add(filePrefix + "libsecondary.so");
     if (installLibrary(filePrefix + "libclient.so", secondaryLibs, false) != ERROR_NONE) {
         Log.e(TAG, "Error installing Dual DAR client lib" + RELAY_FILE_NAME);
         return false;
     }
 
Note: This API is to be used in onClientBringup() callback only.
Since
API level 28
KNOX 3.3

public boolean isSupported (int feature)

Since: API level 28

API callback from DualDAR platform to check if DualDAR Client App supports a certain feature.

Returns
  • true if feature supported otherwise false
Usage
Refer to features: FEATURE_RESET_PASSWORD
Note: Incoming callbacks are not guaranteed to be executed on the main thread or any one thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
Since
API level 28
KNOX 3.3

public abstract void onClearResetPasswordToken (int userId, long tokenHandle)

Since: API level 28

API callback to Dual DAR client to clear the reset password token which was provisioned earlier using onSetResetPasswordToken(int, byte[], long, byte[]).

Parameters
userId User ID of the workspace
tokenHandle handle to the reset password token to be cleared
Usage
This API will be invoked when workspace(container) admin is trying to clear an earlier provisioned reset password token. Applicable only if Client supports FEATURE_RESET_PASSWORD.
Since
API level 28
KNOX 3.3

public abstract boolean onClientBringup ()

Since: API level 28

API callback for Dual DAR bring up event to inform DualDAR Client App to initiate Dual DAR related activities. Note: This is not related to Android App lifecycle events.

Returns
  • true on success otherwise false
Usage
This method is invoked when -
  1. Dual DAR enabled workspace(container) is being created
  2. Trying to unlock device from Data Lock state

Note: Incoming callbacks are not guaranteed to be executed on the main thread or any one thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
Since
API level 28
KNOX 3.3

public abstract void onDataLockStateChange (int userId, boolean locked)

Since: API level 28

API callback for Dual DAR workspace(container) Data Lock State change event to inform Dual DAR Client App.

Parameters
userId User ID of the workspace
locked true for locked and false for unlocked
Usage

Note: Incoming callbacks are not guaranteed to be executed on the main thread or any one thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
Since
API level 28
KNOX 3.3

public abstract boolean onDualDARDestroyForUser (int userId)

Since: API level 28

API callback for Dual DAR workspace(container) destroy event to inform Dual DAR Client App.

Parameters
userId User ID of the workspace
Returns
  • true on success otherwise false
Usage
Typical client activities include cleaning the secret and other data structures
Note: Incoming callbacks are not guaranteed to be executed on the main thread or any one thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
Since
API level 28
KNOX 3.3

public abstract boolean onDualDARSetupForUser (int userId)

Since: API level 28

API callback for Dual DAR workspace(container) creation event to inform Dual DAR Client App.

Parameters
userId User ID of the workspace
Returns
  • true on success otherwise false
Usage
Typical client activities include creating a secret and setting up client library
Note: Incoming callbacks are not guaranteed to be executed on the main thread or any one thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
Since
API level 28
KNOX 3.3

public abstract boolean onPasswordAuth (int userId, byte[] password)

Since: API level 28

API callback for Dual DAR workspace(container) authentication event to inform Dual DAR Client App.

Parameters
userId User ID of the workspace
password user password for dualDAR profile
Returns
  • true on successful authentication from the Client Side otherwise false
Usage
Typical client activities include verify password and set secret to platform by invoking setSecret(int, Map).
Note: Incoming callbacks are not guaranteed to be executed on the main thread or any one thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
Since
API level 28
KNOX 3.3

public abstract boolean onPasswordChange (int userId, byte[] oldPassword, byte[] newPassword)

Since: API level 28

API callback for Dual DAR workspace(container) password change event to inform Dual DAR Client App.

Parameters
userId User ID of the workspace
oldPassword old user password for dualDAR profile
newPassword new user password for dualDAR profile
Returns
  • true on success otherwise false
Usage
Typical client activities include updating to new password.
Note: Incoming callbacks are not guaranteed to be executed on the main thread or any one thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
Since
API level 28
KNOX 3.3

public abstract boolean onResetPasswordWithToken (int userId, byte[] newPassword, long tokenHandle, byte[] token)

Since: API level 28

API callback to Dual DAR client to reset password using the token which was provisioned earlier using onSetResetPasswordToken(int, byte[], long, byte[]).

Parameters
userId User ID of the workspace
newPassword new user password to be set for the dualDAR profile
tokenHandle handle to the reset password token passed
token token to be used to reset the password
Returns
  • true on success, false otherwise
Usage
This API will be invoked when workspace(container) admin is trying to reset password using earlier provisioned token. Applicable only if Client supports FEATURE_RESET_PASSWORD.
Since
API level 28
KNOX 3.3

public abstract boolean onSetResetPasswordToken (int userId, byte[] password, long tokenHandle, byte[] token)

Since: API level 28

API callback to Dual DAR client to provision the reset password token which can later be used to reset the workspace(container) password via onResetPasswordWithToken(int, byte[], long, byte[]) .

Parameters
userId User ID of the workspace
password current user password for dualDAR profile
tokenHandle handle to the reset password token passed
token reset password token to be provisioned
Returns
  • true on success, false otherwise
Usage
This API will be invoked when workspace(container) admin is trying to setup a reset password token. Applicable only if Client supports FEATURE_RESET_PASSWORD.
Since
API level 28
KNOX 3.3

public void setSecret (int userId, Map<StringByte[]> secrets)

Since: API level 28

API to set secrets for client lib

The secret that is set is passed to the native client library for (en)decrypt operation of filesystem.

Parameters
userId User ID of the workspace
secrets secret data for client library
Usage
Note: This API is generally used in onDualDARSetupForUser(int) and onPasswordAuth(int, byte[]) callbacks.
Since
API level 28
KNOX 3.3