Beispiel #1
0
    def _compute_challenge(self, channel_binding):
        self._challenge = os.urandom(32)

        if self._channel_id:
            self._expected_signed_message = util.xor(self._challenge, self._channel_id)
        else:
            self._expected_signed_message = self._challenge

        extra = {u"challenge": binascii.b2a_hex(self._challenge)}
        return extra
        def sign_challenge(self, session, challenge):
            """
            Sign WAMP-cryptosign challenge.

            :param challenge: The WAMP-cryptosign challenge object for which a signature should be computed.
            :type challenge: instance of autobahn.wamp.types.Challenge

            :returns: A Deferred/Future that resolves to the computed signature.
            :rtype: str
            """
            if not isinstance(challenge, Challenge):
                raise Exception("challenge must be instance of autobahn.wamp.types.Challenge, not {}".format(type(challenge)))

            if u'challenge' not in challenge.extra:
                raise Exception("missing challenge value in challenge.extra")

            # the challenge sent by the router (a 32 bytes random value)
            challenge_hex = challenge.extra[u'challenge']

            # the challenge for WAMP-cryptosign is a 32 bytes random value in Hex encoding (that is, a unicode string)
            challenge_raw = binascii.a2b_hex(challenge_hex)

            # if the transport has a channel ID, the message to be signed by the client actually
            # is the XOR of the challenge and the channel ID
            channel_id_raw = session._transport.get_channel_id()
            if channel_id_raw:
                data = util.xor(challenge_raw, channel_id_raw)
            else:
                data = challenge_raw

            # a raw byte string is signed, and the signature is also a raw byte string
            d1 = self.sign(data)

            # asyncio lacks callback chaining (and we cannot use co-routines, since we want
            # to support older Pythons), hence we need d2
            d2 = txaio.create_future()

            def process(signature_raw):
                # convert the raw signature into a hex encode value (unicode string)
                signature_hex = binascii.b2a_hex(signature_raw).decode('ascii')

                # we return the concatenation of the signature and the message signed (96 bytes)
                data_hex = binascii.b2a_hex(data).decode('ascii')

                sig = signature_hex + data_hex
                txaio.resolve(d2, sig)

            txaio.add_callbacks(d1, process, None)

            return d2
        def sign_challenge(self, session, challenge):
            """
            Sign WAMP-cryptosign challenge.

            :param challenge: The WAMP-cryptosign challenge object for which a signature should be computed.
            :type challenge: instance of autobahn.wamp.types.Challenge

            :returns: A Deferred/Future that resolves to the computed signature.
            :rtype: unicode
            """
            if not isinstance(challenge, Challenge):
                raise Exception(
                    "challenge must be instance of autobahn.wamp.types.Challenge, not {}"
                    .format(type(challenge)))

            if u'challenge' not in challenge.extra:
                raise Exception("missing challenge value in challenge.extra")

            # the challenge sent by the router (a 32 bytes random value)
            challenge_hex = challenge.extra[u'challenge']

            # the challenge for WAMP-cryptosign is a 32 bytes random value in Hex encoding (that is, a unicode string)
            challenge_raw = binascii.a2b_hex(challenge_hex)

            # if the transport has a channel ID, the message to be signed by the client actually
            # is the XOR of the challenge and the channel ID
            channel_id_raw = session._transport.get_channel_id()
            if channel_id_raw:
                data = util.xor(challenge_raw, channel_id_raw)
            else:
                data = challenge_raw

            # a raw byte string is signed, and the signature is also a raw byte string
            signature_raw = yield self.sign(data)

            # convert the raw signature into a hex encode value (unicode string)
            signature_hex = binascii.b2a_hex(signature_raw).decode('ascii')

            # we return the concatenation of the signature and the message signed (96 bytes)
            data_hex = binascii.b2a_hex(data).decode('ascii')

            # we always return a future/deferred, so handling is uniform
            returnValue(signature_hex + data_hex)
Beispiel #4
0
    def format_challenge(challenge: Challenge, channel_id_raw: bytes, channel_id_type: str) -> bytes:
        """
        Format the challenge based on provided parameters

        :param challenge: The WAMP-cryptosign challenge object for which a signature should be computed.
        :param channel_id_raw: The channel ID when channel_id_type is 'tls-unique'.
        :param channel_id_type: The type of the channel id, currently handles 'tls-unique' and
            ignores otherwise.
        """
        if not isinstance(challenge, Challenge):
            raise Exception(
                "challenge must be instance of autobahn.wamp.types.Challenge, not {}".format(type(challenge)))

        if 'challenge' not in challenge.extra:
            raise Exception("missing challenge value in challenge.extra")

        # the challenge sent by the router (a 32 bytes random value)
        challenge_hex = challenge.extra['challenge']

        if type(challenge_hex) != str:
            raise Exception("invalid type {} for challenge (expected a hex string)".format(type(challenge_hex)))

        if len(challenge_hex) != 64:
            raise Exception("unexpected challenge (hex) length: was {}, but expected 64".format(len(challenge_hex)))

        # the challenge for WAMP-cryptosign is a 32 bytes random value in Hex encoding (that is, a unicode string)
        challenge_raw = binascii.a2b_hex(challenge_hex)

        if channel_id_type == 'tls-unique':
            assert len(
                channel_id_raw) == 32, 'unexpected TLS transport channel ID length (was {}, but expected 32)'.format(
                len(channel_id_raw))

            # with TLS channel binding of type "tls-unique", the message to be signed by the client actually
            # is the XOR of the challenge and the TLS channel ID
            data = util.xor(challenge_raw, channel_id_raw)
        elif channel_id_type is None:
            # when no channel binding was requested, the message to be signed by the client is the challenge only
            data = challenge_raw
        else:
            assert False, 'invalid channel_id_type "{}"'.format(channel_id_type)

        return data
Beispiel #5
0
    def sign_challenge(self, session, challenge):
        """
        Sign WAMP-cryptosign challenge.

        :param challenge: The WAMP-cryptosign challenge object for which a signature should be computed.
        :type challenge: instance of autobahn.wamp.types.Challenge

        :returns: A Deferred/Future that resolves to the computed signature.
        :rtype: unicode
        """
        if not isinstance(challenge, Challenge):
            raise Exception("challenge must be instance of autobahn.wamp.types.Challenge, not {}".format(type(challenge)))

        if u'challenge' not in challenge.extra:
            raise Exception("missing challenge value in challenge.extra")

        # the challenge sent by the router (a 32 bytes random value)
        challenge_hex = challenge.extra[u'challenge']

        # the challenge for WAMP-cryptosign is a 32 bytes random value in Hex encoding (that is, a unicode string)
        challenge_raw = binascii.a2b_hex(challenge_hex)

        # if the transport has a channel ID, the message to be signed by the client actually
        # is the XOR of the challenge and the channel ID
        channel_id_raw = session._transport.get_channel_id()
        if channel_id_raw:
            data = util.xor(challenge_raw, channel_id_raw)
        else:
            data = challenge_raw

        # a raw byte string is signed, and the signature is also a raw byte string
        signature_raw = yield self.sign(data)

        # convert the raw signature into a hex encode value (unicode string)
        signature_hex = binascii.b2a_hex(signature_raw).decode('ascii')

        # we return the concatenation of the signature and the message signed (96 bytes)
        data_hex = binascii.b2a_hex(data).decode('ascii')

        # we always return a future/deferred, so handling is uniform
        returnValue(signature_hex + data_hex)
Beispiel #6
0
    def authenticate(self, signed_message):
        """
        Verify the signed message sent by the client.

        :param signed_message: the base64-encoded result "ClientProof"
            from the SCRAM protocol
        """

        channel_binding = ""
        client_nonce = base64.b64encode(self._client_nonce).decode('ascii')
        server_nonce = base64.b64encode(self._server_nonce).decode('ascii')
        salt = base64.b64encode(self._salt).decode('ascii')
        auth_message = (
            "{client_first_bare},{server_first},{client_final_no_proof}".format(
                client_first_bare="n={},r={}".format(saslprep(self._authid), client_nonce),
                server_first="r={},s={},i={}".format(server_nonce, salt, self._iterations),
                client_final_no_proof="c={},r={}".format(channel_binding, server_nonce),
            )
        )

        received_client_proof = base64.b64decode(signed_message)

        client_signature = hmac.new(self._stored_key, auth_message.encode('ascii'), hashlib.sha256).digest()
        recovered_client_key = util.xor(client_signature, received_client_proof)
        recovered_stored_key = hashlib.new('sha256', recovered_client_key).digest()

        # if we adjust self._authextra before _accept() it gets sent
        # back to the client
        server_signature = hmac.new(self._server_key, auth_message.encode('ascii'), hashlib.sha256).digest()
        if self._authextra is None:
            self._authextra = {}
        self._authextra['scram_server_signature'] = base64.b64encode(server_signature).decode('ascii')

        if hmac.compare_digest(recovered_stored_key, self._stored_key):
            return self._accept()

        self.log.error("SCRAM authentication failed for '{authid}'", authid=self._authid)
        return types.Deny(message=u'SCRAM authentication failed')
Beispiel #7
0
    def authenticate(self, signed_message):
        """
        Verify the signed message sent by the client.

        :param signed_message: the base64-encoded result "ClientProof"
            from the SCRAM protocol
        """

        channel_binding = ""
        client_nonce = base64.b64encode(self._client_nonce).decode('ascii')
        server_nonce = base64.b64encode(self._server_nonce).decode('ascii')
        salt = base64.b64encode(self._salt).decode('ascii')
        auth_message = (
            "{client_first_bare},{server_first},{client_final_no_proof}".format(
                client_first_bare="n={},r={}".format(saslprep(self._authid), client_nonce),
                server_first="r={},s={},i={}".format(server_nonce, salt, self._iterations),
                client_final_no_proof="c={},r={}".format(channel_binding, server_nonce),
            )
        )

        received_client_proof = base64.b64decode(signed_message)

        client_signature = hmac.new(self._stored_key, auth_message.encode('ascii'), hashlib.sha256).digest()
        recovered_client_key = util.xor(client_signature, received_client_proof)
        recovered_stored_key = hashlib.new('sha256', recovered_client_key).digest()

        # if we adjust self._authextra before _accept() it gets sent
        # back to the client
        server_signature = hmac.new(self._server_key, auth_message.encode('ascii'), hashlib.sha256).digest()
        if self._authextra is None:
            self._authextra = {}
        self._authextra['scram_server_signature'] = base64.b64encode(server_signature).decode('ascii')

        if hmac.compare_digest(recovered_stored_key, self._stored_key):
            return self._accept()

        self.log.error("SCRAM authentication failed for '{authid}'", authid=self._authid)
        return types.Deny(message='SCRAM authentication failed')
Beispiel #8
0
        def sign_challenge(self, session, challenge):
            """
            Sign WAMP-cryptosign challenge.

            :param session: The authenticating WAMP session.
            :type session: :class:`autobahn.wamp.protocol.ApplicationSession`

            :param challenge: The WAMP-cryptosign challenge object for which a signature should be computed.
            :type challenge: instance of autobahn.wamp.types.Challenge

            :returns: A Deferred/Future that resolves to the computed signature.
            :rtype: str
            """
            if not isinstance(challenge, Challenge):
                raise Exception(
                    u"challenge must be instance of autobahn.wamp.types.Challenge, not {}"
                    .format(type(challenge)))

            if u'challenge' not in challenge.extra:
                raise Exception(u"missing challenge value in challenge.extra")

            # the challenge sent by the router (a 32 bytes random value)
            challenge_hex = challenge.extra[u'challenge']

            if type(challenge_hex) != six.text_type:
                raise Exception(
                    u"invalid type {} for challenge (expected a hex string)".
                    format(type(challenge_hex)))

            if len(challenge_hex) != 64:
                raise Exception(
                    u"unexpected challenge (hex) length: was {}, but expected 64"
                    .format(len(challenge_hex)))

            # the challenge for WAMP-cryptosign is a 32 bytes random value in Hex encoding (that is, a unicode string)
            challenge_raw = binascii.a2b_hex(challenge_hex)

            # if the transport has a channel ID, the message to be signed by the client actually
            # is the XOR of the challenge and the channel ID
            channel_id_raw = session._transport.get_channel_id()
            if channel_id_raw:
                assert len(
                    channel_id_raw
                ) == 32, 'unexpected TLS transport channel ID length: was {}, but expected 32'.format(
                    len(channel_id_raw))
                data = util.xor(challenge_raw, channel_id_raw)
            else:
                data = challenge_raw

            # a raw byte string is signed, and the signature is also a raw byte string
            d1 = self.sign(data)

            # asyncio lacks callback chaining (and we cannot use co-routines, since we want
            # to support older Pythons), hence we need d2
            d2 = txaio.create_future()

            def process(signature_raw):
                # convert the raw signature into a hex encode value (unicode string)
                signature_hex = binascii.b2a_hex(signature_raw).decode('ascii')

                # we return the concatenation of the signature and the message signed (96 bytes)
                data_hex = binascii.b2a_hex(data).decode('ascii')

                sig = signature_hex + data_hex
                txaio.resolve(d2, sig)

            txaio.add_callbacks(d1, process, None)

            return d2
 def xor(bin1, bin2):
     return util.xor(bin1, bin2)