Container Creation

View this page for | |

Before being able to sign a transaction or generate an OTP with the HID Approve SDK, the mobile application has to provision its keys.

Typically, provisioning is triggered by the web application by invoking the HID authentication platform. The provisioning data returned by the server is received by the mobile application by scanning a QR code, or entered manually by the end user.

During provisioning, cryptographic keys designed to address the use cases above, are generated and stored securely. These keys are protected against anti-cloning (that is, they cannot be extracted and used outside the mobile device) and can be further protected with a password or fingerprint.

For further details, see:

  • The HID Approve Security White Paper (available upon request).

Typical Use Case

The mobile application provisions keys as follows:

  1. Create an instance of the Device (HIDDeviceFactory.newInstance).
  2. During this operation, the application provides the SDK with the required server connection configuration (class HIDConnectionConfiguration):

    • Connection timeout
    • Number of connection retries
    • Connection delegates for hostname verification and certificate trust verification (an instance of NSURLSessionDelegate)

    Note:
    • The connection configuration can be changed later using HIDDevice.setConnectionConfiguration or HIDContainer.setConnectionConfiguration.
    • The HIDConnectionConfiguration values are optional.

    Important: If the TLS delegates are not provided, the standard platform default handlers will be used for hostname and validation. These object instances should only be given when implementing a custom behavior in the application such as certificate pinning.

    The default values are:

    PropertyValue
    timeout30 sec
    retry0
    delegateSystem
  3. Create a key container (HIDDevice.createContainer).
  4. The application passes an instance of HIDContainerInitialization to the method with the required data.

    In the simplest use case, the following information is required:

    • Activation code (activationCode) – generated by the HID authentication platform upon customer request (for example, if the end user is using the bank web application to activate a device, the web application requests the server to generate a code).
    • The application can obtain the code by scanning a QR code, or any other means chosen by the integrator.

    • If the specified policy used to protect the generated keys requires a password, the passed HIDProgressListener.onEventReceived callback will be invoked with a HIDPasswordPromptEvent to request a mandatory password from the application during the activation process.
    • Push ID (pushId) – device identifier provided by the notification service. This identifier is communicated to the server to allow it to send notifications.

    The application can customize:

    • The device friendly name as displayed in the HID authentication platform (deviceFriendlyName).
    • The container friendly name that could be displayed by the application (containerFriendlyName).

    Note: If the deviceFriendlyName is not provided, the user-assigned device name will be requested by the SDK. For applications running on Apple iOS 16 (or later), a new user-assigned device name entitlement is required in order to request this information.

    At the end of provisioning, the device is provisioned with the keys.

    Important: The HID Approve SDK implementation requires that all key labels are unique. Otherwise, provisioning will fail.
  5. After the container is created, it is possible to Change Password.

Sample Container Creation on iOS/macOS

Copy
// The EventListener callback will be invoked for the app to provide a password to protect keys, in case the server enforces protection by a password.
// The password creation is app responsibility.
// It will also be used to inform the user about the progress of the container's creation.
class MyEventListener: HIDProgressListener {

    func onEventReceived(_ event: HIDEvent!) -> HIDEventResult! {
        if event is HIDPasswordPromptEvent {
            // Password prompt message
            let pwdEvent = event as! HIDPasswordPromptEvent
            
            // Event parameters will contain information regarding key or contianer requiring a user password
            var type =  ""
            var keyLabel = ""
            var keyUsage = ""
            
            if let params = event.parameters as? [HIDParameter]{
                type = params.first { (p:HIDParameter) -> Bool in return p.key == HID_PARAM_PASSWORD_PROGRESS_EVENT_TYPE }?.value ?? ""
                keyLabel = params.first { (p:HIDParameter) -> Bool in return p.key == HID_PARAM_PASSWORD_PROGRESS_EVENT_KEY_LABEL }?.value ?? ""
                keyUsage = params.first { (p:HIDParameter) -> Bool in return p.key == HID_PARAM_PASSWORD_PROGRESS_EVENT_KEY_USAGE }?.value ?? ""
            }
            
            if(type==HID_PARAM_PASSWORD_PROGRESS_EVENT_TYPE_CONTAINER) {
                NSLog("Event Received: password to protect container")
            } else if(type==HID_PARAM_PASSWORD_PROGRESS_EVENT_TYPE_KEY) {
                NSLog("Event Received: password to protect key (%@, %@)", keyLabel, keyUsage);
            }
            
            // If the password does not respect the policy, the createContainer will fail with an HIDInvalidPassword error.
            // Password policy information is provided with the event
            // ( Refer to Protection Policy for more explanation ) . For example :
            NSLog("Event Received: PasswordPromptEvent - maxLowerCase : %@ ", pwdEvent.passwordPolicy.maxLowerCase())
            NSLog("Event Received: PasswordPromptEvent - minLowerCase : %@ ", pwdEvent.passwordPolicy.minLowerCase())

            // If your container is configured to be protected by a password, this is when the user will be requested to enter it through the PasswordPromptResult API
            let userPassword = self.passwordPrompt()

            // Continue: operation can continue
            // Cancel: operation is cancelled by the user.
            // Abort: operation is aborted by the app
            return HIDPasswordPromptResult(code: Continue, andPassword: userPassword)
        
        } else { // progress message
            // the parameters array provide the state of the provisionning and the percent of progression
            if let params = event.parameters as? [HIDParameter] {
                        
                let messageParameter = params.first { (p:HIDParameter) -> Bool in return p.key == HID_PARAM_PROGRESSEVENT_MESSAGE }
                let percentParameter = params.first { (p:HIDParameter) -> Bool in return p.key == HID_PARAM_PROGRESSEVENT_PERCENT }
                
                let message = messageParameter?.value ?? ""
                let percent = percentParameter?.value ?? ""
                    
                NSLog("Event received: %@ (%@)", message, percent)
                return HIDEventResult(code:Continue)
            }
        }
    }
}

...

    do{
        // Get Device instance with default connection configuration
        let config = HIDConnectionConfiguration()
        let deviceFactory = HIDDeviceFactory.factory() as! HIDDeviceFactory
        let device = try deviceFactory.newInstance(config)
        
        // ContainerInitialization structure contains configuration information for the new container
        let containerInitialization = HIDContainerInitialization()
        // QR code or payload of push notification
        containerInitialization.activationCode = scanContent
        // Identifier given by the push notification system
        // Should be left as nil if notifications are disabled
        containerInitialization.pushId = sPushID;
        
        // Container creation
        // we assume session key is not password protected (password nil)
        let listener = MyEventListener()
        let container = try device.createContainer(containerInitialization, withSessionPassword: nil, with: listener)
    }
    catch let error as NSError{
        NSLog("Failed to create container: %@",error.localizedDescription);
    }
}
Copy
// The EventListener callback will be invoked for the app to provide a password to protect keys, in case the server enforces protection by a password.
// The password creation is app responsibility.
// It will also be used to inform the user about the progress of the container's creation.
@interface MyEventListener : NSObject<HIDProgressListener>
@end
@implementation MyEventListener
-( HIDEventResult*)onEventReceived:(NSObject<HIDEvent>*)event {

    if ([event isKindOfClass:[HIDPasswordPromptEvent class]]) {
        
        // Password prompt message
        HIDPasswordPromptEvent* pPwdEvent = (HIDPasswordPromptEvent*)event;

        // Event parameters will contain information regarding key or container requiring a user password
        NSArray* params = [event parameters];
        NSString* type;
        NSString* keyLabel;
        NSString* keyUsage;
        for (HIDParameter* p in params) {
            if ( [[p key] caseInsensitiveCompare:HID_PARAM_PASSWORD_PROGRESS_EVENT_TYPE]==NSOrderedSame)
                type = [p value];
            else if ( [[p key] caseInsensitiveCompare:HID_PARAM_PASSWORD_PROGRESS_EVENT_KEY_LABEL]==NSOrderedSame)
                keyLabel = [p value];
            else if ( [[p key] caseInsensitiveCompare:HID_PARAM_PASSWORD_PROGRESS_EVENT_KEY_USAGE]==NSOrderedSame)
                keyUsage = [p value];
        }
        if (type == HID_PARAM_PASSWORD_PROGRESS_EVENT_TYPE_CONTAINER)
            NSLog(@"Event Received: password to protect container");
        else if (type == HID_PARAM_PASSWORD_PROGRESS_EVENT_TYPE_KEY)
            NSLog(@"Event Received: password to protect key (%@, %@)", keyLabel, keyUsage);

        // If the password does not respect the policy, the createContainer will fail with an HIDInvalidPassword error.
        // Password policy information is provided with the event
        // ( Refer to Protection Policy for more explanation ) . For example :
        NSLog(@"Event Received: PasswordPromptEvent - maxLowerCase : %@ ", [pPwdEvent.passwordPolicy.maxLowerCase]);
        NSLog(@"Event Received: PasswordPromptEvent - minLowerCase : %@ ", [pPwdEvent.passwordPolicy.minLowerCase]);

        // If your container is configured to be protected by a password, this is when the user will be requested to enter it through the PasswordPromptResult API
        NSString* userPassword = [self passwordPrompt];

        // Continue: operation can continue
        // Cancel: operation is cancelled by the user.
        // Abort: operation is aborted by the app
        return [[HIDPasswordPromptResult alloc] initWithCode:Continue andPassword:userPassword];
    }
    else { // progress message
    
        // the parameters array provide the state of the provisionning and the percent of progression
        NSArray* params = [event parameters];
        NSString* message;
        NSString* percent;
        for (HIDParameter* p in params) {
            if ( [[p key] caseInsensitiveCompare:HID_PARAM_PROGRESSEVENT_MESSAGE]==NSOrderedSame)
                message = [p value];
            if ( [[p key] caseInsensitiveCompare:HID_PARAM_PROGRESSEVENT_PERCENT]==NSOrderedSame)
                percent = [p value];
        }
        NSString* text = [NSString stringWithFormat:@"%@ (%@)", message, percent];
        NSLog(@"Event Received: %@", text);
    }

    // Continue
    return [[HIDEventResult alloc] initWithCode:(Continue)];
@end

...

    // Get Device instance with default connection configuration
    NSError* error;
    HIDConnectionConfiguration* connectionConfig = [[HIDConnectionConfiguration alloc] init];
    id<HIDDevice> pDevice = [[HIDDeviceFactory alloc] newInstance:connectionConfig error:&error];

    // ContainerInitialization structure contains configuration information for the new container
    HIDContainerInitialization* containerInitialization = [[HIDContainerInitialization alloc] init];
    // QR code or payload of push notification
    containerInitialization.activationCode =  [[NSString alloc] initWithData:scanContent encoding:NSUTF8StringEncoding];
    // Identifier given by the push notification system
    // Should be left as nil if notifications are disabled
    containerInitialization.pushId = sPushID;

    // Container creation 
    // we assume session key is not password protected (password nil)
    MyEventListener* listener = [[MyEventListener alloc] init];
    id<HIDContainer> pContainer = [pDevice createContainer:containerInitialization withSessionPassword:nil withListener:listener error:&error];

Degraded Use Case

If the application is unable to get an activation code (for example, the device does not have a camera to scan the QR code), then it can prompt the end user to enter the required information (for example, provided by the HID authentication platform, and displayed in the customer web application):

  • User identifier (userId) – the user code in the HID authentication platform.
  • Invite code (inviteCode) – this is a short code provided by the server to ensure the Container creation is genuine.
  • Server URL (serverUrl) – the HID authentication platform short URL in the format <host>[:<port>]/<domain>.

Container Replacement

A container is uniquely identified by the user identifier, the server URL and the server domain used to create it.

If a container exists, and you provision a new one with the same user identifier, server URL and server domain, the previous one will be deleted.

Otherwise a new container will be provisioned for the user.