Ocra HOTP Smartcard
The OCRA container facilitates communication with card readers with initialised OCRA applet. The T1C-JS client library provides function to communicate with the smart card and facilitates integration into a web or native application for the generation of HOTP challenges.
This document describes the functionality provided by the OCRA - temporary documentation - on the T1C-GCL (Generic Connector Library).
Refereces
The following standards must be taken into account:
- HOTP: An HMAC-Based One-Time Password Algorithm
- OCRA: OATH Challenge-Response Algorithm
Generation of an HOTP Value
Additional reference upon the interpretation of the HOTP challenge received from the OCRA applet:
https://tools.ietf.org/html/rfc4226#section-5.3
Resynchronization of the Counter
Additional reference upon the resynchronization of the counter:
https://tools.ietf.org/html/rfc4226#section-7.4
Interface Summary
The Abstract LuxTrust smartcard interface is summarized in the following snippet:
interface AbstractOcra{
allDataFilters():Array<string>;
challenge(body:any, callback:(error:CoreExceptions.RestException, data:any) => void):void;
readCounter(body:any, callback:(error:CoreExceptions.RestException, data:any) => void):void;
verifyPin(body:any, callback:(error:CoreExceptions.RestException, data:any) => void):void;
}
Each interface will be covered on this wiki, accompanied with example code and response objects.
Get the OCRA container object
For more information on how to configure the T1C-JS client library see Client Configuration.
Initialize a gclClient:
GCLLib.GCLClient.initialize(config, function(err, gclClient) {
// gclClient ready to use
});
Get the Ocra container service:
var ocra = gclClient.ocra(reader_id);
Call a function for the Ocra container:
function callback(err,data) {
if(err){console.log("Error:",JSON.stringify(err, null, ' '));}
else {console.log(JSON.stringify(data, null, ' '));}
}
ocra.readCounter(callback);
Obtain the Reader-ID
The constructor for the Ocra container expect as the parameter to be a valid reader-ID. A reader-ID can be obtained from the exposed core functionality, for more information see Core Services.
Core services responds with available card-readers, available card in a card-reader, etc.
For example:
In order to get all connected card-readers, with available cards:
var coreService = gclClient.core();
core.readersCardAvailable(callback);
This function call returns:
{
"data": [
{
"card": {
"atr": "3B7F94000080318065B0850300EF120FFF829000",
"description": [
"Gemalto Classic V4","Juridic Person's Token (PKI)"
]
},
"id": "707e7a6e449f2250",
"name": "OMNIKEY CardMan 5321",
"pinpad": false
}
],
"success": true
}
We notice that a card object is available in the response in the context of a detected reader.
The reader in the example above is VASCO DIGIPASS 870
, has pin-pad capabilities, and there is a card detected with given ATR and some descriptions.
An ATR (Answer To Reset) identifies the type of a smart-card.
The reader, has a unique ID, reader_id
; this reader_id
must be used in order to request functionalities for the Ocra card.
This must be done upon instantiation of the Ocra container:
var emv = gclClient.ocra(reader_id);
All methods for ocra will use the selected reader - identified by the reader_id
.
Reading data
Counter
The counter on the card is incremented each time you generate a new otp with the challenge method.
gclClient.ocra(reader_id).readCounter(callback);
An example callback:
function callback(err,data) {
if(err){
console.log("Error:",JSON.stringify(err, null, ' '));
}
else {
console.log(JSON.stringify(data, null, ' '));
}
}
Response:
{
"counter": "12345678901234567",
"success": true
}
Data Filter
Filter
All data on the smart card can be dumped at once, or using a filter. In order to read all data at once:
var filter = [];
gclClient.ocra().allData(filter,callback);
Response:
{
"data": {
"counter": "zMzMzMzMzN4="
},
"success": true
}
The filter can be used to ask a list of custom data containers. At the moment only the 'counter' is supported:
var filter = ['counter'];
gclClient.ocra().allData(filter,callback);
Response:
{
"data": {
"counter": "zMzMzMzMzN4="
},
"success": true
}
Verify PIN
When the web or native application is responsible for showing the password input, the following request is used to verify a card holder PIN:
var data = {
"pin":"..."
}
gclClient.ocra().verifyPin(data, callback);
Response:
{
"success": true
}
Retries left
After an unsuccessful PIN verification, the error code indicates the number of retries left. For example, when executing:
$("#buttonValidate").on('click', function () {
var _body={};
_body.pin = $("#psw").val(); //only when no pin-pad available
var ocra = connector.ocra(reader_id);
ocra.verifyPin(_body, validationCallback);
});
The following error message will be returned when PIN is wrong:
{
"code": 103,
"description": "Wrong pin, 2 tries remaining",
"success": false
}
After a second wrong PIN verification:
{
"code": 104,
"description": "Wrong pin, 1 try remaining",
"success": false
}
Note that, when the user has at least one retry left, entering a correct PIN resets the PIN retry status
.
Code | Description |
---|---|
103 |
Warning: the user can try twice more to verify his PIN |
104 |
Warning: the user has only 1 retry left |
105 |
Error: the PIN is blocked |
106 | Invalid PIN |
Challenge
To generate a new OTP value, the challenge method can be called. The request requires a valid PIN code and a challenge. The challenge is a base64 encoded bytestring .
var data = {
"pin": "...",
"challenge": "kgg0MTQ4NTkzNZMA"
}
gclClient.ocra().challenge(data, callback);
Response:
{
"data": 50066323,
"success": true
}
Error Handling
Error Object
The functions specified are asynchronous and always need a callback function.
The callback function will reply with a data object in case of success, or with an error object in case of an error. An example callback:
function callback(err,data) {
if(err){
console.log("Error:",JSON.stringify(err, null, ' '));
}
else {
console.log(JSON.stringify(data, null, ' '));
}
}
The error object returned:
{
success: false,
description: "some error description",
code: "some error code"
}
For the error codes and description, see Status codes.