Exemple #1
0
    def update(self, param, reset_failcount=True):
        '''
        update - process the initialization parameters

        :param param: dict of initialization parameters
        :type param: dict

        :return: nothing
        '''
        ## Remark: the otpKey is handled in the parent class

        val = getParam(param, "hashlib", optional)
        if val is not None:
            self.hashlibStr = val
        else:
            self.hashlibStr = 'sha1'

        ## check if the key_size id provided
        ## if not, we could derive it from the hashlib
        key_size = getParam(param, 'key_size', optional)
        if key_size == None:
            param['key_size'] = keylen.get(self.hashlibStr)

        param['hashlib'] = self.hashlibStr
        self.addToTokenInfo("hashlib", self.hashlibStr)

        TokenClass.update(self, param, reset_failcount)
        return
    def update(self, param):
        # check for the required parameters
        '''
        The key holds the public ssh key and this is required
        
        The key probably is of the form "ssh-rsa BASE64 comment"
        '''
        if (self.hKeyRequired is True):
            getParam(param, "otpkey", required)
            
        key_elem = param.get("otpkey").split()
        if len(key_elem) != 3:
            raise Exception("The key must consist of 'ssh-rsa BASE64 comment'")
        
        key_type = key_elem[0]
        key = key_elem[1]
        key_comment = key_elem[2]
        
        # convert key to hex
        param["otpkey"] = binascii.hexlify(base64.b64decode(key))
        self.addToTokenInfo("ssh_type", key_type)
        self.addToTokenInfo("ssh_comment", key_comment)

        # call the parents function
        TokenClass.update(self, param)
 def test_99_delete_token(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.delete_token()
     
     db_token = Token.query.filter_by(serial=self.serial1).first()
     self.assertTrue(db_token is None, db_token)
    def update(self, param):

        TokenClass.update(self, param)
        # The otplen is determined by the otpkey. So we
        # call the setOtpLen after the parents update, to overwrite
        # specified OTP lengths with the length of the password
        self.setOtpLen(0)
    def revoke(self):
        """
        This revokes the token. We need to determine the CA, which issues the
        certificate, contact the connector and revoke the certificate

        Some token types may revoke a token without locking it.
        """
        TokenClass.revoke(self)

        # determine the CA and its connector.
        ti = self.get_tokeninfo()
        ca_specifier = ti.get("CA")
        log.debug("Revoking certificate {0!s} on CA {1!s}.".format(
            self.token.serial, ca_specifier))
        certificate_pem = ti.get("certificate")

        # call CAConnector.revoke_cert()
        ca_obj = get_caconnector_object(ca_specifier)
        revoked = ca_obj.revoke_cert(certificate_pem)
        log.info("Certificate {0!s} revoked on CA {1!s}.".format(revoked,
                                                                 ca_specifier))

        # call CAConnector.create_crl()
        crl = ca_obj.create_crl()
        log.info("CRL {0!s} created.".format(crl))

        return revoked
Exemple #6
0
    def update(self, param, reset_failcount=True):
        """
        This method is called during the initialization process.

        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        TokenClass.update(self, param)
        description = "U2F initialization"
        reg_data = getParam(param, "regdata")
        if reg_data:
            self.init_step = 2
            attestation_cert, user_pub_key, key_handle, \
                signature, description = parse_registration_data(reg_data)
            client_data = getParam(param, "clientdata", required)
            client_data_str = url_decode(client_data)
            app_id = self.get_tokeninfo("appId", "")
            # Verify the registration data
            # In case of any crypto error, check_data raises an exception
            check_registration_data(attestation_cert, app_id, client_data_str,
                                    user_pub_key, key_handle, signature)
            self.set_otpkey(key_handle)
            self.add_tokeninfo("pubKey", user_pub_key)

        self.set_description(description)
    def update(self, param):
        # New value
        radius_identifier = getParam(param, "radius.identifier")
        self.add_tokeninfo("radius.identifier", radius_identifier)

        # old values
        if not radius_identifier:
            radiusServer = getParam(param, "radius.server", optional=required)
            self.add_tokeninfo("radius.server", radiusServer)
            radius_secret = getParam(param, "radius.secret", optional=required)
            self.token.set_otpkey(binascii.hexlify(radius_secret))
            system_settings = getParam(param, "radius.system_settings",
                                       default=False)
            self.add_tokeninfo("radius.system_settings", system_settings)

        if not radius_identifier and not (radiusServer or radius_secret) and \
                not system_settings:
            raise ParameterError("Missing parameter: radius.identifier", id=905)

        # if another OTP length would be specified in /admin/init this would
        # be overwritten by the parent class, which is ok.
        self.set_otplen(6)
        TokenClass.update(self, param)
        val = getParam(param, "radius.local_checkpin", optional) or 0
        self.add_tokeninfo("radius.local_checkpin", val)

        val = getParam(param, "radius.user", required)
        self.add_tokeninfo("radius.user", val)
    def update(self, param, reset_failcount=True):
        """
        update - process initialization parameters

        :param param: dict of initialization parameters
        :type param: dict

        :return: nothing
        """
        if is_true(getParam(param, 'genkey', optional)):
            raise ParameterError("Generating OTP keys is not supported")

        upd_param = param.copy()

        # If the OTP key is given, it is given as a 496-character hex string which
        # encodes a 248-byte blob. As we want to set a 248-byte OTPKey (= Blob),
        # we unhexlify the OTP key
        if 'otpkey' in param:
            if len(param['otpkey']) != 496:
                raise ParameterError('Expected OTP key as 496-character hex string, but length is {!s}'.format(
                    len(param['otpkey'])
                ))
            try:
                upd_param['otpkey'] = binascii.unhexlify(upd_param['otpkey'])
            except (binascii.Error, TypeError):
                raise ParameterError('Expected OTP key as 496-character hex string, but it is malformed')

        TokenClass.update(self, upd_param, reset_failcount)
    def update(self, param):

        # check for the required parameters
        if (self.hKeyRequired is True):
            getParam(param, "otpkey", required)

        TokenClass.update(self, param)
Exemple #10
0
    def update(self, param):
        # cko: changed for backward compat
        getParam(param, "pin", optional)
        if not param.has_key('otpkey'):
            param['genkey'] = 1

        TokenClass.update(self, param)
    def update(self, param):
        """
        This method is called during the initialization process.
        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        TokenClass.update(self, param)

        request = getParam(param, "request", optional)
        spkac = getParam(param, "spkac", optional)
        certificate = None
        if request:
            ca = getParam(param, "ca", required)
            self.add_tokeninfo("CA", ca)
            # During the initialization process, we need to create the
            # certificate
            cacon = get_caconnector_object(ca)
            x509object = cacon.sign_request(request,
                                            options={"spkac": spkac})
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
        else:
            certificate = getParam(param, "certificate", optional)

        if certificate:
            self.add_tokeninfo("certificate", certificate)
 def test_10_get_hashlib(self):
     # check if functions are returned
     for hl in ["sha1", "md5", "sha256", "sha512",
                "sha224", "sha384", "", None]:
         self.assertTrue(hasattr(TokenClass.get_hashlib(hl),
                                 '__call__'),
                         TokenClass.get_hashlib(hl))
    def update(self, param):
        """
        second phase of the init process - updates parameters

        :param param: the request parameters
        :return: - nothing -
        """
        # if another OTP length would be specified in /admin/init this would
        # be overwritten by the parent class, which is ok.
        self.set_otplen(6)
        TokenClass.update(self, param)

        remoteServer = getParam(param, "remote.server", required)
        self.add_tokeninfo("remote.server", remoteServer)

        val = getParam(param, "remote.local_checkpin", optional) or 0
        self.add_tokeninfo("remote.local_checkpin", val)

        for key in ["remote.serial", "remote.user", "remote.path",
                    "remote.realm", "remote.resolver"]:
            val = getParam(param, key, optional)
            if val is not None:
                self.add_tokeninfo(key, val)

        self.add_tokeninfo("tokenkind", TOKENKIND.VIRTUAL)
    def update(self, param):
        """
        This method is called during the initialization process.
        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        TokenClass.update(self, param)

        request = getParam(param, "request", optional)
        spkac = getParam(param, "spkac", optional)
        certificate = getParam(param, "certificate", optional)
        generate = getParam(param, "genkey", optional)
        if request or generate:
            # If we do not upload a user certificate, then we need a CA do
            # sign the uploaded request or generated certificate.
            ca = getParam(param, "ca", required)
            self.add_tokeninfo("CA", ca)
            cacon = get_caconnector_object(ca)
        if request:
            # During the initialization process, we need to create the
            # certificate
            x509object = cacon.sign_request(request,
                                            options={"spkac": spkac})
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
        elif generate:
            # Create the certificate on behalf of another user.
            # Now we need to create the key pair,
            # the request
            # and the certificate
            # We need the user for whom the certificate should be created
            user = get_user_from_param(param, optionalOrRequired=required)

            keysize = getParam(param, "keysize", optional, 2048)
            key = crypto.PKey()
            key.generate_key(crypto.TYPE_RSA, keysize)
            req = crypto.X509Req()
            req.get_subject().CN = user.login
            # Add email to subject
            if user.info.get("email"):
                req.get_subject().emailAddress = user.info.get("email")
            req.get_subject().organizationalUnitName = user.realm
            # TODO: Add Country, Organization, Email
            # req.get_subject().countryName = 'xxx'
            # req.get_subject().stateOrProvinceName = 'xxx'
            # req.get_subject().localityName = 'xxx'
            # req.get_subject().organizationName = 'xxx'
            req.set_pubkey(key)
            req.sign(key, "sha256")
            x509object = cacon.sign_request(crypto.dump_certificate_request(
                crypto.FILETYPE_PEM, req))
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
            # Save the private key to the encrypted key field of the token
            s = crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
            self.add_tokeninfo("privatekey", s, value_type="password")

        if certificate:
            self.add_tokeninfo("certificate", certificate)
Exemple #15
0
    def update(self, param):

        self.radiusServer = getParam(param, "radius.server", required)
        # if another OTP length would be specified in /admin/init this would
        # be overwritten by the parent class, which is ok.
        self.setOtpLen(6)

        val = getParam(param, "radius.local_checkpin", optional)
        if val is not None:
            self.radiusLocal_checkpin = val

        val = getParam(param, "radius.user", required)
        if val is not None:
            self.radiusUser = val

        val = getParam(param, "radius.secret", required)
        if val is not None:
            self.radiusSecret = val

        if self.radiusSecret == VOID_RADIUS_SECRET:
            log.warning("Usage of default radius secret is not recomended!!")


        TokenClass.update(self, param)
        # We need to write the secret!
        self.token.setHKey(binascii.hexlify(self.radiusSecret))

        self.addToTokenInfo("radius.server", self.radiusServer)
        self.addToTokenInfo("radius.local_checkpin", self.radiusLocal_checkpin)
        self.addToTokenInfo("radius.user", self.radiusUser)
Exemple #16
0
    def __init__(self, aToken):
        '''
        constructor - create a token object

        :param aToken: instance of the orm db object
        :type aToken:  orm object

        '''
        TokenClass.__init__(self, aToken)
        self.setType(u"TOTP")
        self.hKeyRequired = True

        ''' timeStep defines the granularity: '''
        self.timeStep = getFromConfig("totp.timeStep", 30)

        ''' window size in seconds:
            30 seconds with as step width of 30 seconds results
            in a window of 1  which is one attempt
        '''
        self.timeWindow = getFromConfig("totp.timeWindow", 180)

        '''the time shift is specified in seconds  - and could be
        positive and negative
        '''
        self.timeShift = getFromConfig("totp.timeShift", 0)

        '''we support various hashlib methods, but only on create
        which is effectively set in the update
        '''
        self.hashlibStr = getFromConfig("totp.hashlib", u'sha1')
        return
    def test_18_challenges(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = TokenClass(db_token)

        db_token.set_pin("test")
        # No challenge request
        req = token.is_challenge_request("test", User(login="******",
                                                      realm=self.realm1))
        self.assertFalse(req, req)
        # A challenge request
        req = token.is_challenge_request("test",
                                         User(login="******",
                                              realm=self.realm1),
                                         {"data": "a challenge"})
        self.assertTrue(req, req)

        resp = token.is_challenge_response(User(login="******",
                                                realm=self.realm1),
                                            "test123456")
        self.assertFalse(resp, resp)
        resp = token.is_challenge_response(User(login="******",
                                                realm=self.realm1),
                                            "test123456",
                                            options={"transaction_id": "123456789"})
        self.assertTrue(resp, resp)

        # test if challenge is valid
        C = Challenge("S123455", transaction_id="tid", challenge="Who are you?")
        C.save()
        self.assertTrue(C.is_valid())
    def test_20_check_challenge_response(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        db_token.set_pin("test")
        token = TokenClass(db_token)
        r = token.check_challenge_response(user=None,
                                           passw="123454")
        # check that challenge does not match
        self.assertTrue(r == -1)

        # create a challenge and match the transaction_id
        c = Challenge(self.serial1, transaction_id="mytransaction",
                      challenge="Blah, what now?")
        # save challenge to the database
        c.save()
        r = token.check_challenge_response(user=None,
                                           passw="123454",
                                           options={"state": "mytransaction"})
        # The challenge matches, but the OTP does not match!
        self.assertTrue(r == -1, r)
        
        # test the challenge janitor
        c1 = Challenge(self.serial1, transaction_id="t1", validitytime=0)
        c1.save()
        c2 = Challenge(self.serial1, transaction_id="t2", validitytime=0)
        c2.save()
        c3 = Challenge(self.serial1, transaction_id="t3", validitytime=0)
        c3.save()
        c4 = Challenge(self.serial1, transaction_id="t4", validitytime=0)
        c4.save()
        # Delete potentiall expired challenges
        token.challenge_janitor()
        # Check, that the challenge does not exist anymore
        r = Challenge.query.filter(Challenge.transaction_id == "t1").count()
        self.assertTrue(r == 0, r)
Exemple #19
0
    def update(self, param, reset_failcount=True):
        """
        update - process initialization parameters

        :param param: dict of initialization parameters
        :type param: dict

        :return: nothing
        """
        if self.hKeyRequired is True:
            genkey = is_true(getParam(param, "genkey", optional))
            if not param.get('keysize'):
                param['keysize'] = 16
            if genkey:
                otpKey = generate_otpkey(param['keysize'])
                del param['genkey']
            else:
                # genkey not set: check otpkey is given
                # this will raise an exception if otpkey is not present
                otpKey = getParam(param, "otpkey", required)

            param['otpkey'] = otpKey
            
        # motp token specific
        mOTPPin = getParam(param, "motppin", required)
        self.token.set_user_pin(mOTPPin)

        TokenClass.update(self, param, reset_failcount)

        return
 def test_07_enable(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.enable(False)
     self.assertTrue(token.token.active is False)
     token.enable()
     self.assertTrue(token.token.active)        
Exemple #21
0
    def update(self, param):
        """
        The key holds the public ssh key and this is required
        
        The key probably is of the form "ssh-rsa BASE64 comment"
        """
        # We need to save the token, so that we can later add the tokeninfo
        # Otherwise we might not have created the DB entry, yet and we would
        # be missing the token.id
        self.token.save()

        getParam(param, "sshkey", required)
            
        key_elem = param.get("sshkey").split(" ", 2)
        if len(key_elem) != 3 or key_elem[0] != "ssh-rsa":
            raise Exception("The key must consist of 'ssh-rsa BASE64 comment'")

        key_type = key_elem[0]
        key = key_elem[1]
        key_comment = key_elem[2]
        
        # convert key to hex
        self.add_tokeninfo("ssh_key", key, value_type="password")
        self.add_tokeninfo("ssh_type", key_type)
        self.add_tokeninfo("ssh_comment", key_comment)

        # call the parents function
        TokenClass.update(self, param)
 def test_03_reset_failcounter(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.token.failcount = 10
     token.reset()
     self.assertTrue(token.token.failcount == 0,
                     token.token.failcount)
 def test_36_change_pin(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     # Set a pin change date that is already over.
     token.set_next_pin_change("-1d")
     # check that the pin needs to be changed
     r = token.is_pin_change()
     self.assertEqual(r, True)
Exemple #24
0
 def __init__(self, db_token):
     """
     :param db_token: the token
     :type db_token: database token object
     """
     TokenClass.__init__(self, db_token)
     self.set_type(u"4eyes")
     # We can not do challenge response
     self.mode = ['authenticate']
    def test_21_get_token_data_as_dict(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = TokenClass(db_token)
        token_data = token.get_as_dict()

        self.assertTrue("info" in token_data)
        self.assertTrue(token_data.get("user_id") == "1000")
        self.assertTrue(token_data.get("tokentype") == "newtype")
        self.assertTrue(token_data.get("count_window") == 52)
 def test_31_revoke(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.revoke()
     self.assertTrue(token.is_revoked())
     self.assertTrue(token.is_locked())
     self.assertTrue(token.token.active is False)
     # A revoked token can not be enabled anymore
     self.assertRaises(Exception, token.enable)
Exemple #27
0
    def update(self, param, reset_failcount=True):
        """
        process the initialization parameters

        Do we really always need an otpkey?
        the otpKey is handled in the parent class
        :param param: dict of initialization parameters
        :type param: dict

        :return: nothing
        """
        # In case am Immutable MultiDict:
        upd_param = {}
        for k, v in param.items():
            upd_param[k] = v

        # Special handling of 2-step enrollment
        if is_true(getParam(param, "2stepinit", optional)):
            # Use the 2step_serversize setting for the size of the server secret
            # (if it is set)
            if "2step_serversize" in upd_param:
                upd_param["keysize"] = int(getParam(upd_param, "2step_serversize", required))
            # Add twostep settings to the tokeninfo
            for key, default in [
                ("2step_difficulty", TWOSTEP_DEFAULT_DIFFICULTY),
                ("2step_clientsize", TWOSTEP_DEFAULT_CLIENTSIZE)]:
                self.add_tokeninfo(key, getParam(param, key, optional, default))

        val = getParam(upd_param, "hashlib", optional)
        if val is not None:
            hashlibStr = val
        else:
            hashlibStr = self.hashlib

        # check if the key_size is provided
        # if not, we could derive it from the hashlib
        key_size = getParam(upd_param, 'key_size', optional) \
                   or getParam(upd_param, 'keysize', optional)
        if key_size is None:
            upd_param['keysize'] = keylen.get(hashlibStr)

        otpKey = getParam(upd_param, "otpkey", optional)
        genkey = is_true(getParam(upd_param, "genkey", optional))
        if genkey and otpKey:
            # The Base TokenClass does not allow otpkey and genkey at the
            # same time
            del upd_param['otpkey']
        upd_param['hashlib'] = hashlibStr
        # We first need to call the parent class. Since exceptions would be
        # raised here.
        TokenClass.update(self, upd_param, reset_failcount)

        self.add_tokeninfo("hashlib", hashlibStr)

        # check the tokenkind
        if self.token.serial.startswith("UB"):
            self.add_tokeninfo("tokenkind", TOKENKIND.HARDWARE)
Exemple #28
0
    def __init__(self, db_token):
        """
        constructor - create a token class object with its db token binding

        :param aToken: the db bound token
        """
        TokenClass.__init__(self, db_token)
        self.set_type(u"vasco")
        self.hKeyRequired = True
Exemple #29
0
    def __init__(self, db_token):
        """
        constructor - create a token class object with it's db token binding

        :param aToken: the db bound token
        """
        TokenClass.__init__(self, db_token)
        self.set_type(u"remote")
        self.mode = ['authenticate', 'challenge']
 def test_05_get_set_realms(self):
     set_realm(self.realm2)
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     realms = token.get_realms()
     self.assertTrue(len(realms) == 1, realms)
     token.set_realms([self.realm1, self.realm2])
     realms = token.get_realms()
     self.assertTrue(len(realms) == 2, realms)
Exemple #31
0
    def test_37_is_orphaned(self):
        resolver = "orphreso"
        realm = "orphrealm"
        rid = save_resolver({
            "resolver": resolver,
            "type": "passwdresolver",
            "fileName": PWFILE
        })
        self.assertTrue(rid > 0, rid)
        (added, failed) = set_realm(realm, [resolver])
        self.assertTrue(len(failed) == 0)
        self.assertTrue(len(added) == 1)

        # Assign token to user "cornelius" "realm1", "resolver1" "uid=1000
        db_token = Token("orphaned",
                         tokentype="spass",
                         userid=1000,
                         resolver=resolver,
                         realm=realm)
        db_token.save()
        token_obj = TokenClass(db_token)
        orph = token_obj.is_orphaned()
        self.assertFalse(orph)
        # clean up token
        token_obj.delete_token()

        # Assign a token to a user in a resolver. user_id does not exist
        db_token = Token("orphaned",
                         tokentype="spass",
                         userid=872812,
                         resolver=resolver,
                         realm=realm)
        db_token.save()
        token_obj = TokenClass(db_token)
        orph = token_obj.is_orphaned()
        self.assertTrue(orph)
        # clean up token
        token_obj.delete_token()

        # A token, which a resolver name, that does not exist anymore
        db_token = Token("orphaned",
                         tokentype="spass",
                         userid=1000,
                         resolver=resolver,
                         realm=realm)
        db_token.save()

        # delete the realm
        delete_realm(realm)
        # token is orphaned
        token_obj = TokenClass(db_token)
        orph = token_obj.is_orphaned()
        self.assertTrue(orph)

        # delete the resolver
        delete_resolver(resolver)
        # token is orphaned
        token_obj = TokenClass(db_token)
        orph = token_obj.is_orphaned()
        self.assertTrue(orph)
        # clean up token
        token_obj.delete_token()
Exemple #32
0
 def __init__(self, aToken):
     TokenClass.__init__(self, aToken)
     self.set_type(u"certificate")
     self.otp_len = 0
Exemple #33
0
    def get_init_detail(self, params=None, user=None):
        """
        to complete the token initialization some additional details
        should be returned, which are displayed at the end of
        the token initialization.
        This is the e.g. the enrollment URL for a Google Authenticator.
        """
        response_detail = TokenClass.get_init_detail(self, params, user)
        params = params or {}
        tokenlabel = params.get("tokenlabel", "<s>")
        tokenissuer = params.get("tokenissuer", "privacyIDEA")
        # If the init_details contain an OTP key the OTP key
        # should be displayed as an enrollment URL
        otpkey = self.init_details.get('otpkey')
        # Add rollout state the response
        response_detail['rollout_state'] = self.token.rollout_state
        # Add two-step initialization parameters to response and QR code
        extra_data = {}
        if is_true(params.get("2stepinit")):
            twostep_parameters = self._get_twostep_parameters()
            extra_data.update(twostep_parameters)
            response_detail.update(twostep_parameters)
        imageurl = params.get("appimageurl")
        if imageurl:
            extra_data.update({"image": imageurl})
        force_app_pin = params.get('force_app_pin')
        if force_app_pin:
            extra_data.update({'pin': True})
        if otpkey:
            tok_type = self.type.lower()
            if user is not None:
                try:
                    key_bin = binascii.unhexlify(otpkey)
                    # also strip the padding =, as it will get problems with the google app.
                    value_b32_str = b32encode_and_unicode(key_bin).strip('=')
                    response_detail["otpkey"]["value_b32"] = value_b32_str
                    goo_url = cr_google(key=otpkey,
                                        user=user.login,
                                        realm=user.realm,
                                        tokentype=tok_type.lower(),
                                        serial=self.get_serial(),
                                        tokenlabel=tokenlabel,
                                        hash_algo=params.get(
                                            "hashlib", "sha1"),
                                        digits=params.get("otplen", 6),
                                        period=params.get("timeStep", 30),
                                        issuer=tokenissuer,
                                        user_obj=user,
                                        extra_data=extra_data)
                    response_detail["googleurl"] = {
                        "description": _("URL for google "
                                         "Authenticator"),
                        "value": goo_url,
                        "img": create_img(goo_url, width=250)
                    }

                    oath_url = cr_oath(otpkey=otpkey,
                                       user=user.login,
                                       realm=user.realm,
                                       type=tok_type,
                                       serial=self.get_serial(),
                                       tokenlabel=tokenlabel,
                                       extra_data=extra_data)
                    response_detail["oathurl"] = {
                        "description": _("URL for"
                                         " OATH "
                                         "token"),
                        "value": oath_url,
                        "img": create_img(oath_url, width=250)
                    }
                except Exception as ex:  # pragma: no cover
                    log.error("{0!s}".format((traceback.format_exc())))
                    log.error(
                        'failed to set oath or google url: {0!r}'.format(ex))

        return response_detail
Exemple #34
0
    def update(self, param, reset_failcount=True):
        """
        process the initialization parameters

        We need to distinguish the first authentication step
        and the second authentication step.

        1. step:
            ``param`` contains:

            - ``type``
            - ``genkey``

        2. step:
            ``param`` contains:

            - ``serial``
            - ``fbtoken``
            - ``pubkey``

        :param param: dict of initialization parameters
        :type param: dict

        :return: nothing
        """
        upd_param = {}
        for k, v in param.items():
            upd_param[k] = v

        if "serial" in upd_param and "fbtoken" in upd_param and "pubkey" in upd_param:
            # We are in step 2:
            if self.token.rollout_state != "clientwait":
                raise ParameterError(
                    "Invalid state! The token you want to enroll is not in the state 'clientwait'."
                )
            enrollment_credential = getParam(upd_param,
                                             "enrollment_credential",
                                             optional=False)
            if enrollment_credential != self.get_tokeninfo(
                    "enrollment_credential"):
                raise ParameterError(
                    "Invalid enrollment credential. You are not authorized to finalize this token."
                )
            self.del_tokeninfo("enrollment_credential")
            self.token.rollout_state = "enrolled"
            self.token.active = True
            self.add_tokeninfo(PUBLIC_KEY_SMARTPHONE, upd_param.get("pubkey"))
            self.add_tokeninfo("firebase_token", upd_param.get("fbtoken"))
            # create a keypair for the server side.
            pub_key, priv_key = generate_keypair(4096)
            self.add_tokeninfo(PUBLIC_KEY_SERVER, pub_key)
            self.add_tokeninfo(PRIVATE_KEY_SERVER, priv_key, "password")

        elif "genkey" in upd_param:
            # We are in step 1:
            upd_param["2stepinit"] = 1
            self.add_tokeninfo("enrollment_credential", geturandom(20,
                                                                   hex=True))
            # We also store the firebase config, that was used during the enrollment.
            self.add_tokeninfo(PUSH_ACTION.FIREBASE_CONFIG,
                               param.get(PUSH_ACTION.FIREBASE_CONFIG))
        else:
            raise ParameterError(
                "Invalid Parameters. Either provide (genkey) or (serial, fbtoken, pubkey)."
            )

        TokenClass.update(self, upd_param, reset_failcount)
Exemple #35
0
    def get_init_detail(self, params=None, user=None):
        """
        This returns the init details during enrollment.

        In the 1st step the QR Code is returned.
        """
        response_detail = TokenClass.get_init_detail(self, params, user)
        if "otpkey" in response_detail:
            del response_detail["otpkey"]
        params = params or {}
        user = user or User()
        tokenlabel = params.get("tokenlabel", "<s>")
        tokenissuer = params.get("tokenissuer", "privacyIDEA")
        sslverify = getParam(params,
                             PUSH_ACTION.SSL_VERIFY,
                             allowed_values=["0", "1"],
                             default="1")
        # Add rollout state the response
        response_detail['rollout_state'] = self.token.rollout_state

        extra_data = {
            "enrollment_credential":
            self.get_tokeninfo("enrollment_credential")
        }
        imageurl = params.get("appimageurl")
        if imageurl:
            extra_data.update({"image": imageurl})
        if self.token.rollout_state == "clientwait":
            # Get the values from the configured PUSH config
            fb_identifier = params.get(PUSH_ACTION.FIREBASE_CONFIG)
            firebase_configs = get_smsgateway(identifier=fb_identifier,
                                              gwtype=GWTYPE)
            if len(firebase_configs) != 1:
                raise ParameterError("Unknown Firebase configuration!")
            fb_options = firebase_configs[0].option_dict
            for k in [
                    FIREBASE_CONFIG.PROJECT_NUMBER, FIREBASE_CONFIG.PROJECT_ID,
                    FIREBASE_CONFIG.APP_ID, FIREBASE_CONFIG.API_KEY,
                    FIREBASE_CONFIG.APP_ID_IOS, FIREBASE_CONFIG.API_KEY_IOS
            ]:
                extra_data[k] = fb_options.get(k)
            # this allows to upgrade our crypto
            extra_data["v"] = 1
            extra_data["serial"] = self.get_serial()
            extra_data["sslverify"] = sslverify
            # We display this during the first enrollment step!
            qr_url = create_push_token_url(
                url=fb_options.get(FIREBASE_CONFIG.REGISTRATION_URL),
                user=user.login,
                realm=user.realm,
                serial=self.get_serial(),
                tokenlabel=tokenlabel,
                issuer=tokenissuer,
                user_obj=user,
                extra_data=extra_data,
                ttl=fb_options.get(FIREBASE_CONFIG.TTL))
            response_detail["pushurl"] = {
                "description": _("URL for privacyIDEA Push Token"),
                "value": qr_url,
                "img": create_img(qr_url, width=250)
            }
            self.add_tokeninfo(FIREBASE_CONFIG.PROJECT_ID,
                               fb_options.get(FIREBASE_CONFIG.PROJECT_ID))

            response_detail["enrollment_credential"] = self.get_tokeninfo(
                "enrollment_credential")

        elif self.token.rollout_state == "enrolled":
            # in the second enrollment step we return the public key of the server to the smartphone.
            pubkey = strip_key(self.get_tokeninfo(PUBLIC_KEY_SERVER))
            response_detail["public_key"] = pubkey

        return response_detail
Exemple #36
0
    def test_40_failcounter_exceeded(self):
        from privacyidea.lib.tokenclass import (FAILCOUNTER_EXCEEDED,
                                                FAILCOUNTER_CLEAR_TIMEOUT)
        db_token = Token("failcounter", tokentype="spass")
        db_token.save()
        token_obj = TokenClass(db_token)
        for i in range(0, 11):
            token_obj.inc_failcount()
        now = datetime.datetime.now(tzlocal()).strftime(DATE_FORMAT)
        # Now the FAILCOUNTER_EXCEEDED is set
        ti = token_obj.get_tokeninfo(FAILCOUNTER_EXCEEDED)
        # We only compare the date
        self.assertEqual(ti[:10], now[:10])
        # and the timezone
        self.assertEqual(ti[-5:], now[-5:])
        # reset the failcounter
        token_obj.reset()
        ti = token_obj.get_tokeninfo(FAILCOUNTER_EXCEEDED)
        self.assertEqual(ti, None)

        # Now check with failcounter clear, with timeout 5 minutes
        set_privacyidea_config(FAILCOUNTER_CLEAR_TIMEOUT, 5)
        token_obj.set_failcount(10)
        failed_recently = (datetime.datetime.now(tzlocal()) -
                           datetime.timedelta(minutes=3)).strftime(DATE_FORMAT)
        token_obj.add_tokeninfo(FAILCOUNTER_EXCEEDED, failed_recently)

        r = token_obj.check_reset_failcount()
        # the fail is only 3 minutes ago, so we will not reset and check will
        #  be false
        self.assertFalse(r)

        # Set the timeout to a shorter value
        set_privacyidea_config(FAILCOUNTER_CLEAR_TIMEOUT, 2)
        r = token_obj.check_reset_failcount()
        # The fail is longer ago.
        self.assertTrue(r)

        # The tokeninfo of this token is deleted and the failcounter is 0
        self.assertEqual(token_obj.get_tokeninfo(FAILCOUNTER_EXCEEDED), None)
        self.assertEqual(token_obj.get_failcount(), 0)

        token_obj.delete_token()
Exemple #37
0
 def test_15_check_pin(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.set_pin("test")
     self.assertTrue(token.check_pin("test"))
     self.assertFalse(token.check_pin("wrong pin"))
Exemple #38
0
 def __init__(self, db_token):
     TokenClass.__init__(self, db_token)
     self.set_type(u"sshkey")
Exemple #39
0
 def __init__(self, db_token):
     TokenClass.__init__(self, db_token)
     self.set_type(u"yubico")
     self.tokenid = ""
Exemple #40
0
 def update(self, param, reset_failcount=True):
     TokenClass.update(self, param, reset_failcount)
     self.add_tokeninfo("tokenkind", TOKENKIND.HARDWARE)
Exemple #41
0
 def __init__(self, db_token):
     TokenClass.__init__(self, db_token)
     self.set_type(u"yubikey")
     self.hKeyRequired = True
 def __init__(self, aToken):
     TokenClass.__init__(self, aToken)
     self.hKeyRequired = True
     self.set_type(u"pw")
Exemple #43
0
 def test_34_get_default_settings(self):
     r = TokenClass.get_default_settings(FakeFlaskG(), {})
     self.assertEqual(r, {})
Exemple #44
0
    def test_11_old_validity_time(self):
        old_time_1 = "11/04/17 22:00"  # April 4th
        old_time_2 = "24/04/17 22:00"  # April 24th
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = TokenClass(db_token)
        token.add_tokeninfo("validity_period_start", old_time_1)
        token.add_tokeninfo("validity_period_end", old_time_2)
        info = token.get_tokeninfo()
        self.assertEqual(info.get("validity_period_start"), old_time_1)
        self.assertEqual(info.get("validity_period_end"), old_time_2)
        e = token.get_validity_period_end()
        self.assertTrue(e.startswith("2017-04-24T22:00"), e)
        s = token.get_validity_period_start()
        self.assertTrue(s.startswith("2017-04-11T22:00"), s)

        # old date format has problems with check_validity_date
        start_date = datetime.datetime.now() - datetime.timedelta(days=15)
        end_date = datetime.datetime.now() + datetime.timedelta(days=15)
        start = start_date.strftime("%d/%m/%Y")
        end = end_date.strftime("%d/%m/%Y")
        # Need to write the old format to the database
        token.add_tokeninfo("validity_period_start", start)
        token.add_tokeninfo("validity_period_end", end)
        r = token.check_validity_period()
        self.assertTrue(r)
Exemple #45
0
 def __init__(self, aToken):
     TokenClass.__init__(self, aToken)
     self.set_type(u"indexedsecret")
     self.mode = ['challenge']
Exemple #46
0
    def test_38_last_auth(self):
        db_token = Token("lastauth001", tokentype="spass", userid=1000)
        db_token.save()
        token_obj = TokenClass(db_token)
        tdelta = datetime.timedelta(days=1)
        token_obj.add_tokeninfo(ACTION.LASTAUTH,
                                datetime.datetime.now(tzlocal()) - tdelta)
        r = token_obj.check_last_auth_newer("10h")
        self.assertFalse(r)
        r = token_obj.check_last_auth_newer("2d")
        self.assertTrue(r)

        # Old time format
        # lastauth_alt = datetime.datetime.utcnow().isoformat()
        token_obj.add_tokeninfo(ACTION.LASTAUTH,
                                datetime.datetime.utcnow() - tdelta)
        r = token_obj.check_last_auth_newer("10h")
        self.assertFalse(r)
        r = token_obj.check_last_auth_newer("2d")
        token_obj.delete_token()
Exemple #47
0
 def test_13_check_otp(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     self.assertTrue(token.check_otp_exist("123456") == -1)
Exemple #48
0
 def test_33_get_setting_type(self):
     r = TokenClass.get_setting_type("something")
     self.assertEqual(r, "")
Exemple #49
0
    def test_11_tokeninfo(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = TokenClass(db_token)
        token.add_tokeninfo("key1", "value2")
        info1 = token.get_tokeninfo()
        self.assertTrue("key1" in info1, info1)
        token.add_tokeninfo("key2", "value3")
        info2 = token.get_tokeninfo()
        self.assertTrue("key2" in info2, info2)
        token.set_tokeninfo(info1)
        info2 = token.get_tokeninfo()
        self.assertTrue("key2" not in info2, info2)
        self.assertTrue(token.get_tokeninfo("key1") == "value2", info2)

        # auth counter
        token.set_count_auth_success_max(200)
        token.set_count_auth_max(1000)
        token.set_count_auth_success(100)
        token.inc_count_auth_success()
        token.set_count_auth(200)
        token.inc_count_auth()
        self.assertTrue(token.get_count_auth_success_max() == 200)
        self.assertTrue(token.get_count_auth_success() == 101)
        self.assertTrue(token.get_count_auth_max() == 1000)
        self.assertTrue(token.get_count_auth() == 201)

        self.assertTrue(token.check_auth_counter())
        token.set_count_auth_max(10)
        self.assertFalse(token.check_auth_counter())
        token.set_count_auth_max(1000)
        token.set_count_auth_success_max(10)
        self.assertFalse(token.check_auth_counter())

        # set the max counter to 0 means authentication is allowed
        # 0 is unlimited
        token.set_count_auth_max(0)
        token.set_count_auth_success_max(0)
        self.assertEqual(token.check_auth_counter(), True)

        # handle validity end date
        token.set_validity_period_end("2014-12-30T16:00+0400")
        end = token.get_validity_period_end()
        self.assertTrue(end == "2014-12-30T16:00+0400", end)
        self.assertRaises(TokenAdminError, token.set_validity_period_end,
                          "wrong date")
        # handle validity start date
        token.set_validity_period_start("2014-12-30T16:00+0400")
        start = token.get_validity_period_start()
        self.assertTrue(start == "2014-12-30T16:00+0400", start)
        self.assertRaises(TokenAdminError, token.set_validity_period_start,
                          "wrong date")

        self.assertFalse(token.check_validity_period())
        # delete the validity period end by passing an empty string
        token.set_validity_period_end('')
        end = token.get_validity_period_end()
        self.assertTrue(end == "", end)
        self.assertTrue(token.check_validity_period())

        # try the same for the validity period start
        start_date_5d = datetime.datetime.now(
            tzlocal()) + datetime.timedelta(5)
        token.set_validity_period_start(start_date_5d.strftime(DATE_FORMAT))
        self.assertFalse(token.check_validity_period())
        token.set_validity_period_start('')
        self.assertTrue(token.check_validity_period())

        token.set_validity_period_end("2015-05-22T22:00:00.000Z")
        end = token.get_validity_period_end()
        self.assertEqual(end, "2015-05-22T22:00+0000")

        token.set_validity_period_start("2015-05-22T22:00:00.000Z")
        start = token.get_validity_period_start()
        self.assertEqual(start, "2015-05-22T22:00+0000")

        # check validity period
        # +5 days
        end_date = datetime.datetime.now(tzlocal()) + datetime.timedelta(5)
        end = end_date.strftime(DATE_FORMAT)
        token.set_validity_period_end(end)
        # - 5 days
        start_date = datetime.datetime.now(tzlocal()) - datetime.timedelta(5)
        start = start_date.strftime(DATE_FORMAT)
        token.set_validity_period_start(start)
        self.assertTrue(token.check_validity_period())

        # check before start date
        # +5 days
        end_date = datetime.datetime.now(tzlocal()) + datetime.timedelta(5)
        end = end_date.strftime(DATE_FORMAT)
        token.set_validity_period_end(end)
        # + 2 days
        start_date = datetime.datetime.now(tzlocal()) + datetime.timedelta(2)
        start = start_date.strftime(DATE_FORMAT)
        token.set_validity_period_start(start)
        self.assertFalse(token.check_validity_period())

        # check after enddate
        # -1 day
        end_date = datetime.datetime.now(tzlocal()) - datetime.timedelta(1)
        end = end_date.strftime(DATE_FORMAT)
        token.set_validity_period_end(end)
        # - 10 days
        start_date = datetime.datetime.now(tzlocal()) - datetime.timedelta(10)
        start = start_date.strftime(DATE_FORMAT)
        token.set_validity_period_start(start)
        self.assertFalse(token.check_validity_period())
Exemple #50
0
 def test_32_test_config(self):
     r = TokenClass.test_config()
     self.assertEqual(r[0], False)
     self.assertEqual(r[1], "Not implemented")
 def test_03_reset_failcounter(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.token.failcount = 10
     token.reset()
     self.assertTrue(token.token.failcount == 0, token.token.failcount)
    def test_04_base_methods(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = TokenClass(db_token)
        self.assertTrue(token.check_otp("123456", 1, 10) == -1)
        self.assertTrue(token.get_otp() == (-2, 0, 0, 0))
        res = token.get_multi_otp()
        self.assertTrue(res[0] is False, res)

        c = token.create_challenge("transactionid")
        self.assertTrue(c[0], c)
        self.assertTrue("transactionid" in c[2], c)

        c = token.create_challenge()
        self.assertTrue(c[0], c)

        transaction_id = c[2]
        self.assertEqual(len(transaction_id), 20)
        # Now we create the challenge with the same transaction_id for
        # another token.
        db_token2 = Token(self.serial2, tokentype="spass")
        db_token2.save()
        token2 = TokenClass(db_token)
        c = token2.create_challenge(transaction_id)
        self.assertEqual(c[2], transaction_id)

        # Now there should be two entries with the same transaction_id
        r = Challenge.query.filter(
            Challenge.transaction_id == transaction_id).all()
        self.assertEqual(len(r), 2)

        # set the description
        token.set_description("something new")
        self.assertTrue(token.token.description == "something new",
                        token.token)

        # set defaults
        token.set_defaults()
        self.assertTrue(token.token.otplen == 6)
        self.assertTrue(token.token.sync_window == 1000)

        token.resync("1234", "3456")

        token.token.count_window = 17
        self.assertTrue(token.get_otp_count_window() == 17)

        token.token.count = 18
        self.assertTrue(token.get_otp_count() == 18)

        token.token.active = False
        self.assertTrue(token.is_active() is False)

        token.token.failcount = 7
        self.assertTrue(token.get_failcount() == 7)
        token.set_failcount(8)
        self.assertTrue(token.token.failcount == 8)

        token.token.maxfail = 12
        self.assertTrue(token.get_max_failcount() == 12)

        self.assertTrue(token.get_user_id() == token.token.user_id)

        self.assertTrue(token.get_serial() == "SE123456", token.token.serial)
        self.assertTrue(token.get_tokentype() == "newtype",
                        token.token.tokentype)

        token.set_so_pin("sopin")
        token.set_user_pin("userpin")
        token.set_otpkey("123456")
        token.set_otplen(8)
        token.set_otp_count(1000)
        self.assertTrue(len(token.token.so_pin) == 32, token.token.so_pin)
        self.assertTrue(len(token.token.user_pin) == 32, token.token.user_pin)
        self.assertTrue(len(token.token.key_enc) == 32, token.token.key_enc)
        self.assertTrue(token.get_otplen() == 8)
        self.assertTrue(token.token.count == 1000, token.token.count)

        token.set_maxfail(1000)
        self.assertTrue(token.token.maxfail == 1000)

        token.set_count_window(52)
        self.assertTrue(token.get_count_window() == 52)

        token.set_sync_window(53)
        self.assertTrue(token.get_sync_window() == 53)
Exemple #53
0
 def __init__(self, db_token):
     TokenClass.__init__(self, db_token)
     self.set_type(u"push")
     self.mode = ['challenge', 'authenticate']
     self.hKeyRequired = False
 def test_08_info(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.set_hashlib("sha1")
     tinfo = token.token.get_info()
     self.assertTrue("hashlib" in tinfo, tinfo)
    def test_16_init_detail(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = TokenClass(db_token)
        token.add_init_details("otpkey", "secretkey")
        detail = token.get_init_detail()
        self.assertTrue("otpkey" in detail, detail)

        # but the otpkey must not be written to token.token.info (DB)
        self.assertTrue("otpkey" not in token.token.get_info(),
                        token.token.get_info())

        token.get_QRimage_data({"googleurl": "hotp://"})
        self.assertRaises(Exception, token.set_init_details, "unvalid value")
        token.set_init_details({"detail1": "value1"})
        self.assertTrue("detail1" in token.get_init_details(),
                        token.get_init_details())
 def test_09_failcount(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     start = token.token.failcount
     end = token.inc_failcount()
     self.assertTrue(end == start + 1, (end, start))
Exemple #57
0
    def update(self, param):
        """
        This method is called during the initialization process.
        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        TokenClass.update(self, param)

        request = getParam(param, "request", optional)
        spkac = getParam(param, "spkac", optional)
        certificate = getParam(param, "certificate", optional)
        generate = getParam(param, "genkey", optional)
        template_name = getParam(param, "template", optional)
        if request or generate:
            # If we do not upload a user certificate, then we need a CA do
            # sign the uploaded request or generated certificate.
            ca = getParam(param, "ca", required)
            self.add_tokeninfo("CA", ca)
            cacon = get_caconnector_object(ca)
        if request:
            # During the initialization process, we need to create the
            # certificate
            x509object = cacon.sign_request(request,
                                            options={
                                                "spkac": spkac,
                                                "template": template_name
                                            })
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
        elif generate:
            # Create the certificate on behalf of another user.
            # Now we need to create the key pair,
            # the request
            # and the certificate
            # We need the user for whom the certificate should be created
            user = get_user_from_param(param, optionalOrRequired=required)

            keysize = getParam(param, "keysize", optional, 2048)
            key = crypto.PKey()
            key.generate_key(crypto.TYPE_RSA, keysize)
            req = crypto.X509Req()
            req.get_subject().CN = user.login
            # Add email to subject
            if user.info.get("email"):
                req.get_subject().emailAddress = user.info.get("email")
            req.get_subject().organizationalUnitName = user.realm
            # TODO: Add Country, Organization, Email
            # req.get_subject().countryName = 'xxx'
            # req.get_subject().stateOrProvinceName = 'xxx'
            # req.get_subject().localityName = 'xxx'
            # req.get_subject().organizationName = 'xxx'
            req.set_pubkey(key)
            req.sign(key, "sha256")
            x509object = cacon.sign_request(
                crypto.dump_certificate_request(crypto.FILETYPE_PEM, req),
                options={"template": template_name})
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
            # Save the private key to the encrypted key field of the token
            s = crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
            self.add_tokeninfo("privatekey", s, value_type="password")

        if "pin" in param:
            self.set_pin(param.get("pin"), encrypt=True)

        if certificate:
            self.add_tokeninfo("certificate", certificate)
Exemple #58
0
 def test_15_status_validation(self):
     db_token = Token.query.filter_by(serial=self.serial1).first()
     token = TokenClass(db_token)
     token.status_validation_fail()
     token.status_validation_success()
Exemple #59
0
 def test_42_has_further_challenge(self):
     db_token = Token("furhterchallenge", tokentype="spass")
     db_token.save()
     token_obj = TokenClass(db_token)
     self.assertFalse(token_obj.has_further_challenge())
     token_obj.delete_token()
Exemple #60
0
    def test_35_next_pin_change(self):
        ndate = (datetime.datetime.now(tzlocal()) +
                 datetime.timedelta(12)).strftime(DATE_FORMAT)

        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = TokenClass(db_token)
        token.set_next_pin_change("12d")
        r = token.get_tokeninfo("next_pin_change")
        self.assertEqual(r, ndate)

        token.set_next_pin_change("12d", password=True)
        r = token.get_tokeninfo("next_password_change")
        self.assertEqual(r, ndate)
        # The password must not be changed
        r = token.is_pin_change(password=True)
        self.assertEqual(r, False)

        datestring = "03/04/01 10:00"
        token.add_tokeninfo("next_pin_change", datestring)
        r = token.is_pin_change()
        self.assertTrue(r)