示例#1
0
    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())
示例#2
0
    def create_challenge(self, transactionid=None, options=None):
        """
        This method creates a challenge, which is submitted to the user.
        The submitted challenge will be preserved in the challenge
        database.

        If no transaction id is given, the system will create a transaction
        id and return it, so that the response can refer to this transaction.

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :type options: dict
        :return: tuple of (bool, message, transactionid, attributes)
        :rtype: tuple

        The return tuple builds up like this:
        ``bool`` if submit was successful;
        ``message`` which is displayed in the JSON response;
        additional ``attributes``, which are displayed in the JSON response.
        """
        options = options or {}
        message = 'Please scan the QR Code'

        # Get ValidityTime=120s. Maybe there is a TIQRChallengeValidityTime...
        validity = int(get_from_config('DefaultChallengeValidityTime', 120))
        tokentype = self.get_tokentype().lower()
        lookup_for = tokentype.capitalize() + 'ChallengeValidityTime'
        validity = int(get_from_config(lookup_for, validity))

        # We need to set the user ID
        user_identifier, user_displayname = self.get_user_displayname()

        service_identifier = get_from_config("tiqr.serviceIdentifier") or \
                             "org.privacyidea"

        # Get the OCRASUITE from the token information
        ocrasuite = self.get_tokeninfo("ocrasuite") or OCRA_DEFAULT_SUITE
        # Depending on the OCRA-SUITE we create the challenge
        os = OCRASuite(ocrasuite)
        challenge = os.create_challenge()

        # Create the challenge in the database
        db_challenge = Challenge(self.token.serial,
                                 transaction_id=None,
                                 challenge=challenge,
                                 data=None,
                                 session=options.get("session"),
                                 validitytime=validity)
        db_challenge.save()

        authurl = "tiqrauth://%s@%s/%s/%s" % (user_identifier,
                                              service_identifier,
                                              db_challenge.transaction_id,
                                              challenge)
        attributes = {"img": create_img(authurl, width=250),
                      "value": authurl,
                      "poll": True,
                      "hideResponseInput": True}

        return True, message, db_challenge.transaction_id, attributes
def _create_challenge(token_obj, challenge_type, message, challenge_data=None):
    validity = int(get_from_config('DefaultChallengeValidityTime', 120))
    if challenge_type == CHALLENGE_TYPE.PIN_RESET:
        validity = int(
            get_from_config('PinResetChallengeValidityTime', validity))
    db_challenge = Challenge(token_obj.token.serial,
                             challenge=challenge_type,
                             data=challenge_data,
                             validitytime=validity)
    db_challenge.save()
    token_obj.challenge_janitor()
    reply_dict = {}
    reply_dict["multi_challenge"] = [{
        "transaction_id": db_challenge.transaction_id,
        "message": message,
        "attributes": None,
        "serial": token_obj.token.serial,
        "type": token_obj.token.tokentype
    }]
    reply_dict["message"] = message
    reply_dict["messages"] = [message]
    reply_dict["transaction_id"] = db_challenge.transaction_id
    # TODO: This line is deprecated: Add the information for the old administrative triggerchallenge
    reply_dict["transaction_ids"] = [db_challenge.transaction_id]

    return reply_dict
示例#4
0
    def create_challenge(self, transactionid=None, options=None):
        """
        This method creates a challenge, which is submitted to the user.
        The submitted challenge will be preserved in the challenge
        database.

        If no transaction id is given, the system will create a transaction
        id and return it, so that the response can refer to this transaction.

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :type options: dict
        :return: tuple of (bool, message, transactionid, attributes)
        :rtype: tuple

        The return tuple builds up like this:
        ``bool`` if submit was successful;
        ``message`` which is displayed in the JSON response;
        additional ``attributes``, which are displayed in the JSON response.
        """
        options = options or {}
        message = 'Please scan the QR Code'

        # Get ValidityTime=120s. Maybe there is a TIQRChallengeValidityTime...
        validity = int(get_from_config('DefaultChallengeValidityTime', 120))
        tokentype = self.get_tokentype().lower()
        lookup_for = tokentype.capitalize() + 'ChallengeValidityTime'
        validity = int(get_from_config(lookup_for, validity))

        # We need to set the user ID
        user_identifier, user_displayname = self.get_user_displayname()

        service_identifier = get_from_config("tiqr.serviceIdentifier") or \
                             "org.privacyidea"

        # Get the OCRASUITE from the token information
        ocrasuite = self.get_tokeninfo("ocrasuite") or OCRA_DEFAULT_SUITE
        # Depending on the OCRA-SUITE we create the challenge
        os = OCRASuite(ocrasuite)
        challenge = os.create_challenge()

        # Create the challenge in the database
        db_challenge = Challenge(self.token.serial,
                                 transaction_id=None,
                                 challenge=challenge,
                                 data=None,
                                 session=options.get("session"),
                                 validitytime=validity)
        db_challenge.save()

        authurl = "tiqrauth://{0!s}@{1!s}/{2!s}/{3!s}".format(user_identifier,
                                              service_identifier,
                                              db_challenge.transaction_id,
                                              challenge)
        attributes = {"img": create_img(authurl, width=250),
                      "value": authurl,
                      "poll": True,
                      "hideResponseInput": True}

        return True, message, db_challenge.transaction_id, attributes
示例#5
0
    def test_16_remove_token(self):
        self.assertRaises(ParameterError, remove_token)

        count1 = get_tokens(count=True)
        tokenobject = init_token({"type": "hotp",
                                  "otpkey": "1234567890123456",
                                  "realm": self.realm1})
        count2 = get_tokens(count=True)
        self.assertTrue(count2 == count1 + 1, count2)
        # check for the token association
        token_id = tokenobject.token.id
        realm_assoc = TokenRealm.query.filter(TokenRealm.token_id == \
            token_id).count()
        self.assertTrue(realm_assoc == 1, realm_assoc)
        # Add a challenge for this token
        challenge = Challenge(tokenobject.get_serial(), transaction_id="918273")
        challenge.save()
        chall_count = Challenge.query.filter(Challenge.serial ==
                                             tokenobject.get_serial()).count()
        self.assertTrue(chall_count == 1, chall_count)

        # remove the token
        count_remove = remove_token(serial=tokenobject.get_serial())
        self.assertTrue(count_remove == 1, count_remove)
        self.assertTrue(get_tokens(count=True) == count1)
        # check for the realm association
        realm_assoc = TokenRealm.query.filter(TokenRealm.token_id == \
            token_id).count()
        self.assertTrue(realm_assoc == 0, realm_assoc)
        # check if the challenge is removed
        chall_count = Challenge.query.filter(Challenge.serial ==
                                             tokenobject.get_serial()).count()
        self.assertTrue(chall_count == 0, chall_count)
示例#6
0
    def create_challenge(self, transactionid=None, options=None):
        """
        create a challenge, which is submitted to the user

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
                You can pass exception=1 to raise an exception, if
                the SMS could not be sent. Otherwise the message is contained in the response.
        :return: tuple of (bool, message and data)
                 bool, if submit was successful
                 message is submitted to the user
                 data is preserved in the challenge
                 attributes - additional attributes, which are displayed in the
                    output
        """
        success = False
        options = options or {}
        return_message = get_action_values_from_options(
            SCOPE.AUTH, "{0!s}_{1!s}".format(self.get_class_type(),
                                             ACTION.CHALLENGETEXT),
            options) or _("Enter the OTP from the SMS:")
        attributes = {'state': transactionid}
        validity = self._get_sms_timeout()

        if self.is_active() is True:
            counter = self.get_otp_count()
            log.debug("counter={0!r}".format(counter))
            self.inc_otp_counter(counter, reset=False)
            # At this point we must not bail out in case of an
            # Gateway error, since checkPIN is successful. A bail
            # out would cancel the checking of the other tokens
            try:
                message_template = self._get_sms_text(options)
                success, sent_message = self._send_sms(
                    message=message_template)

                # Create the challenge in the database
                db_challenge = Challenge(self.token.serial,
                                         transaction_id=transactionid,
                                         challenge=options.get("challenge"),
                                         session=options.get("session"),
                                         validitytime=validity)
                db_challenge.save()
                transactionid = transactionid or db_challenge.transaction_id
            except Exception as e:
                info = ("The PIN was correct, but the "
                        "SMS could not be sent: %r" % e)
                log.warning(info)
                log.debug("{0!s}".format(traceback.format_exc()))
                return_message = info
                if is_true(options.get("exception")):
                    raise Exception(info)

        expiry_date = datetime.datetime.now() + \
                                    datetime.timedelta(seconds=validity)
        attributes['valid_until'] = "{0!s}".format(expiry_date)

        return success, return_message, transactionid, attributes
示例#7
0
    def create_challenge(self, transactionid=None, options=None):
        """
        create a challenge, which is submitted to the user

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
                You can pass exception=1 to raise an exception, if
                the SMS could not be sent. Otherwise the message is contained in the response.
        :return: tuple of (bool, message and data)
                 bool, if submit was successful
                 message is submitted to the user
                 data is preserved in the challenge
                 attributes - additional attributes, which are displayed in the
                    output
        """
        success = False
        options = options or {}
        return_message = get_action_values_from_options(SCOPE.AUTH,
                                                        "{0!s}_{1!s}".format(self.get_class_type(),
                                                                             ACTION.CHALLENGETEXT),
                                                        options) or _("Enter the OTP from the SMS:")
        attributes = {'state': transactionid}
        validity = self._get_sms_timeout()

        if self.is_active() is True:
            counter = self.get_otp_count()
            log.debug("counter={0!r}".format(counter))
            self.inc_otp_counter(counter, reset=False)
            # At this point we must not bail out in case of an
            # Gateway error, since checkPIN is successful. A bail
            # out would cancel the checking of the other tokens
            try:
                message_template = self._get_sms_text(options)
                success, sent_message = self._send_sms(
                    message=message_template)

                # Create the challenge in the database
                db_challenge = Challenge(self.token.serial,
                                         transaction_id=transactionid,
                                         challenge=options.get("challenge"),
                                         session=options.get("session"),
                                         validitytime=validity)
                db_challenge.save()
                transactionid = transactionid or db_challenge.transaction_id
            except Exception as e:
                info = ("The PIN was correct, but the "
                        "SMS could not be sent: %r" % e)
                log.warning(info)
                log.debug("{0!s}".format(traceback.format_exc()))
                return_message = info
                if is_true(options.get("exception")):
                    raise Exception(info)

        expiry_date = datetime.datetime.now() + \
                                    datetime.timedelta(seconds=validity)
        attributes['valid_until'] = "{0!s}".format(expiry_date)

        return success, return_message, transactionid, attributes
示例#8
0
    def create_challenge(self, transactionid=None, options=None):
        """
        create a challenge, which is submitted to the user

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :return: tuple of (bool, message and data)
                 bool, if submit was successful
                 message is submitted to the user
                 data is preserved in the challenge
                 attributes - additional attributes, which are displayed in the
                    output
        """
        success = False
        options = options or {}
        return_message = "Enter the OTP from the Email:"
        attributes = {'state': transactionid}
        validity = int(get_from_config("email.validtime", 120))

        if self.is_active() is True:
            counter = self.get_otp_count()
            log.debug("counter={0!r}".format(counter))
            self.inc_otp_counter(counter, reset=False)
            # At this point we must not bail out in case of an
            # Gateway error, since checkPIN is successful. A bail
            # out would cancel the checking of the other tokens
            try:
                message_template, mimetype = self._get_email_text_or_subject(
                    options)
                subject_template, _ = self._get_email_text_or_subject(
                    options, EMAILACTION.EMAILSUBJECT, "Your OTP")

                # Create the challenge in the database
                db_challenge = Challenge(self.token.serial,
                                         transaction_id=transactionid,
                                         challenge=options.get("challenge"),
                                         session=options.get("session"),
                                         validitytime=validity)
                db_challenge.save()
                transactionid = transactionid or db_challenge.transaction_id
                # We send the email after creating the challenge for testing.
                success, sent_message = self._compose_email(
                    message=message_template,
                    subject=subject_template,
                    mimetype=mimetype)

            except Exception as e:
                info = ("The PIN was correct, but the "
                        "EMail could not be sent: %r" % e)
                log.warning(info)
                log.debug(u"{0!s}".format(traceback.format_exc(e)))
                return_message = info

        expiry_date = datetime.datetime.now() + \
                                    datetime.timedelta(seconds=validity)
        attributes['valid_until'] = "{0!s}".format(expiry_date)

        return success, return_message, transactionid, attributes
示例#9
0
    def create_challenge(self, transactionid=None, options=None):
        """
        This method creates a challenge, which is submitted to the user.
        The submitted challenge will be preserved in the challenge
        database.

        If no transaction id is given, the system will create a transaction
        id and return it, so that the response can refer to this transaction.

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :type options: dict
        :return: tuple of (bool, message, transactionid, attributes)
        :rtype: tuple

        The return tuple builds up like this:
        ``bool`` if submit was successful;
        ``message`` which is displayed in the JSON response;
        additional ``attributes``, which are displayed in the JSON response.
        """
        options = options or {}
        message = 'Please confirm with your U2F token ({0!s})'.format( \
                  self.token.description)

        validity = int(get_from_config('DefaultChallengeValidityTime', 120))
        tokentype = self.get_tokentype().lower()
        lookup_for = tokentype.capitalize() + 'ChallengeValidityTime'
        validity = int(get_from_config(lookup_for, validity))

        challenge = geturandom(32)
        # Create the challenge in the database
        db_challenge = Challenge(self.token.serial,
                                 transaction_id=None,
                                 challenge=binascii.hexlify(challenge),
                                 data=None,
                                 session=options.get("session"),
                                 validitytime=validity)
        db_challenge.save()
        sec_object = self.token.get_otpkey()
        key_handle_hex = sec_object.getKey()
        key_handle_bin = binascii.unhexlify(key_handle_hex)
        key_handle_url = url_encode(key_handle_bin)
        challenge_url = url_encode(challenge)
        u2f_sign_request = {
            "appId": self.get_tokeninfo("appId"),
            "version": U2F_Version,
            "challenge": challenge_url,
            "keyHandle": key_handle_url
        }

        image_url = IMAGES.get(self.token.description.lower().split()[0], "")
        response_details = {
            "u2fSignRequest": u2f_sign_request,
            "hideResponseInput": True,
            "img": image_url
        }

        return True, message, db_challenge.transaction_id, response_details
示例#10
0
    def create_challenge(self, transactionid=None, options=None):
        """
        This method creates a challenge, which is submitted to the user.
        The submitted challenge will be preserved in the challenge
        database.

        If no transaction id is given, the system will create a transaction
        id and return it, so that the response can refer to this transaction.

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :type options: dict
        :return: tuple of (bool, message, transactionid, attributes)
        :rtype: tuple

        The return tuple builds up like this:
        ``bool`` if submit was successful;
        ``message`` which is displayed in the JSON response;
        additional ``attributes``, which are displayed in the JSON response.
        """
        options = options or {}
        message = get_action_values_from_options(SCOPE.AUTH,
                                                 "{0!s}_{1!s}".format(self.get_class_type(),
                                                                      ACTION.CHALLENGETEXT),
                                                 options)or _(u'Please confirm with your U2F token ({0!s})').format(
            self.token.description)

        validity = int(get_from_config('DefaultChallengeValidityTime', 120))
        tokentype = self.get_tokentype().lower()
        lookup_for = tokentype.capitalize() + 'ChallengeValidityTime'
        validity = int(get_from_config(lookup_for, validity))

        challenge = geturandom(32)
        # Create the challenge in the database
        db_challenge = Challenge(self.token.serial,
                                 transaction_id=transactionid,
                                 challenge=hexlify_and_unicode(challenge),
                                 data=None,
                                 session=options.get("session"),
                                 validitytime=validity)
        db_challenge.save()
        sec_object = self.token.get_otpkey()
        key_handle_hex = sec_object.getKey()
        key_handle_bin = binascii.unhexlify(key_handle_hex)
        key_handle_url = url_encode(key_handle_bin)
        challenge_url = url_encode(challenge)
        u2f_sign_request = {"appId": self.get_tokeninfo("appId"),
                            "version": U2F_Version,
                            "challenge": challenge_url,
                            "keyHandle": key_handle_url}

        image_url = IMAGES.get(self.token.description.lower().split()[0], "")
        response_details = {"u2fSignRequest": u2f_sign_request,
                            "hideResponseInput": True,
                            "img": image_url}

        return True, message, db_challenge.transaction_id, response_details
示例#11
0
    def test_13_challenges_transaction(self):
        transaction_id = "some_id"
        challenge = Challenge("hotptoken", transaction_id=transaction_id,
                              challenge="You dont guess this")
        challenge.save()

        serial = get_tokenserial_of_transaction(transaction_id)
        self.assertTrue(serial == "hotptoken", serial)

        # Challenge does not exist
        serial = get_tokenserial_of_transaction("other id")
        self.assertTrue(serial is None, serial)
示例#12
0
    def create_challenge(self, transactionid=None, options=None):
        """
        create a challenge, which is submitted to the user

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :return: tuple of (bool, message and data)
                 bool, if submit was successful
                 message is submitted to the user
                 data is preserved in the challenge
                 attributes - additional attributes, which are displayed in the
                    output
        """
        success = False
        options = options or {}
        return_message = "Enter the OTP from the Email:"
        attributes = {'state': transactionid}

        if self.is_active() is True:
            counter = self.get_otp_count()
            log.debug("counter={0!r}".format(counter))
            self.inc_otp_counter(counter, reset=False)
            # At this point we must not bail out in case of an
            # Gateway error, since checkPIN is successful. A bail
            # out would cancel the checking of the other tokens
            try:
                message_template = self._get_email_text_or_subject(options)
                subject_template = self._get_email_text_or_subject(options,
                                                                   EMAILACTION.EMAILSUBJECT,
                                                                   "Your OTP")
                validity = int(get_from_config("email.validtime", 120))

                # Create the challenge in the database
                db_challenge = Challenge(self.token.serial,
                                         transaction_id=transactionid,
                                         challenge=options.get("challenge"),
                                         session=options.get("session"),
                                         validitytime=validity)
                db_challenge.save()
                transactionid = transactionid or db_challenge.transaction_id
                # We send the email after creating the challenge for testing.
                success, sent_message = self._compose_email(
                    message=message_template,
                    subject=subject_template)

            except Exception as e:
                info = ("The PIN was correct, but the "
                        "EMail could not be sent: %r" % e)
                log.warning(info)
                log.debug("{0!s}".format(traceback.format_exc(e)))
                return_message = info

        return success, return_message, transactionid, attributes
示例#13
0
    def create_challenge(self, transactionid=None, options=None):
        """
        create a challenge, which is submitted to the user

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :return: tuple of (bool, message and data)
                 bool, if submit was successful
                 message is submitted to the user
                 data is preserved in the challenge
                 attributes - additional attributes, which are displayed in the
                    output
        """
        success = False
        sms = ""
        options = options or {}
        return_message = "Enter the OTP from the SMS:"
        attributes = {'state': transactionid}
        validity = self._get_sms_timeout()

        if self.is_active() is True:
            counter = self.get_otp_count()
            log.debug("counter=%r" % counter)
            self.inc_otp_counter(counter, reset=False)
            # At this point we must not bail out in case of an
            # Gateway error, since checkPIN is successful. A bail
            # out would cancel the checking of the other tokens
            try:
                message_template = self._get_sms_text(options)
                success, sent_message = self._send_sms(
                    message=message_template)

                # Create the challenge in the database
                db_challenge = Challenge(self.token.serial,
                                         transaction_id=transactionid,
                                         challenge=options.get("challenge"),
                                         session=options.get("session"),
                                         validitytime=validity)
                db_challenge.save()
                transactionid = transactionid or db_challenge.transaction_id
            except Exception as e:
                info = ("The PIN was correct, but the "
                        "SMS could not be sent: %r" % e)
                log.warning(info)
                return_message = info

        validity = self._get_sms_timeout()
        expiry_date = datetime.datetime.now() + \
                                    datetime.timedelta(seconds=validity)
        attributes['valid_until'] = "%s" % expiry_date

        return success, return_message, transactionid, attributes
示例#14
0
    def create_challenge(self, transactionid=None, options=None):
        """
        create a challenge, which is submitted to the user

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :return: tuple of (bool, message and data)
                 bool, if submit was successful
                 message is submitted to the user
                 data is preserved in the challenge
                 attributes - additional attributes, which are displayed in the
                    output
        """
        success = False
        sms = ""
        options = options or {}
        return_message = "Enter the OTP from the SMS:"
        attributes = {'state': transactionid}
        validity = self._get_sms_timeout()

        if self.is_active() is True:
            counter = self.get_otp_count()
            log.debug("counter=%r" % counter)
            self.inc_otp_counter(counter, reset=False)
            # At this point we must not bail out in case of an
            # Gateway error, since checkPIN is successful. A bail
            # out would cancel the checking of the other tokens
            try:
                message_template = self._get_sms_text(options)
                success, sent_message = self._send_sms(
                    message=message_template)

                # Create the challenge in the database
                db_challenge = Challenge(self.token.serial,
                                         transaction_id=transactionid,
                                         challenge=options.get("challenge"),
                                         session=options.get("session"),
                                         validitytime=validity)
                db_challenge.save()
                transactionid = transactionid or db_challenge.transaction_id
            except Exception as e:
                info = ("The PIN was correct, but the "
                        "SMS could not be sent: %r" % e)
                log.warning(info)
                return_message = info

        validity = self._get_sms_timeout()
        expiry_date = datetime.datetime.now() + \
                                    datetime.timedelta(seconds=validity)
        attributes['valid_until'] = "%s" % expiry_date

        return success, return_message, transactionid, attributes
    def create_challenge(self, transactionid=None, options=None):
        """
        This method creates a challenge, which is submitted to the user.
        The submitted challenge will be preserved in the challenge
        database.

        The challenge is a randomly selected question of the available
        questions for this token.

        If no transaction id is given, the system will create a transaction
        id and return it, so that the response can refer to this transaction.

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :type options: dict
        :return: tuple of (bool, message, transactionid, attributes)
        :rtype: tuple

        The return tuple builds up like this:
        ``bool`` if submit was successful;
        ``message`` which is displayed in the JSON response;
        additional ``attributes``, which are displayed in the JSON response.
        """
        options = options or {}

        # Get a random question
        questions = []
        tinfo = self.get_tokeninfo()
        for question, answer in tinfo.iteritems():
            if question.endswith(".type") and answer == "password":
                # This is "Question1?.type" of type "password"
                # So this is actually a question and we add the question to
                # the list
                questions.append(question.strip(".type"))
        message = random.choice(questions)
        attributes = None

        validity = int(get_from_config('DefaultChallengeValidityTime', 120))
        tokentype = self.get_tokentype().lower()
        # Maybe there is a QUESTIONChallengeValidityTime...
        lookup_for = tokentype.capitalize() + 'ChallengeValidityTime'
        validity = int(get_from_config(lookup_for, validity))

        # Create the challenge in the database
        db_challenge = Challenge(self.token.serial,
                                 transaction_id=transactionid,
                                 challenge=message,
                                 validitytime=validity)
        db_challenge.save()
        self.challenge_janitor()
        return True, message, db_challenge.transaction_id, attributes
    def test_18_challenges(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = DaplugTokenClass(db_token)
        resp = token.is_challenge_response(User(login="******", realm=self.realm1), "test" + _digi2daplug("123456"))
        self.assertFalse(resp, resp)
        resp = token.is_challenge_response(
            User(login="******", realm=self.realm1),
            "test" + _digi2daplug("123456"),
            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()
    def test_20_check_challenge_response(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        db_token.set_pin("test")
        token = DaplugTokenClass(db_token)
        r = token.check_challenge_response(user=None, passw=_digi2daplug("123454"))
        # check empty challenges
        self.assertTrue(r == -1, r)

        # 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=_digi2daplug("123454"), options={"state": "mytransaction"})
        # The challenge matches, but the OTP does not match!
        self.assertTrue(r == -1, r)
def _create_pin_reset_challenge(token_obj, message, challenge_data=None):
    validity = int(get_from_config('DefaultChallengeValidityTime', 120))
    validity = int(get_from_config('PinResetChallengeValidityTime', validity))
    db_challenge = Challenge(token_obj.token.serial,
                             challenge=CHALLENGE_TYPE.PIN_RESET,
                             data=challenge_data,
                             validitytime=validity)
    db_challenge.save()
    token_obj.challenge_janitor()
    reply_dict = {}
    reply_dict["multi_challenge"] = [{"transaction_id": db_challenge.transaction_id,
                                      "message": message,
                                      "serial": token_obj.token.serial,
                                      "type": token_obj.token.tokentype}]
    reply_dict["message"] = message
    reply_dict["transaction_id"] = db_challenge.transaction_id
    reply_dict["transaction_ids"] = [db_challenge.transaction_id]

    return reply_dict
示例#19
0
    def test_18_challenges(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        token = DaplugTokenClass(db_token)
        resp = token.is_challenge_response(User(login="******",
                                                realm=self.realm1),
                                            "test"+_digi2daplug("123456"))
        self.assertFalse(resp, resp)

        transaction_id = "123456789"
        C = Challenge(self.serial1, transaction_id=transaction_id, challenge="Who are you?")
        C.save()
        resp = token.is_challenge_response(User(login="******",
                                                realm=self.realm1),
                                            "test"+_digi2daplug("123456"),
                                            options={"transaction_id":
                                                         transaction_id})
        self.assertTrue(resp, resp)

        # test if challenge is valid
        C.is_valid()
示例#20
0
 def test_12_challenge(self):
     c = Challenge("S123456")
     self.assertTrue(len(c.transaction_id) == 20, c.transaction_id)
     self.assertTrue(len(c.get_transaction_id()) == 20, c.transaction_id)
     
     c.set_data("some data")
     self.assertTrue(c.data == "some data", c.data)
     self.assertTrue(c.get_data() == "some data", c.data)
     c.set_data({"some": "data"})
     self.assertTrue("some" in c.data, c.data)
     c.set_session("session")
     self.assertTrue(c.get_session() == "session", c.session)
     c.set_challenge("challenge")
     self.assertTrue(c.get_challenge() == "challenge", c.challenge)
     
     self.assertTrue("otp_received" in "{0!s}".format(c), "{0!s}".format(c))
     self.assertTrue("transaction_id" in "{0!s}".format(c), "{0!s}".format(c))
     self.assertTrue("timestamp" in "{0!s}".format(c), "{0!s}".format(c))
     
     # test with timestamp=True, which results in something like this:
     timestamp = '2014-11-29 21:56:43.057293'
     self.assertTrue(len(c.get(True).get("timestamp")) == len(timestamp),
                     c.get(True))
     # otp_status
     c.set_otp_status(valid=False)
     self.assertTrue(c.get_otp_status()[0], c.get_otp_status())
     self.assertFalse(c.get_otp_status()[1], c.get_otp_status())
示例#21
0
    def create_challenge(self, transactionid=None, options=None):
        """
        This method creates a challenge, which is submitted to the user.
        The submitted challenge will be preserved in the challenge
        database.

        If no transaction id is given, the system will create a transaction
        id and return it, so that the response can refer to this transaction.

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :type options: dict
        :return: tuple of (bool, message, transactionid, attributes)
        :rtype: tuple

        The return tuple builds up like this:
        ``bool`` if submit was successful;
        ``message`` which is displayed in the JSON response;
        additional ``attributes``, which are displayed in the JSON response.
        """
        options = options or {}
        message = 'Please answer the challenge'
        attributes = {}

        # Get ValidityTime=120s. Maybe there is a OCRAChallengeValidityTime...
        validity = int(get_from_config('DefaultChallengeValidityTime', 120))
        tokentype = self.get_tokentype().lower()
        lookup_for = tokentype.capitalize() + 'ChallengeValidityTime'
        validity = int(get_from_config(lookup_for, validity))

        # Get the OCRASUITE from the token information
        ocrasuite = self.get_tokeninfo("ocrasuite") or OCRA_DEFAULT_SUITE

        challenge = options.get("challenge")
        # TODO: we could add an additional parameter to hash the challenge
        # cleartext -> sha1
        if not challenge:
            # If no challenge is given in the Request, we create a random
            # challenge based on the OCRA-SUITE
            os = OCRASuite(ocrasuite)
            challenge = os.create_challenge()
        else:
            # Add a random challenge
            if options.get("addrandomchallenge"):
                challenge += get_alphanum_str(int(options.get(
                    "addrandomchallenge")))
            attributes["original_challenge"] = challenge
            attributes["qrcode"] = create_img(challenge)
            if options.get("hashchallenge", "").lower() == "sha256":
                challenge = binascii.hexlify(hashlib.sha256(challenge).digest())
            elif options.get("hashchallenge", "").lower() == "sha512":
                challenge = binascii.hexlify(hashlib.sha512(challenge).digest())
            elif options.get("hashchallenge"):
                challenge = binascii.hexlify(hashlib.sha1(challenge).digest())


        # Create the challenge in the database
        db_challenge = Challenge(self.token.serial,
                                 transaction_id=None,
                                 challenge=challenge,
                                 data=None,
                                 session=None,
                                 validitytime=validity)
        db_challenge.save()

        attributes["challenge"] = challenge

        return True, message, db_challenge.transaction_id, attributes
示例#22
0
    def test_05_u2f_auth_fails_wrong_issuer(self):
        # test data taken from
        # https://fidoalliance.org/specs/fido-u2f-v1.0-ps-20141009/fido-u2f-raw-message-formats-ps-20141009.html#examples
        serial = "U2F0010BF6F"
        set_privacyidea_config("u2f.appId",
                               "https://puck.az.intern")
        pin = "test"
        # Registration data
        client_data = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6ImpIakIxaEM2VjA3dDl4ZnNNaDRfOEQ3U1JuSHRFY1BqUTdsaVl3cWxkX009Iiwib3JpZ2luIjoiaHR0cHM6Ly9wdWNrLmF6LmludGVybiIsImNpZF9wdWJrZXkiOiJ1bnVzZWQifQ"
        reg_data = "BQRHjwxEYFCkLHz3xdrmifKOHl2h17BmRJQ_S1Y9PRAhS2R186T391YE-ryqWis9HSmdp0XpRqUaKk9L8lxJTPpTQF_xFJ_LAsKkPTzKIwUlPIjGZDsLmv0en2Iya17Yz8X8OS89fuxwZOvEok-NQOKUTJP3att_RVe3dEAbq_iOtyAwggJEMIIBLqADAgECAgRVYr6gMAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWNvIFUyRiBFRSBTZXJpYWwgMTQzMjUzNDY4ODBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEszH3c9gUS5mVy-RYVRfhdYOqR2I2lcvoWsSCyAGfLJuUZ64EWw5m8TGy6jJDyR_aYC4xjz_F2NKnq65yvRQwmjOzA5MCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS41MBMGCysGAQQBguUcAgEBBAQDAgUgMAsGCSqGSIb3DQEBCwOCAQEArBbZs262s6m3bXWUs09Z9Pc-28n96yk162tFHKv0HSXT5xYU10cmBMpypXjjI-23YARoXwXn0bm-BdtulED6xc_JMqbK-uhSmXcu2wJ4ICA81BQdPutvaizpnjlXgDJjq6uNbsSAp98IStLLp7fW13yUw-vAsWb5YFfK9f46Yx6iakM3YqNvvs9M9EUJYl_VrxBJqnyLx2iaZlnpr13o8NcsKIJRdMUOBqt_ageQg3ttsyq_3LyoNcu7CQ7x8NmeCGm_6eVnZMQjDmwFdymwEN4OxfnM5MkcKCYhjqgIGruWkVHsFnJa8qjZXneVvKoiepuUQyDEJ2GcqvhU2YKY1zBFAiEA4ZkIXXyjEPExcMGtW6kJXqYv7UHgjxJR5h3H9w9FV7gCIFGdhxZDqwCQKplDi-LU4WJ45OyCpNK6lGa72eZqUR_k"
        # Authentication data
        transaction_id = "05871369157706202013"
        challenge = "1616515928c389ba9e028d83eb5f63782cbf351ca6abbc81aeb0dddd4895b609"
        # challenge = "FhZRWSjDibqeAo2D619jeCy_NRymq7yBrrDd3UiVtgk"
        key_handle = "X_EUn8sCwqQ9PMojBSU8iMZkOwua_R6fYjJrXtjPxfw5Lz1-7HBk68SiT41A4pRMk_dq239FV7d0QBur-I63IA"
        client_data_auth = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiRmhaUldTakRpYnFlQW8yRDYxOWplQ3lfTlJ5bXE3eUJyckRkM1VpVnRnayIsIm9yaWdpbiI6Imh0dHBzOi8vcHVjay5hei5pbnRlcm4iLCJjaWRfcHVia2V5IjoidW51c2VkIn0"
        signature_data = "AQAAAAMwRQIgU8d6waOIRVVydg_AXxediEZGkfFioUjd6FG3OxH2wUMCIQDpxzavJyxRlMwgNmD1Kw-iw_oP2egdshU9hrpxFHTRzQ"

        # step 1
        with self.app.test_request_context('/token/init',
                                           method='POST',
                                           data={"type": "u2f",
                                                 "user": "******",
                                                 "realm": self.realm1,
                                                 "serial": serial},
                                           headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            result = json.loads(res.data).get("result")
            detail = json.loads(res.data).get("detail")
            self.assertEqual(result.get("status"), True)
            self.assertEqual(result.get("value"), True)

        # Init step 2
        with self.app.test_request_context('/token/init',
                                           method='POST',
                                           data={"type": "u2f",
                                                 "serial": serial,
                                                 "regdata": reg_data,
                                                 "clientdata": client_data},
                                           headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            result = json.loads(res.data).get("result")
            detail = json.loads(res.data).get("detail")
            self.assertEqual(result.get("status"), True)
            self.assertEqual(result.get("value"), True)

        # create a challenge in the database
        db_challenge = Challenge(serial,
                                 transaction_id=transaction_id,
                                 challenge=challenge,
                                 data=None)
        db_challenge.save()

        set_policy(name="u2f01", scope=SCOPE.AUTHZ,
                   action="{0!s}=issuer/.*Plugup.*/".format(U2FACTION.REQ) )

        # Successful C/R authentication
        with self.app.test_request_context('/validate/check',
                                           method='POST',
                                           data={"serial": serial,
                                                 "pass": "",
                                                 "transaction_id":
                                                     transaction_id,
                                                 "clientdata":
                                                     client_data_auth,
                                                 "signaturedata":
                                                     signature_data}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 403)
            result = json.loads(res.data).get("result")
            self.assertEqual(result.get("status"), False)
            self.assertEqual(result.get("error").get("message"),
                             u'The U2F device is not allowed to authenticate due to policy restriction.')

        delete_policy("u2f01")
        remove_token(serial)
示例#23
0
    def test_20_check_challenge_response(self):
        db_token = Token.query.filter_by(serial=self.serial1).first()
        db_token.set_pin("test")
        token = TotpTokenClass(db_token)
        r = token.check_challenge_response(user=None,
                                           passw="123454")
        # check empty challenges
        self.assertTrue(r == -1, r)

        # 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=100)
        c3.save()
        c4 = Challenge(self.serial1, transaction_id="t4", validitytime=100)
        c4.save()
        num = Challenge.query.filter(Challenge.serial==self.serial1).count()
        self.assertTrue(num >= 5, num)
        # We pass the third challenge as the valid challenge.
        # So 3 challenges will be deleted.
        token.challenge_janitor()
        # Now see if those challenges are deleted
        num1 = Challenge.query.filter(Challenge.transaction_id == "t1").count()
        num2 = Challenge.query.filter(Challenge.transaction_id == "t2").count()
        num3 = Challenge.query.filter(Challenge.transaction_id == "t3").count()
        num4 = Challenge.query.filter(Challenge.transaction_id == "t4").count()
        self.assertTrue(num1 == 0)
        self.assertTrue(num2 == 0)
        self.assertTrue(num3 == 1)
        self.assertTrue(num4 == 1)
示例#24
0
    def test_05_u2f_auth_fails_wrong_issuer(self):
        # test data taken from
        # https://fidoalliance.org/specs/fido-u2f-v1.0-ps-20141009/fido-u2f-raw-message-formats-ps-20141009.html#examples
        serial = "U2F0010BF6F"
        set_privacyidea_config("u2f.appId", "https://puck.az.intern")
        pin = "test"
        # Registration data
        client_data = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6ImpIakIxaEM2VjA3dDl4ZnNNaDRfOEQ3U1JuSHRFY1BqUTdsaVl3cWxkX009Iiwib3JpZ2luIjoiaHR0cHM6Ly9wdWNrLmF6LmludGVybiIsImNpZF9wdWJrZXkiOiJ1bnVzZWQifQ"
        reg_data = "BQRHjwxEYFCkLHz3xdrmifKOHl2h17BmRJQ_S1Y9PRAhS2R186T391YE-ryqWis9HSmdp0XpRqUaKk9L8lxJTPpTQF_xFJ_LAsKkPTzKIwUlPIjGZDsLmv0en2Iya17Yz8X8OS89fuxwZOvEok-NQOKUTJP3att_RVe3dEAbq_iOtyAwggJEMIIBLqADAgECAgRVYr6gMAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWNvIFUyRiBFRSBTZXJpYWwgMTQzMjUzNDY4ODBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEszH3c9gUS5mVy-RYVRfhdYOqR2I2lcvoWsSCyAGfLJuUZ64EWw5m8TGy6jJDyR_aYC4xjz_F2NKnq65yvRQwmjOzA5MCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS41MBMGCysGAQQBguUcAgEBBAQDAgUgMAsGCSqGSIb3DQEBCwOCAQEArBbZs262s6m3bXWUs09Z9Pc-28n96yk162tFHKv0HSXT5xYU10cmBMpypXjjI-23YARoXwXn0bm-BdtulED6xc_JMqbK-uhSmXcu2wJ4ICA81BQdPutvaizpnjlXgDJjq6uNbsSAp98IStLLp7fW13yUw-vAsWb5YFfK9f46Yx6iakM3YqNvvs9M9EUJYl_VrxBJqnyLx2iaZlnpr13o8NcsKIJRdMUOBqt_ageQg3ttsyq_3LyoNcu7CQ7x8NmeCGm_6eVnZMQjDmwFdymwEN4OxfnM5MkcKCYhjqgIGruWkVHsFnJa8qjZXneVvKoiepuUQyDEJ2GcqvhU2YKY1zBFAiEA4ZkIXXyjEPExcMGtW6kJXqYv7UHgjxJR5h3H9w9FV7gCIFGdhxZDqwCQKplDi-LU4WJ45OyCpNK6lGa72eZqUR_k"
        # Authentication data
        transaction_id = "05871369157706202013"
        challenge = "1616515928c389ba9e028d83eb5f63782cbf351ca6abbc81aeb0dddd4895b609"
        # challenge = "FhZRWSjDibqeAo2D619jeCy_NRymq7yBrrDd3UiVtgk"
        key_handle = "X_EUn8sCwqQ9PMojBSU8iMZkOwua_R6fYjJrXtjPxfw5Lz1-7HBk68SiT41A4pRMk_dq239FV7d0QBur-I63IA"
        client_data_auth = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiRmhaUldTakRpYnFlQW8yRDYxOWplQ3lfTlJ5bXE3eUJyckRkM1VpVnRnayIsIm9yaWdpbiI6Imh0dHBzOi8vcHVjay5hei5pbnRlcm4iLCJjaWRfcHVia2V5IjoidW51c2VkIn0"
        signature_data = "AQAAAAMwRQIgU8d6waOIRVVydg_AXxediEZGkfFioUjd6FG3OxH2wUMCIQDpxzavJyxRlMwgNmD1Kw-iw_oP2egdshU9hrpxFHTRzQ"

        # step 1
        with self.app.test_request_context('/token/init',
                                           method='POST',
                                           data={
                                               "type": "u2f",
                                               "user": "******",
                                               "realm": self.realm1,
                                               "serial": serial
                                           },
                                           headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            result = json.loads(res.data.decode('utf8')).get("result")
            detail = json.loads(res.data.decode('utf8')).get("detail")
            self.assertEqual(result.get("status"), True)
            self.assertEqual(result.get("value"), True)

        # Init step 2
        with self.app.test_request_context('/token/init',
                                           method='POST',
                                           data={
                                               "type": "u2f",
                                               "serial": serial,
                                               "regdata": reg_data,
                                               "clientdata": client_data
                                           },
                                           headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            result = json.loads(res.data.decode('utf8')).get("result")
            detail = json.loads(res.data.decode('utf8')).get("detail")
            self.assertEqual(result.get("status"), True)
            self.assertEqual(result.get("value"), True)

        # create a challenge in the database
        db_challenge = Challenge(serial,
                                 transaction_id=transaction_id,
                                 challenge=challenge,
                                 data=None)
        db_challenge.save()

        set_policy(name="u2f01",
                   scope=SCOPE.AUTHZ,
                   action="{0!s}=issuer/.*Plugup.*/".format(U2FACTION.REQ))

        # Successful C/R authentication
        with self.app.test_request_context('/validate/check',
                                           method='POST',
                                           data={
                                               "serial": serial,
                                               "pass": "",
                                               "transaction_id":
                                               transaction_id,
                                               "clientdata": client_data_auth,
                                               "signaturedata": signature_data
                                           }):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 403)
            result = json.loads(res.data.decode('utf8')).get("result")
            self.assertEqual(result.get("status"), False)
            self.assertEqual(result.get("error").get("code"), ERROR.POLICY)
            self.assertEqual(
                result.get("error").get("message"),
                u'The U2F device is not allowed to authenticate due to policy restriction.'
            )

        delete_policy("u2f01")
        remove_token(serial)
示例#25
0
    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)
示例#26
0
    def create_challenge(self, transactionid=None, options=None):
        """
        This method creates a challenge, which is submitted to the user.
        The submitted challenge will be preserved in the challenge
        database.

        If no transaction id is given, the system will create a transaction
        id and return it, so that the response can refer to this transaction.

        :param transactionid: the id of this challenge
        :param options: the request context parameters / data
        :type options: dict
        :return: tuple of (bool, message, transactionid, attributes)
        :rtype: tuple

        The return tuple builds up like this:
        ``bool`` if submit was successful;
        ``message`` which is displayed in the JSON response;
        additional ``attributes``, which are displayed in the JSON response.
        """
        res = False
        options = options or {}
        message = get_action_values_from_options(SCOPE.AUTH,
                                                 ACTION.CHALLENGETEXT,
                                                 options) or DEFAULT_CHALLENGE_TEXT

        sslverify = get_action_values_from_options(SCOPE.AUTH,
                                                   PUSH_ACTION.SSL_VERIFY,
                                                   options) or "1"
        sslverify = getParam({"sslverify": sslverify}, "sslverify", allowed_values=["0", "1"], default="1")

        attributes = None
        data = None
        challenge = b32encode_and_unicode(geturandom())
        fb_identifier = self.get_tokeninfo(PUSH_ACTION.FIREBASE_CONFIG)
        if fb_identifier:
            # We send the challenge to the Firebase service
            fb_gateway = create_sms_instance(fb_identifier)
            url = fb_gateway.smsgateway.option_dict.get(FIREBASE_CONFIG.REGISTRATION_URL)
            message_on_mobile = get_action_values_from_options(SCOPE.AUTH,
                                                   PUSH_ACTION.MOBILE_TEXT,
                                                   options) or DEFAULT_MOBILE_TEXT
            title = get_action_values_from_options(SCOPE.AUTH,
                                                   PUSH_ACTION.MOBILE_TITLE,
                                                   options) or "privacyIDEA"
            smartphone_data = {"nonce": challenge,
                               "question": message_on_mobile,
                               "serial": self.token.serial,
                               "title": title,
                               "sslverify": sslverify,
                               "url": url}
            # Create the signature.
            # value to string
            sign_string = u"{nonce}|{url}|{serial}|{question}|{title}|{sslverify}".format(**smartphone_data)

            pem_privkey = self.get_tokeninfo(PRIVATE_KEY_SERVER)
            privkey_obj = serialization.load_pem_private_key(to_bytes(pem_privkey), None, default_backend())

            # Sign the data with PKCS1 padding. Not all Androids support PSS padding.
            signature = privkey_obj.sign(sign_string.encode("utf8"),
                                         padding.PKCS1v15(),
                                         hashes.SHA256())
            smartphone_data["signature"] = b32encode_and_unicode(signature)

            res = fb_gateway.submit_message(self.get_tokeninfo("firebase_token"), smartphone_data)
            if not res:
                raise ValidateError("Failed to submit message to firebase service.")
        else:
            log.warning(u"The token {0!s} has no tokeninfo {1!s}. "
                        u"The message could not be sent.".format(self.token.serial,
                                                                 PUSH_ACTION.FIREBASE_CONFIG))
            raise ValidateError("The token has no tokeninfo. Can not send via firebase service.")

        validity = int(get_from_config('DefaultChallengeValidityTime', 120))
        tokentype = self.get_tokentype().lower()
        # Maybe there is a PushChallengeValidityTime...
        lookup_for = tokentype.capitalize() + 'ChallengeValidityTime'
        validity = int(get_from_config(lookup_for, validity))

        # Create the challenge in the database
        db_challenge = Challenge(self.token.serial,
                                 transaction_id=transactionid,
                                 challenge=challenge,
                                 data=data,
                                 session=options.get("session"),
                                 validitytime=validity)
        db_challenge.save()
        self.challenge_janitor()
        return True, message, db_challenge.transaction_id, attributes