linotp.lib.tokenclass module

This file containes the standard token definitions: - OCRATokenClass

It also contains the base class “TokenClass”, that you may use to define your own tokenclasses.

You can add your own Tokens by adding the modules comma seperated to the directive ‘linotpTokenModules’ in the linotp.ini file.

depends on several modules from linotp.lib but also in case of VascoTokenClass on linotp.lib.ImportOTP.vasco

class linotp.lib.tokenclass.OcraTokenClass(aToken)[source]

Bases: linotp.lib.tokenclass.TokenClass

OcraTokenClass implement an ocra compliant token

used from Config
OcraMaxChallenges: number of open challenges per token if None: 3 OcraChallengeTimeout: timeout definition like 1D, 2H or 3M if None: 1M OcraDefaultSuite: if none :’OCRA-1:HOTP-SHA256-8:C-QN08’ QrOcraDefaultSuite: if none :’OCRA-1:HOTP-SHA256-8:C-QA64’

algorithm Ocra Token Rollout: two phases of rollout:

1. https://linotpserver/admin/init?
    type=ocra&
    genkey=1&
    sharedsecret=1&
    user=BENUTZERNAME&
    session=SESSIONKEY

    =>> "serial" : SERIENNUMMER, "sharedsecret" : DATAOBJECT,
                          "app_import" : IMPORTURL
    - genSharedSecret - vom HSM oder urandom ?
    - app_import : + linotp://
                + ocrasuite ->> default aus dem config:
                                            (DefaultOcraSuite)
                + sharedsecret (Länge wie ???)
                + seriennummer
    - seriennummer: uuid
    - token wird angelegt ist aber nicht aktiv!!! (counter == 0)


2. https://linotpserver/admin/init?
    type=ocra&
    genkey=1&
    activationcode=AKTIVIERUNGSCODE&
    user=BENUTZERNAME&
    message=MESSAGE&
    session=SESSIONKEY

    =>> "serial" : SERIENNUMMER, "nonce" : DATAOBJECT,
        "transactionid" : "TRANSAKTIONSID, "app_import" : IMPORTURL

    - nonce - von HSM oder random ?
    - pkcs5 - kdf2
    - es darf zur einer Zeit nur eine QR Token inaktiv
           (== im Ausrollzustand) sein !!!!!
    der Token wird über den User gefunden
    - seed = pdkdf2(nonce + activcode + shared secret)
    - challenge generiern - von urandom oder HSM

3. check_t
    - counter ist > nach der ersten Transaktion
    - if counter >= 1: delete sharedsecret löschen
autosync(ocraSuite, passw, challenge)[source]

try to resync a token automaticaly, if a former and the current request failed

Parameters:
  • ocraSuite (ocra object) – the ocraSuite of the current Token
  • passw
challenge(data, session='', typ='raw', challenge=None)[source]

the challenge method is for creating an transaction / challenge object

remark: the transaction has a maximum lifetime and a reference to
the OcraSuite token (serial)
Parameters:
  • data (string or None) – data, which is the base for the challenge or None
  • session (string) – session support for ocratokens
Returns:

challenge response containing the transcation id and the challenge for the ocrasuite

:rtype : tuple of (transId(string), challenge(string))

checkOtp(passw, counter, window, options=None)[source]

checkOtp - standard callback of linotp to verify the token

Parameters:
  • passw (string) – the passw / otp, which has to be checked
  • counter (int) – the start counter
  • window (int) – the window, in which the token is valid
  • options (dict) – options contains the transaction id, eg. if check_t checks one transaction this will support assynchreonous otp checks (when check_t is used)
Returns:

verification counter or -1

Return type:

int (-1)

classmethod getClassPrefix()[source]
classmethod getClassType()[source]

getClassType - return the token type shortname

Returns:‘ocra’
Return type:string
getInfo()[source]

getInfo - return the status of the token rollout

Returns:info of the ocra token state
Return type:dict
getInitDetail(params, user=None)[source]

to complete the token normalisation, the response of the initialiastion should be build by the token specific method, the getInitDetails

getOcraSuiteSuite()[source]
getQROcraSuiteSuite - return the QR Ocra Suite
  • if none, it will return the default
Returns:Ocrasuite of token
Return type:string
getQRImageData(response_detail)[source]
getQROcraSuiteSuite()[source]
getQROcraSuiteSuite - return the QR Ocra Suite
  • if none, it will return the default
Returns:QROcrasuite of token
Return type:string
getStatus(transactionId)[source]

getStatus - assembles the status of a transaction / challenge in a dict

{ “serial”: SERIENNUMMER1,
“transactionid”: TRANSACTIONID1, “received_tan”: true, “valid_tan”: true, “failcount”: 0

}

Parameters:transactionId (string) – the transaction / challenge id
Returns:status dict
Return type:dict
classmethod getTransaction(transId)[source]

getTransaction - lookup for the challenge object of the given id

Parameters:transId (string) – challenge identifier
Returns:the challenge data object
Return type:OcraChallenge
classmethod getTransactions4serial(serial, currentOnly=False)[source]
getTransactions4serial - give all challenges for a
given token serial number
Parameters:
  • serial (string) – token serial identifier
  • currentOnly (boolean flag) –

    boolean Flag to return all Challenges (like for status request)

    or to return the eldest open transaction / challenge
Returns:

return a list of Challenges

Return type:

OcraChallenge obejct list

classmethod get_helper_params_post(param, user=None)[source]
is_challenge_response(passw, user, options=None, challenges=None)[source]

check, if the request contains the result of a challenge

Parameters:
  • passw – password, which might be pin or pin+otp
  • user – the requesting user
  • options – dictionary of additional request parameters
Returns:

returns true or false

classmethod maxChallengeJanitor(transId=None, serial=None)[source]
maxChallengeJanitor - remove for one token (serial) all challengens
but the last ones
Parameters:
  • transId (string) – the current transaction, which provides a the lookup for the serial number
  • serial (string) – the serial number of the token
Returns:

  • nothing

classmethod maxChallengeRequestJanitor()[source]
maxChallengeRequestJanitor - remove all transactions / challenges
which have been made more than maxChallengeRequests
Returns:
  • nothing
resync(otp1, otp2, options=None)[source]
  • for the resync to work, we take the last two transactions and their challenges
  • for each challenge, we search forward the sync window length
signData(data)[source]

sign the received data with the secret key

Parameters:data – arbitrary string object
Returns:hexlified signature of the data
statusValidationFail()[source]

statusValidationFail - callback to enable a status change,

will be called if the token verification has failed

:return - nothing

statusValidationSuccess()[source]

statusValidationSuccess - callback to enable a status change,

remark: will be called if the token shas been succesfull verified

Returns:
  • nothing
classmethod timeoutJanitor()[source]

timeoutJanitor - remove all outdated transactions / challenges

Returns:
  • nothing
update(params, reset_failcount=True)[source]

update: add further defintion for token from param in case of init

class linotp.lib.tokenclass.StatefulTokenMixin[source]

Bases: object

A mixin used by token types that have different rollout states (e.g. QRToken and OCRA)

change_state(state_id)[source]

changes the state of this token

Parameters:state_id – The new state_id this token should have
current_state

signifies the current state of the token

ensure_state(state_id)[source]

a barrier method to ensure that a token has a certain state.

Parameters:state_id – The state the token has to be in
Raises:TokenStateError – If state_id is different from the current state of this token
ensure_state_is_in(valid_state_ids)[source]

a barrier method to ensure that the token state is in a list of valid_states

Parameters:valid_state_ids – A list of allowed states
Raises:TokenStateError – If token state is not in the list of valid states
class linotp.lib.tokenclass.TokenClass(token)[source]

Bases: object

addToInfo(key, value)[source]
addToSession(Session)[source]
addToTokenInfo(key, value)[source]
authenticate(passw, user, options=None)[source]

This is the method that verifies single shot authentication like they are done with push button tokens.

It is a high level interface to support as well other tokens, which do not have a pin and otp seperation - they could overwrite this method

remarks: we have to call the global methods (check_pin,++) as they take the pin policies into account

Parameters:
  • passw (string) – the passw which could be pin+otp
  • user (User object) – The authenticating user
  • options ((dict)) – dictionary of additional request parameters
Returns:

returns tuple true or false for the pin match, the otpcounter (int) and the reply (dict) that will be added as additional information in the JSON response of /validate/check.

challenge_janitor(matching_challenges, challenges)[source]

This is the default janitor for the challenges of a token.

The idea is to delete all challenges, which have an id lower than the matching one. Other janitors could be implemented on a token base and overwrite this behaviour.

Remarks: In later versions this will be the place to hook a dynamically loaded default token specific janitor.

Parameters:
  • matching_challenges (list) – the last matching challenge
  • challenges (list) – all current challenges
Returns:

list of all challenges, which should be deleted

checkOtp(anOtpVal1, counter, window, options=None)[source]

This checks the OTP value, AFTER the upper level did the checkPIN

return:
counter of the matching OTP value.
checkPin(pin, options=None)[source]

checkPin - test is the pin is matching

Parameters:
  • pin – the pin
  • options – additional optional parameters, which could be token specific
Returns:

boolean

checkResponse4Challenge(user, passw, options=None, challenges=None)[source]

This method verifies if the given passw matches any existing challenge of the token.

It then returns the new otp_counter of the token and the list of the matching challenges.

In case of success the otp_counter needs to be > 0. The matching_challenges is passed to the method challenge_janitor() to clean up challenges.

Parameters:
  • user (User object) – the requesting user
  • passw (string) – the password (pin+otp)
  • options (dict) – additional arguments from the request, which could be token specific
  • challenges (list) – A sorted list of valid challenges for this token.
Returns:

tuple of (otpcounter and the list of matching challenges)

check_auth_counter()[source]

This function checks the count_auth and the count_auth_success

check_authenticate(user, passw, options=None)[source]

simple authentication with pin+otp

Parameters:
  • passw – the password, which should be checked
  • options – dict with additional request parameters
Returns:

tuple of matching otpcounter and a potential reply

check_challenge_response(challenges, user, passw, options=None)[source]

This function checks, if the given response (passw) matches any of the open challenges

to prevent the token author to deal with the database layer, the token.checkResponse4Challenge will recieve only the dictionary of the challenge data

Parameters:
  • challenges – the list of database challenges
  • user – the requesting use
  • passw – the to password of the request, which must be pin+otp
  • options – the addtional request parameters
Returns:

tuple of otpcount (as result of an internal token.checkOtp) and additional optional reply

check_otp_exist(otp, window=None, user=None, autoassign=False)[source]

checks if the given OTP value is/are values of this very token. This is used to autoassign and to determine the serial number of a token.

check_standard(passw, user, options=None)[source]

do a standard verification, as we are not in a challengeResponse mode

the upper interfaces expect in the success the otp counter or at least 0 if we have a success. A -1 identifies an error

Parameters:
  • passw – the password, which should be checked
  • options – dict with additional request parameters
Returns:

tuple of matching otpcounter and a potential reply

check_token(passw, user, options=None, challenges=None)[source]

validate a token against the provided pass

Raises:

“challenge not found”, if a state is given and no challenge is found for this challenge id

Parameters:
  • passw – the password, which could either be a pin, a pin+otp or otp
  • user – the user which the token belongs to
  • options – dict with additional request parameters
  • challenges
Returns:

tuple of otpcounter and potential reply

check_validity_period()[source]

This checks if the datetime.datetime.now() is within the validity period of the token.

Returns either True/False

static copy_pin(src, target)[source]
createChallenge(transactionid, options=None)[source]

This method creates a challenge, which is submitted to the user. The submitted challenge will be preserved in the challenge database.

This method is called after the method initChallenge().

Parameters:
  • transactionid – the id of this challenge
  • options (dict) – the request context parameters / data
Returns:

tuple of (bool, message, data, attributes)

The return tuple builds up like this:

bool if submit was successfull; message which is displayed in the JSON response; data is preserved in the challenge; additional attributes, which are displayed in the JSON response.

deleteToken()[source]
enable(enable)[source]
flush()[source]
getAuthDetail()[source]
classmethod getClassPrefix()[source]
classmethod getClassType()[source]
getCounterWindow()[source]
getFailCount()[source]
getFromTokenInfo(key, default=None)[source]
getHashlib(hLibStr)[source]
getInfo()[source]

getInfo - return the status of the token rollout

Returns:return the status dict.
Return type:dict
getInitDetail(params, user=None)[source]

to complete the token normalisation, the response of the initialiastion should be build by the token specific method, the getInitDetails

getMaxFailCount()[source]
getOfflineInfo()[source]
getOtp(curtTime='')[source]

The default token does not support getting the otp value will return something like:

1, pin, otpval, combined

a negative value is a failure.

getOtpCount()[source]
getOtpCountWindow()[source]
getOtpLen()[source]
getPin()[source]
Returns:the value of the pin- if it is stored encrypted
getQRImageData(response_detail)[source]
getRealms()[source]
getSerial()[source]
getSyncWindow()[source]
getTokenInfo()[source]
getType()[source]
getUser()[source]

get the user info of the token

Returns:tuple of user id, user resolver and resolver class
getUserId()[source]
get_challenge_validity()[source]

This method returns the token specific challenge validity

Returns:int - validity in seconds
get_count_auth()[source]
get_count_auth_max()[source]
get_count_auth_success()[source]
get_count_auth_success_max()[source]
classmethod get_helper_params_post(params, user)[source]

hook method which gets called with the parameters given to admin/init and the user that possibly gets created from it. It returns a dictionary which will be added to the helper_params. In contrast to get_helper_params_pre this function will be called _after_ the user object gets created from the parameters

Params params:the request parameters supplied to admin/init
Params user:the user object created from the request parameters (None if no user was specified in the request)
Returns:dictionary with additional helper params
classmethod get_helper_params_pre(params)[source]

hook method which gets called with the parameters given to admin/init and returns a dictionary which will be added to the helper_params. In contrast to get_helper_params_post this function will be called _before_ the user object gets created from the parameters

Params params:the request parameters supplied to admin/init
Returns:dictionary with additional helper params
get_multi_otp(count=0, epoch_start=0, epoch_end=0, curTime=None)[source]

This returns a dictionary of multiple future OTP values of a token.

parameter
count - how many otp values should be returned epoch_start - time based tokens: start when epoch_end - time based tokens: stop when
return
True/False error text OTP dictionary
Returns:list of related challenges
get_token_realm_user()[source]
get_validity_period_end()[source]

returns the end of validity period (if set)

get_validity_period_start()[source]

returns the start of validity period (if set)

get_vars(save=False)[source]

return the token state as dicts :return: token as dict

get_verification_result()[source]

return the internal result representation of the token verification which are a set of list, which stand for the challenge, pinMatching or invalid or valid token list

  • the lists are returned as they easily could be joined into the final token list, independent of they are empty or contain a token obj
Returns:tuple of token lists
incOtpCounter(counter=None, reset=True)[source]
method
incOtpCounter(aToken, counter)
parameters:
token - a token object counter - the new counter reset - optional -
exception:
in case of an transaction fail an exception is thrown
side effects:
default of reset will reset the failCounter
incOtpFailCounter()[source]
inc_count_auth()[source]
inc_count_auth_success()[source]
initChallenge(transactionid, challenges=None, options=None)[source]

This method initializes the challenge.

This is a hook that is called before the method createChallenge(), which will only be called if this method returns success==true.

Thus this method can be used, to verify if there is an outstanding challenge or if a new challenge needs to be created. E.g. this hook can be used, to implement a blocking mechanism to allow the creation of a new challenge only after a certain timeout. If there is an already outstanding challenge the return value can refer to this. (s. ticket #2986)

Parameters:
  • transactionid (string) – the id of the new challenge
  • options (dict) – the request parameters
  • challenges (list) – a list of all valid challenges for this token.
Returns:

tuple of ( success, transid, message, additional attributes )

The transid (the best transaction id for this request context), message, and additional attributes (dictionar) are displayed as results in the JSON response of the /validate/check request.

Only in case of success == true the next method createChallenge will be called.

isActive()[source]
is_auth_only_token(user)[source]

check if token is in the authenticate only mode this is required to optimize the number of requests

Parameters:user – the user / realm where the token policy is applied
Returns:boolean
is_challenge_and_auth_token(user)[source]
check if token supports both authentication methods:
authenticate an challenge responser
Parameters:user – the user / realm where the token policy is applied
Returns:boolean
is_challenge_request(passw, user, options=None)[source]

This method checks, if this is a request, that triggers a challenge.

The default behaviour to trigger a challenge is, if the passw parameter only contains the correct token pin and the request contains a data or a challenge key i.e. if the options parameter contains a key data or challenge.

Each token type can decide on its own under which condition a challenge is triggered by overwriting this method.

please note: in case of pin policy == 2 (no pin is required) the check_pin would always return true! Thus each request containing a data or challenge would trigger a challenge!

Parameters:
  • passw (string) – password, which might be pin or pin+otp
  • user (User object) – The user from the authentication request
  • options (dict) – dictionary of additional request parameters
Returns:

true or false

is_challenge_response(passw, user, options=None, challenges=None)[source]

This method checks, if this is a request, that is the response to a previously sent challenge.

The default behaviour to check if this is the response to a previous challenge is simply by checking if the request contains a parameter state or transactionid i.e. checking if the options parameter contains a key state or transactionid.

This method does not try to verify the response itself! It only determines, if this is a response for a challenge or not.

Parameters:
  • passw (string) – password, which might be pin or pin+otp
  • user (User object) – the requesting user
  • options ((dict)) – dictionary of additional request parameters
  • challenges – A list of challenges for this token. These challenges may be used, to identify if this request is a response for a challenge.
Returns:

true or false

is_challenge_valid(challenge=None)[source]

This method verifies if the given challenge is still valid.

The default implementation checks, if the challenge start is in the default validity time window.

Please note: This method does not check the response for the challenge itself. This is done by the method checkResponse4Challenge(). E.g. this very method is_challenge_valid is used by the method challenge_janitor() to clean up old challenges.

Parameters:challenge (challenge object) – The challenge to be checked
Returns:true or false
removeFromTokenInfo(key)[source]
reset()[source]

reset the token failcount value

resetTokenInfo()[source]

TODO: to be implemented or to be removed!

resync(otp1, otp2, options=None)[source]
setCounterWindow(countWindow)[source]
setDefaults()[source]
setDescription(description)[source]

set the token description :param description: set the token description

setFailCount(failCount)[source]
setHashLib(hashlib)[source]
setInfo(info)[source]
setMaxFail(maxFail)[source]
setOtpCount(otpCount)[source]
setOtpKey(otpKey, reset_failcount=True)[source]
set the token seed / secret
the seed / secret is encrypted and the encrypte value is stored in the Token model
Parameters:
  • otpKey – the token seed / secret
  • reset_failcount – boolean, if the failcounter should be reseted
setOtpLen(otplen)[source]
setPin(pin, param=None)[source]

set the PIN. The optional parameter “param” can hold the information, if the PIN is encrypted or hashed.

Parameters:
  • pin – the pin value
  • param – the additional request parameters, which could contain the ‘encryptpin’ value, that triggers, that the token secret are stored in an encrypted form
Returns:

  • nothing -

setRealms(realms)[source]
setSoPin(soPin)[source]
set the soPin of the token
the soPin is encrypted and the encrypte value is stored in the Token model
Parameters:soPin – the special so pin
setSyncWindow(syncWindow)[source]
setTokenInfo(info)[source]
setType(typ)[source]
setUid(uid, uidResolver, uidResClass)[source]

sets the UID values in the database

setUser(user, report)[source]
Parameters:
  • user – a User() object, consisting of loginname and realm
  • report – tbdf.
setUserPin(userPin)[source]
set the userPin of the token
the userPin is encrypted and the encrypte value is stored in the Token model
Parameters:userPin – the user pin
set_count_auth(count)[source]

Sets the counter for the occurred login attepms

set_count_auth_max(count)[source]

Sets the counter for the maximum allowed login attemps

set_count_auth_success(count)[source]

Sets the counter for the occurred successful logins

set_count_auth_success_max(count)[source]

Sets the counter for the maximum allowed successful logins

set_validity_period_end(end_date)[source]

sets the end date of the validity period for a token

set_validity_period_start(start_date)[source]

sets the start date of the validity period for a token

splitPinPass(passw)[source]
statusValidationFail()[source]

callback to enable a status change, when authentication failed

statusValidationSuccess()[source]

callback to enable a status change, on authentication success

storeToken()[source]
update(param, reset_failcount=True)[source]
linotp.lib.tokenclass.asc(column)

Produce an ascending ORDER BY clause element.

e.g.:

from sqlalchemy import asc
stmt = select([users_table]).order_by(asc(users_table.c.name))

will produce SQL as:

SELECT id, name FROM user ORDER BY name ASC

The asc() function is a standalone version of the ColumnElement.asc() method available on all SQL expressions, e.g.:

stmt = select([users_table]).order_by(users_table.c.name.asc())
Parameters:column – A ColumnElement (e.g. scalar SQL expression) with which to apply the asc() operation.

See also

desc()

nullsfirst()

nullslast()

Select.order_by()

linotp.lib.tokenclass.desc(column)

Produce a descending ORDER BY clause element.

e.g.:

from sqlalchemy import desc

stmt = select([users_table]).order_by(desc(users_table.c.name))

will produce SQL as:

SELECT id, name FROM user ORDER BY name DESC

The desc() function is a standalone version of the ColumnElement.desc() method available on all SQL expressions, e.g.:

stmt = select([users_table]).order_by(users_table.c.name.desc())
Parameters:column – A ColumnElement (e.g. scalar SQL expression) with which to apply the desc() operation.

See also

asc()

nullsfirst()

nullslast()

Select.order_by()