def test_nfkc_rfcx(self): self.assertEqual( "a", saslprep("\u00AA"), "SASLprep requirement: NFKC") self.assertEqual( "IX", saslprep("\u2168"), "SASLprep requirement: NFKC")
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))
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")
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))
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
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
def test_case_preservation_rfcx(self): self.assertEqual("USER", saslprep("USER"), "SASLprep requirement: preserve case")
def test_case_preservation_rfcx(self): self.assertEqual( "USER", saslprep("USER"), "SASLprep requirement: preserve case")
def test_identity_rfcx(self): self.assertEqual( "user", saslprep("user"), "SASLprep requirement: identity transform")
def test_map_to_space(self): self.assertEqual( "I X", saslprep("I\u00A0X"), "SASLprep requirement: map SOFT HYPHEN to nothing")
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
def test_identity_rfcx(self): self.assertEqual("user", saslprep("user"), "SASLprep requirement: identity transform")
def test_bidirectional_check_rfcx(self): with self.assertRaises( ValueError, msg="SASLprep requirement: bidirectional check"): saslprep("\u0627\u0031")
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
def test_nfkc_rfcx(self): self.assertEqual("a", saslprep("\u00AA"), "SASLprep requirement: NFKC") self.assertEqual("IX", saslprep("\u2168"), "SASLprep requirement: NFKC")
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
def test_map_to_space(self): self.assertEqual("I X", saslprep("I\u00A0X"), "SASLprep requirement: map SOFT HYPHEN to nothing")