def test_fail_on_protocol_violation(self): user = "******" password = "******" smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;PLAIN", b"\0tim\0tanstaaftanstaaf", "challenge", b"foo") ])) @asyncio.coroutine def provide_credentials(*args): return user, password def run(): plain = aiosasl.PLAIN(provide_credentials) yield from plain.authenticate( smmock, "PLAIN") with self.assertRaisesRegexp(aiosasl.SASLFailure, "protocol violation") as ctx: asyncio.get_event_loop().run_until_complete(run()) self.assertEqual( None, ctx.exception.opaque_error ) smmock.interface.finalize()
def test_passes_token_through_trace(self): with unittest.mock.patch("aiosasl.trace") as trace: trace.return_value = "traced" anon = aiosasl.ANONYMOUS(unittest.mock.sentinel.token) trace.assert_called_with(unittest.mock.sentinel.token) smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;ANONYMOUS", b"traced", "success", None) ])) def run(): result = yield from anon.authenticate( smmock, "ANONYMOUS") self.assertTrue(result) asyncio.get_event_loop().run_until_complete(run()) smmock.interface.finalize()
def test_rfc(self): user = "******" password = "******" smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;PLAIN", b"\0tim\0tanstaaftanstaaf", "success", None) ])) @asyncio.coroutine def provide_credentials(*args): return user, password def run(): plain = aiosasl.PLAIN(provide_credentials) result = yield from plain.authenticate( smmock, "PLAIN") self.assertTrue(result) asyncio.get_event_loop().run_until_complete(run()) smmock.interface.finalize()
def test_malformed_reply(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1-PLUS", b"p=tls-unique,,"+self.client_first_message_bare, "challenge", b"s=hut,t=hefu,c=kup,d=onny"), ("abort", None, "failure", ("aborted", None)) ])) with self.assertRaises(aiosasl.SASLFailure) as ctx: self._run( smmock, aiosasl.SCRAMPLUS( self._provide_credentials, TLSUnique(self._tls_connection) ) ) self.assertIn( "malformed", str(ctx.exception).lower() )
def test_reject_protocol_violation(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1-PLUS", b"p=tls-unique,,"+self.client_first_message_bare, "challenge", self.server_first_message), ("response", self.client_final_message_without_proof + b",p="+base64.b64encode(self.client_proof), "challenge", b"foo"), ("response", b"", "success", b"bar") ])) with self.assertRaisesRegexp(aiosasl.SASLFailure, "protocol violation") as ctx: self._run( smmock, aiosasl.SCRAMPLUS( self._provide_credentials, TLSUnique(self._tls_connection) ) ) self.assertEqual( None, ctx.exception.opaque_error )
def test_promote_failure_to_authentication_failure(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1-PLUS", b"p=tls-unique,,"+self.client_first_message_bare, "challenge", self.server_first_message), ("response", self.client_final_message_without_proof + b",p="+base64.b64encode(self.client_proof), "failure", ("credentials-expired", None)) ])) with self.assertRaises(aiosasl.AuthenticationFailure) as ctx: self._run( smmock, aiosasl.SCRAMPLUS( self._provide_credentials, TLSUnique(self._tls_connection) ) ) self.assertEqual( "credentials-expired", ctx.exception.opaque_error )
def test_invalid_signature(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1-PLUS", b"p=tls-unique,,"+self.client_first_message_bare, "challenge", self.server_first_message), ("response", self.client_final_message_without_proof + b",p="+base64.b64encode(self.client_proof), "success", b"v="+base64.b64encode(b"fnord")) ])) with self.assertRaises(aiosasl.SASLFailure) as ctx: self._run( smmock, aiosasl.SCRAMPLUS( self._provide_credentials, TLSUnique(self._tls_connection) ) ) self.assertIsNone(ctx.exception.opaque_error) self.assertIn( "signature", str(ctx.exception).lower() )
def test_other_malformed_reply(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1-PLUS", b"p=tls-unique,,"+self.client_first_message_bare, "challenge", b"i=sometext,s=ABC,r=Zm9vAAAAAAAAAAAAAAAA3rfcNHYJY1ZVvWVs7j"), ("abort", None, "failure", ("aborted", None)) ])) with self.assertRaises(aiosasl.SASLFailure) as ctx: self._run( smmock, aiosasl.SCRAMPLUS( self._provide_credentials, TLSUnique(self._tls_connection) ) ) self.assertIn( "malformed", str(ctx.exception).lower() )
def _execute(self, intf, mechanism, token): """ Execute a SASL authentication process. :param intf: SASL interface to use :type intf: :class:`~.sasl.SASLXMPPInterface` :param mechanism: SASL mechanism to use :type mechanism: :class:`aiosasl.SASLMechanism` :param token: The opaque token argument for the mechanism :type token: not :data:`None` :raises aiosasl.AuthenticationFailure: if authentication failed due to bad credentials :raises aiosasl.SASLFailure: on other SASL error conditions (such as protocol violations) :return: true if authentication succeeded, false if the mechanism has to be disabled :rtype: :class:`bool` This executes the SASL authentication process. The more specific exceptions are generated by inspecting the :attr:`aiosasl.SASLFailure.opaque_error` on exceptinos raised from the :class:`~.sasl.SASLXMPPInterface`. Other :class:`aiosasl.SASLFailure` exceptions are re-raised without modification. """ sm = aiosasl.SASLStateMachine(intf) try: yield from mechanism.authenticate(sm, token) return True except aiosasl.SASLFailure as err: if err.opaque_error in self.AUTHENTICATION_FAILURES: raise aiosasl.AuthenticationFailure( opaque_error=err.opaque_error, text=err.text) elif err.opaque_error in self.MECHANISM_REJECTED_FAILURES: return False raise
def setUp(self): self.loop = asyncio.get_event_loop() self.intf = unittest.mock.Mock() self.intf.initiate = CoroutineMock() self.intf.respond = CoroutineMock() self.intf.abort = CoroutineMock() self.sm = aiosasl.SASLStateMachine(self.intf) self.intf.initiate.return_value = (aiosasl.SASLState.SUCCESS, None)
def test_reject_NUL_bytes_in_password(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ])) @asyncio.coroutine def provide_credentials(*args): return "foo", "\0" with self.assertRaises(ValueError): run_coroutine( aiosasl.PLAIN(provide_credentials).authenticate(smmock, "PLAIN") )
def test_incorrect_nonce(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1", b"n,,"+self.client_first_message_bare, "challenge", b"r=foobar,s="+base64.b64encode(self.salt)+b",i=4096"), ("abort", None, "failure", ("aborted", None)) ])) with self.assertRaisesRegexp(aiosasl.SASLFailure, "nonce") as ctx: self._run(smmock, aiosasl.SCRAM(self._provide_credentials)) self.assertIsNone(ctx.exception.opaque_error)
def _execute(self, intf, mechanism, token): """ Execute SASL negotiation using the given `mechanism` instance and `token` using the :class:`~.sasl.SASLXMPPInterface` `intf`. """ sm = aiosasl.SASLStateMachine(intf) try: yield from mechanism.authenticate(sm, token) return True except aiosasl.SASLFailure as err: if err.opaque_error in self.AUTHENTICATION_FAILURES: raise aiosasl.AuthenticationFailure( opaque_error=err.opaque_error, text=err.text) elif err.opaque_error in self.MECHANISM_REJECTED_FAILURES: return False raise
def test_rfc_with_downgrade_protection(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1", b"y,,"+self.client_first_message_bare, "challenge", self.server_first_message), ("response", self.client_final_message_without_proof + b",p="+base64.b64encode(self.client_proof), "success", b"v="+base64.b64encode(self.server_signature)) ])) self.assertTrue(self._run( smmock, aiosasl.SCRAM(self._provide_credentials, after_scram_plus=True) ))
def test_high_iteration_count(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1", b"n,,"+self.client_first_message_bare, "challenge", self.server_first_message_5000), ("response", self.client_final_message_without_proof + b",p="+base64.b64encode(self.client_proof_5000), "success", b"v="+base64.b64encode(self.server_signature_5000)) ])) self.assertTrue(self._run( smmock, aiosasl.SCRAM(self._provide_credentials) ))
def test_reject_protocol_violation_2(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1", b"n,,"+self.client_first_message_bare, "success", None), ("abort", None, "failure", ("aborted", None)), ])) with self.assertRaisesRegexp(aiosasl.SASLFailure, "protocol violation") as ctx: self._run(smmock, aiosasl.SCRAM(self._provide_credentials)) self.assertEqual( None, ctx.exception.opaque_error )
def test_simply_sends_token(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;ANONYMOUS", b"sirhc", "success", None) ])) def run(): anon = aiosasl.ANONYMOUS("sirhc") result = yield from anon.authenticate( smmock, "ANONYMOUS") self.assertTrue(result) asyncio.get_event_loop().run_until_complete(run()) smmock.interface.finalize()
def test_rfc(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1-PLUS", b"p=tls-unique,,"+self.client_first_message_bare, "challenge", self.server_first_message), ("response", self.client_final_message_without_proof + b",p="+base64.b64encode(self.client_proof), "success", b"v="+base64.b64encode(self.server_signature)) ])) self.assertTrue(self._run( smmock, aiosasl.SCRAMPLUS( self._provide_credentials, TLSUnique(self._tls_connection) ) ))
def test_too_low_iteration_count(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1", b"n,,"+self.client_first_message_bare, "challenge", self.server_first_message.replace(b",i=4096", b",i=4095")), ("abort", None, "failure", ("aborted", None)), ])) with self.assertRaisesRegexp( aiosasl.SASLFailure, r"minimum iteration count for SCRAM-SHA-1 violated " r"\(4095 is less than 4096\)") as ctx: self._run(smmock, aiosasl.SCRAM(self._provide_credentials)) self.assertEqual( None, ctx.exception.opaque_error )
def test_reject_protocol_violation_1(self): smmock = aiosasl.SASLStateMachine(SASLInterfaceMock( self, [ ("auth;SCRAM-SHA-1", b"n,,"+self.client_first_message_bare, "challenge", self.server_first_message), ("response", self.client_final_message_without_proof + b",p="+base64.b64encode(self.client_proof), "success", None), ])) with self.assertRaisesRegexp(aiosasl.SASLFailure, "protocol violation") as ctx: self._run(smmock, aiosasl.SCRAM(self._provide_credentials)) self.assertEqual( "malformed-request", ctx.exception.opaque_error )