Esempio n. 1
0
 def test_nfkc_rfcx(self):
     self.assertEqual(
         "a",
         saslprep("\u00AA"),
         "SASLprep requirement: NFKC")
     self.assertEqual(
         "IX",
         saslprep("\u2168"),
         "SASLprep requirement: NFKC")
Esempio n. 2
0
    def test_unassigned(self):
        with self.assertRaises(ValueError,
                               msg="SASLprep requirement: unassigned"):
            saslprep("\u0221", allow_unassigned=False)

        with self.assertRaises(ValueError,
                               msg="enforce no unassigned by default"):
            saslprep("\u0221")

        self.assertEqual("\u0221", saslprep("\u0221", allow_unassigned=True))
Esempio n. 3
0
    def test_prohibited_character_rfcx(self):
        with self.assertRaises(
                ValueError,
                msg="SASLprep requirement: prohibited character (C.2.1)"):
            saslprep("\u0007")

        with self.assertRaises(
                ValueError,
                msg="SASLprep requirement: prohibited character (C.8)"):
            saslprep("\u200E")
Esempio n. 4
0
    def test_prohibited_character_rfcx(self):
        with self.assertRaises(
                ValueError,
                msg="SASLprep requirement: prohibited character (C.2.1)"):
            saslprep("\u0007")

        with self.assertRaises(
                ValueError,
                msg="SASLprep requirement: prohibited character (C.8)"):
            saslprep("\u200E")
Esempio n. 5
0
    def test_unassigned(self):
        with self.assertRaises(
                ValueError,
                msg="SASLprep requirement: unassigned"):
            saslprep("\u0221", allow_unassigned=False)

        with self.assertRaises(
                ValueError,
                msg="enforce no unassigned by default"):
            saslprep("\u0221")

        self.assertEqual(
            "\u0221",
            saslprep("\u0221", allow_unassigned=True))
Esempio n. 6
0
    def authenticate(self, sm, mechanism):
        logger.info("attempting PLAIN mechanism")
        username, password = yield from self._credential_provider()
        username = saslprep(username).encode("utf8")
        password = saslprep(password).encode("utf8")

        state, _ = yield from sm.initiate(mechanism="PLAIN",
                                          payload=b"\0" + username + b"\0" +
                                          password)

        if state != "success":
            raise SASLFailure(None, text="SASL protocol violation")

        return True
Esempio n. 7
0
    def authenticate(self, sm, mechanism):
        logger.info("attempting PLAIN mechanism")
        username, password = yield from self._credential_provider()
        username = saslprep(username).encode("utf8")
        password = saslprep(password).encode("utf8")

        state, _ = yield from sm.initiate(
            mechanism="PLAIN",
            payload=b"\0" + username + b"\0" + password)

        if state != "success":
            raise SASLFailure(
                None,
                text="SASL protocol violation")

        return True
Esempio n. 8
0
 def test_case_preservation_rfcx(self):
     self.assertEqual("USER", saslprep("USER"),
                      "SASLprep requirement: preserve case")
Esempio n. 9
0
 def test_case_preservation_rfcx(self):
     self.assertEqual(
         "USER",
         saslprep("USER"),
         "SASLprep requirement: preserve case")
Esempio n. 10
0
 def test_identity_rfcx(self):
     self.assertEqual(
         "user",
         saslprep("user"),
         "SASLprep requirement: identity transform")
Esempio n. 11
0
 def test_map_to_space(self):
     self.assertEqual(
         "I X",
         saslprep("I\u00A0X"),
         "SASLprep requirement: map SOFT HYPHEN to nothing")
Esempio n. 12
0
    def authenticate(self, sm, token):
        mechanism, hashfun_name, = token
        logger.info("attempting %s mechanism (using %s hashfun)", mechanism,
                    hashfun_name)
        # this is pretty much a verbatim implementation of RFC 5802.

        hashfun_factory = functools.partial(hashlib.new, hashfun_name)
        digest_size = hashfun_factory().digest_size

        # we don’t support channel binding
        gs2_header = b"n,,"
        username, password = yield from self._credential_provider()
        username = saslprep(username).encode("utf8")
        password = saslprep(password).encode("utf8")

        our_nonce = base64.b64encode(
            _system_random.getrandbits(self.nonce_length * 8).to_bytes(
                self.nonce_length, "little"))

        auth_message = b"n=" + username + b",r=" + our_nonce
        _, payload = yield from sm.initiate(mechanism,
                                            gs2_header + auth_message)

        auth_message += b"," + payload

        payload = dict(self.parse_message(payload))

        try:
            iteration_count = int(payload[b"i"])
            nonce = payload[b"r"]
            salt = base64.b64decode(payload[b"s"])
        except (ValueError, KeyError):
            yield from sm.abort()
            raise SASLFailure(
                None, text="malformed server message: {!r}".format(payload))

        if not nonce.startswith(our_nonce):
            yield from sm.abort()
            raise SASLFailure(None, text="server nonce doesn't fit our nonce")

        t0 = time.time()

        salted_password = pbkdf2(hashfun_name, password, salt, iteration_count)

        logger.debug("pbkdf2 timing: %f seconds", time.time() - t0)

        client_key = hmac.new(salted_password, b"Client Key",
                              hashfun_factory).digest()

        stored_key = hashfun_factory(client_key).digest()

        reply = b"c=" + base64.b64encode(b"n,,") + b",r=" + nonce

        auth_message += b"," + reply

        client_proof = (int.from_bytes(
            hmac.new(stored_key, auth_message, hashfun_factory).digest(),
            "big") ^ int.from_bytes(client_key, "big")).to_bytes(
                digest_size, "big")

        logger.debug("response generation time: %f seconds", time.time() - t0)
        try:
            state, payload = yield from sm.response(
                reply + b",p=" + base64.b64encode(client_proof))
        except SASLFailure as err:
            raise err.promote_to_authentication_failure() from None

        if state != "success":
            raise SASLFailure("malformed-request",
                              text="SCRAM protocol violation")

        server_signature = hmac.new(
            hmac.new(salted_password, b"Server Key", hashfun_factory).digest(),
            auth_message, hashfun_factory).digest()

        payload = dict(self.parse_message(payload))

        if base64.b64decode(payload[b"v"]) != server_signature:
            raise SASLFailure(
                None,
                "authentication successful, but server signature invalid")

        return True
Esempio n. 13
0
 def test_identity_rfcx(self):
     self.assertEqual("user", saslprep("user"),
                      "SASLprep requirement: identity transform")
Esempio n. 14
0
 def test_bidirectional_check_rfcx(self):
     with self.assertRaises(
             ValueError,
             msg="SASLprep requirement: bidirectional check"):
         saslprep("\u0627\u0031")
Esempio n. 15
0
    def authenticate(self, sm, token):
        mechanism, info, = token
        logger.info("attempting %s mechanism (using %s hashfun)",
                    mechanism,
                    info)
        # this is pretty much a verbatim implementation of RFC 5802.

        hashfun_factory = functools.partial(hashlib.new, info.hashfun_name)

        gs2_header = self._get_gs2_header()
        username, password = yield from self._credential_provider()
        username = saslprep(username).encode("utf8")
        password = saslprep(password).encode("utf8")

        our_nonce = base64.b64encode(_system_random.getrandbits(
            self.nonce_length * 8
        ).to_bytes(
            self.nonce_length, "little"
        ))

        auth_message = b"n=" + username + b",r=" + our_nonce
        state, payload = yield from sm.initiate(
            mechanism,
            gs2_header + auth_message)

        if state != SASLState.CHALLENGE or payload is None:
            yield from sm.abort()
            raise SASLFailure(
                None,
                text="protocol violation: expected challenge with payload")

        auth_message += b"," + payload

        payload = dict(self.parse_message(payload))

        try:
            iteration_count = int(payload[b"i"])
            nonce = payload[b"r"]
            salt = base64.b64decode(payload[b"s"])
        except (ValueError, KeyError):
            yield from sm.abort()
            raise SASLFailure(
                None,
                text="malformed server message: {!r}".format(payload))

        if not nonce.startswith(our_nonce):
            yield from sm.abort()
            raise SASLFailure(
                None,
                text="server nonce doesn't fit our nonce")

        if (self.enforce_minimum_iteration_count and
                iteration_count < info.minimum_iteration_count):
            raise SASLFailure(
                None,
                text="minimum iteration count for {} violated "
                "({} is less than {})".format(
                    mechanism,
                    iteration_count,
                    info.minimum_iteration_count,
                )
            )

        t0 = time.time()

        salted_password = pbkdf2(
            info.hashfun_name,
            password,
            salt,
            iteration_count)

        logger.debug("pbkdf2 timing: %f seconds", time.time() - t0)

        client_key = hmac.new(
            salted_password,
            b"Client Key",
            hashfun_factory).digest()

        stored_key = hashfun_factory(client_key).digest()

        reply = b"c=" + base64.b64encode(self._get_cb_data()) + b",r=" + nonce

        auth_message += b"," + reply

        client_proof = xor_bytes(
            hmac.new(
                stored_key,
                auth_message,
                hashfun_factory).digest(),
            client_key)

        logger.debug("response generation time: %f seconds", time.time() - t0)
        try:
            state, payload = yield from sm.response(
                reply + b",p=" + base64.b64encode(client_proof)
            )
        except SASLFailure as err:
            raise err.promote_to_authentication_failure() from None

        # this is the pseudo-challenge for the server signature
        # we have to reply with the empty string!
        if state != SASLState.CHALLENGE:
            raise SASLFailure(
                "malformed-request",
                text="SCRAM protocol violation")

        state, dummy_payload = yield from sm.response(b"")
        if state != SASLState.SUCCESS or dummy_payload is not None:
            raise SASLFailure(
                None,
                "SASL protocol violation")

        server_signature = hmac.new(
            hmac.new(
                salted_password,
                b"Server Key",
                hashfun_factory).digest(),
            auth_message,
            hashfun_factory).digest()

        payload = dict(self.parse_message(payload))

        if base64.b64decode(payload[b"v"]) != server_signature:
            raise SASLFailure(
                None,
                "authentication successful, but server signature invalid")

        return True
Esempio n. 16
0
 def test_nfkc_rfcx(self):
     self.assertEqual("a", saslprep("\u00AA"), "SASLprep requirement: NFKC")
     self.assertEqual("IX", saslprep("\u2168"),
                      "SASLprep requirement: NFKC")
Esempio n. 17
0
 def test_bidirectional_check_rfcx(self):
     with self.assertRaises(
             ValueError, msg="SASLprep requirement: bidirectional check"):
         saslprep("\u0627\u0031")
Esempio n. 18
0
    def authenticate(self, sm, token):
        mechanism, hashfun_name, = token
        logger.info("attempting %s mechanism (using %s hashfun)",
                    mechanism,
                    hashfun_name)
        # this is pretty much a verbatim implementation of RFC 5802.

        hashfun_factory = functools.partial(hashlib.new, hashfun_name)
        digest_size = hashfun_factory().digest_size

        # we don’t support channel binding
        gs2_header = b"n,,"
        username, password = yield from self._credential_provider()
        username = saslprep(username).encode("utf8")
        password = saslprep(password).encode("utf8")

        our_nonce = base64.b64encode(_system_random.getrandbits(
            self.nonce_length * 8
        ).to_bytes(
            self.nonce_length, "little"
        ))

        auth_message = b"n=" + username + b",r=" + our_nonce
        _, payload = yield from sm.initiate(
            mechanism,
            gs2_header + auth_message)

        auth_message += b"," + payload

        payload = dict(self.parse_message(payload))

        try:
            iteration_count = int(payload[b"i"])
            nonce = payload[b"r"]
            salt = base64.b64decode(payload[b"s"])
        except (ValueError, KeyError):
            yield from sm.abort()
            raise SASLFailure(
                None,
                text="malformed server message: {!r}".format(payload))

        if not nonce.startswith(our_nonce):
            yield from sm.abort()
            raise SASLFailure(
                None,
                text="server nonce doesn't fit our nonce")

        t0 = time.time()

        salted_password = pbkdf2(
            hashfun_name,
            password,
            salt,
            iteration_count)

        logger.debug("pbkdf2 timing: %f seconds", time.time() - t0)

        client_key = hmac.new(
            salted_password,
            b"Client Key",
            hashfun_factory).digest()

        stored_key = hashfun_factory(client_key).digest()

        reply = b"c=" + base64.b64encode(b"n,,") + b",r=" + nonce

        auth_message += b"," + reply

        client_proof = (
            int.from_bytes(
                hmac.new(
                    stored_key,
                    auth_message,
                    hashfun_factory).digest(),
                "big") ^
            int.from_bytes(client_key, "big")).to_bytes(digest_size, "big")

        logger.debug("response generation time: %f seconds", time.time() - t0)
        try:
            state, payload = yield from sm.response(
                reply + b",p=" + base64.b64encode(client_proof)
            )
        except SASLFailure as err:
            raise err.promote_to_authentication_failure() from None

        if state != "success":
            raise SASLFailure(
                "malformed-request",
                text="SCRAM protocol violation")

        server_signature = hmac.new(
            hmac.new(
                salted_password,
                b"Server Key",
                hashfun_factory).digest(),
            auth_message,
            hashfun_factory).digest()

        payload = dict(self.parse_message(payload))

        if base64.b64decode(payload[b"v"]) != server_signature:
            raise SASLFailure(
                None,
                "authentication successful, but server signature invalid")

        return True
Esempio n. 19
0
 def test_map_to_space(self):
     self.assertEqual("I X", saslprep("I\u00A0X"),
                      "SASLprep requirement: map SOFT HYPHEN to nothing")