OTP Generation
Once the keys are provisioned, the device is ready to generate One-Time Passwords. OTPs are typically used to authenticate an end user to an application that delegates authentication to the HID authentication platform.
The HID Approve SDK supports the following synchronous OTP algorithms:
- HOTP (RFC 4226 HMac-Based One-Time Password Algorithm)
- TOTP (RFC 6238 Time-Based One-Time Password Algorithm)
It also supports the following asynchronous (challenge/response) algorithm:
- OCRA (RFC 6287 OATH Challenge-Response Algorithm)
Synchronous OTP Generation
The mobile application generates an OTP as follows:
- Create an instance of the Device (
HIDDeviceFactory.newInstance
). - Get the instance of the Container (
HIDDevice.findContainers
). - Find the key with usage
HIDKEY_PROPERTY_USAGE_OTP
(HIDContainer.findKeys
). - Check if the protection policy is a password policy, and prompt the user as required (
HIDKey.getProtectionPolicy
). - Get the OTP generator (
HIDKey.getDefaultOTPGenerator
). - Verify it is a
HIDSyncOTPGenerator
and get the OTP (HIDSyncOTPGenerator.getOTP
).
do{
// Find keys available for the usage OTP; if several are available, we need to distinguish them with their labels
let usage = HIDParameter(string: HID_KEY_PROPERTY_USAGE_OTP, forKey: HID_KEY_PROPERTY_USAGE)
let filter:[Any]? = [usage!]
if let keys = try myContainer?.findKeys(filter) as? [HIDKey] {
for key in keys {
// To identify the protection policy used : look at the type of the protectionPolicy.
// ProtectionPolicy.PolicyType.DEVICE : no password required
// ProtectionPolicy.PolicyType.PASSWORD : password required
// ProtectionPolicy.PolicyType.BIOPASSWORD : see the protection policy documentation for usage.
let policyType = try key.getProtectionPolicy().policyType()
let algo = try key.getAlgorithm()
let label = try key.getProperty(HID_KEY_PROPERTY_LABEL)
NSLog("Found key: PolicyType=%@, Algorithm=%@, Label=%@",
policyType.rawValue, algo, label)
}
let key = keys.first! as HIDKey
// Get default OTP generator for this key.
let otpGenerator = try key.getDefaultOTPGenerator()
// Get the next OTP.
// We assume the generator is Synchronous (HOTP or TOTP algorithms)
// We assume the key is not password protected (password nil)
let syncOtpGenerator = otpGenerator as! HIDSyncOTPGenerator
let otp = try syncOtpGenerator.getOTP(nil)
}
}
catch let error as NSError{
NSLog("Failed to generate OTP: %@",error.localizedDescription);
}
}
// Find keys available for the usage OTP; if several are available, we need to distinguish them with their labels
NSMutableArray* filterKeys = [[NSMutableArray alloc] init];
[filterKeys addObject:[HIDParameter parameterWithString:HID_KEY_PROPERTY_USAGE_OTP forKey:HID_KEY_PROPERTY_USAGE]];
NSArray* keys = [pContainer findKeys:filterKeys error:&error];
// Find keys for usage OTP; here we assume there is only one
// If more than one is configured, then label can be used to distinguish them
for (id<HIDKey> key in keys) {
// To identify the protection policy used : look at the type of the protectionPolicy.
// ProtectionPolicy.PolicyType.DEVICE : no password required
// ProtectionPolicy.PolicyType.PASSWORD : password required
// ProtectionPolicy.PolicyType.BIOPASSWORD : see the protection policy documentation for usage.
NSLog(@"Found key: PolicyType=%@, Algorithm=%@, Label=%@",
[[key getProtectionPolicy:nil] policyType],
[key getAlgorithm]
[key getProperty:HID_KEY_PROPERTY_LABEL error:nil]
);
}
id<HIDKey> pKey = [keys objectAtIndex:0];
// Get default OTP generator for this key.
id<HIDOTPGenerator> pOTPGenerator = [pKey getDefaultOTPGenerator:&error];
// Get the next OTP.
// We assume the generator is Synchronous (HOTP or TOTP algorithms)
// We assume the key is not password protected (password nil)
NSString* otp = [((id<HIDSyncOTPGenerator>)pOTPGenerator) getOTP:nil error:&error];
Challenge / Response Authentication
For now, only OCRA algorithm is supported. The algorithm provides mechanisms that leverage the HOTP algorithm and offer one-way and electronic signature capabilities (these are called algorithm modes).
Refer to RFC 6287 for further details. This section only describes how to handle the two algorithm modes for authentication with the HID Approve SDK.
Getting Asynchronous OTP Generator
- Regardless of the algorithm mode, the first operation is to get the
HIDAsyncOTPGenerator
instance (see code snippets for synchronous OTP generation above). - Create an instance of the Device (
HIDDeviceFactory.getDevice
). - Get the instance of the Container (
HIDDevice.findContainers
). - Find the key with usage
HIDKEY_PROPERTY_USAGE_OTP
(HIDContainer.findKeys
). - Check if the protection policy is a password policy, and prompt the user as required (
HIDKey.getProtectionPolicy
). - Get the OTP generator (
HIDKey.getDefaultOTPGenerator
). - Verify it is a
HIDAsyncOTPGenerator
.
One-Way Authentication
For this use case, the OTP generator computes a response from a challenge provided by the authentication server (typically, the challenge is displayed by the web application of the service provider, or sent by email or sms).
- Check
HIDAUTHMODE_CHALLENGE_RESPONSE
is supported by the key: - Get the algorithm parameters (
HIDOTPGenerator.getAlgorithmParameters
– will be an instance ofHIDOCRAParameters
). - Get the supported algorithm modes (
HIDOCRAAlgorithmParameters.getModes
). - Get the server OCRA suite – because the challenge comes from the server (
HIDOCRAParameters.getServerOcraSuite
). - Check whether PIN or session data are required (
HIDOCRASuite.isPinRequired
,HIDOCRA.isSessionRequired
). -
Compute the response (
HIDAsyncOTPGenerator.computeResponse
).
If one or both are required, it is the application’s responsibility to get them.
do{
// Get default OTP generator for this key.
let otpGenerator = try otpKey.getDefaultOTPGenerator()
// Here, we assume the generator is Asynchronous (OCRA algorithm)
let asyncOtpGenerator = otpGenerator as! HIDAsyncOTPGenerator
// With an ocra algorithm, inputs data depends of the algorithm used.
// You can get parameters specific to the generator and key.
// The type of parameters depends on the generator name
NSLog("Found generator: Name=%@, Type=%@",
asyncOtpGenerator.getName(), asyncOtpGenerator.getType())
// Get the authentication modes supported from parameters
let algoParameters = asyncOtpGenerator.getAlgorithmParameters() as! HIDOCRAAlgorithmParameters
NSLog("Generator modes: %@", algoParameters.getModes()!.description)
// We assume the challenge/response mode is supported.
// We assume the OTP key is not password protected. ( password null )
// Check if pin or session is required and set it in InputOCRAParameters if so
// You can validate the server challenge against the expected format
let ocraInput = HIDOCRAInputAlgorithmParameters()
let ocraSuite = algoParameters.getClientOCRASuite()
if(ocraSuite?.isPinRequired() == true) {
NSLog("Pin hash: %@", ocraSuite!.getPINHashAlgo())
ocraInput.setPin(myPin)
}
if(ocraSuite?.isSessionRequired() == true) {
NSLog("SessionInfo is required")
ocraInput.setSession(mySessionData)
}
NSLog("Challenge format: %@", ocraSuite!.getChallengeFormat())
// Compute the response with the provided challenge.
// We assume the key is not password protected (password nil)
let otp = try asyncOtpGenerator.computeResponse(nil, withChallenge: myChallenge, withInputParams: ocraInput)
}
catch let error as NSError{
NSLog("Failed to generate response: %@",error.localizedDescription);
}
}
// Get default OTP generator for this key.
// Here, we assume the generator is Asynchronous (OCRA algorithm)
id<HIDAsyncOTPGenerator> asyncOtpGenerator = (id<HIDAsyncOTPGenerator>)[pOtpKey getDefaultOTPGenerator:&error];
// With an ocra algorithm, inputs data depends of the algorithm used.
// You can get parameters specific to the generator and key.
// The type of parameters depends on the generator name
NSLog(@"Found generator: Name=%@, Type=%@", [asyncOtpGenerator getName], [asyncOtpGenerator getType] );
// Get the authentication modes supported from parameters
id<HIDOCRAAlgorithmParameters> algoParameters = (id<HIDOCRAAlgorithmParameters>)[asyncOtpGenerator getAlgorithmParameters];
NSLog(@"Generator modes: %@", [algoParameters getModes] );
// We assume the challenge/response mode is supported.
// We assume the OTP key is not password protected. ( password null )
// Check if pin or session is required and set it in InputOCRAParameters if so
// You can validate the server challenge against the expected format
HIDOCRAInputAlgorithmParameters *ocraInput = [[HIDOCRAInputAlgorithmParameters alloc]init];
id<HIDOCRASuite> ocraSuite = [algoParameters getClientOCRASuite];
if ([ocraSuite isPinRequired]) {
NSLog(@"Pin hash: %@ ", [ocraSuite getPINHashAlgo]);
[ocraInput setPin:myPin];
}
if ([ocraSuite isSessionRequired]) {
NSLog(@"SessionInfo is required");
[ocraInput setSession:mySessionData];
}
NSLog(@"Challenge format: %@", [ocraSuite getChallengeFormat]);
// Compute the response with the provided challenge.
// We assume the key is not password protected (password nil)
NSString* otp = [asyncOtpGenerator computeResponse:nil withChallenge:myChallenge withInputParams:ocraInput error:&error];
Signature Authentication
For this use case, the end user supplies a number of strings to concatenate in order to form a challenge client-side. For example, to sign a bank transaction, the bank portal might ask the end user to sign the:
- Account number (“11223344”)
- Amount (“100EUR”)
- Beneficiary (“JohnDoe”)
The mobile application can use the SDK to format the challenge from these input strings (HIDAsyncOTPGenerator.formatSignatureChallenge
) using the values (not the associated labels).
The SDK formats the challenge based on the server OCRA suite configured server-side, following OCRA Standalone Client Profile v1.0 (section 3.2.4, c, d and e).
The OTP generator computes the response based on this challenge.
do{
// We assume the signature mode is supported.
// We assume required parameter set :
let ocraInput = HIDOCRAInputAlgorithmParameters(myPin, sessionInfo: mySessionData)
// The end-user has filled the form, input strings are in inputStrings, NSArray of NSString
// Format the challenge
let challenge = try asyncOtpGenerator.formatSignatureChallenge(inputStrings)
// Compute the signature for one-way or two-way signature. For one-way signature, clientChallenge is empty.
// We assume the key is not password protected (password nil)
let otp = try asyncOtpGenerator.computeSignature(nil, withSigChallenge: myChallenge, withClientChallenge: nil, withInputParams: ocraInput)
}
catch let error as NSError{
NSLog("Failed to generate signature: %@",error.localizedDescription);
}
// We assume the signature mode is supported.
// We assume required parameter set :
HIDOCRAInputAlgorithmParameters *ocraInput = [[HIDOCRAInputAlgorithmParameters alloc]init:myPin sessionInfo:mySessionData];
// The end-user has filled the form, input strings are in inputStrings, NSArray of NSString
// Format the challenge
NSString* challenge = [asyncOTPGenerator formatSignatureChallenge:inputStrings error:&error];
// Compute the signature for one-way or two-way signature. For one-way signature, clientChallenge is empty.
// We assume the key is not password protected (password nil)
NSString* otp = [asyncOtpGenerator computeSignature:nil withSigChallenge:myChallenge withClientChallenge:nil withInputParams:ocraInput error:&error];