Welcome to aiosasl’s documentation!

aiosasl is a generic SASL implementation for use with asyncio protocols. It makes very few assumptions about the protocol which uses SASL, making it usable in different contexts. The assumptions are:

  • It uses SASL, i.e. you can perform SASL initiation, responses and abortions.
  • Those actions can be encapsulated in asyncio coroutines which return the server response.

API Reference

Using SASL in a protocol

To make use of SASL over an existing protocol, you first need to subclass and implement SASLInterface.

The usable mechanisms need to be detected by your application using the protocol over which to implement SASL. This is generally protocol-specific. For example, XMPP uses stream features to announce which SASL mechanisms are supported by the server.

When a set of SASL mechanism strings has been obtained by the server (let us call a set with the mechanism strings sasl_mechanisms), the mechanisms supported by your application (a list of SASLMechanism subclass instances, let us call it mechanism_impls) can be queried for support:

# intf = <instance of your subclass of SASLInterface>
for impl in mechanism_impl:
    token = impl.any_supported(sasl_mechanisms)
    if token is not None:
        sm = aiosasl.SASLStateMachine(intf)
        try:
            await impl.authenticate(sm, token)
        except aiosasl.AuthenticationFailure:
            # handle authentication failure
            # it is generally not sensible to re-try with other mechanisms
        except aiosasl.SASLFailure:
            # this is a protocol problem, it is sensible to re-try other
            # mechanisms
        else:
            # authentication was successful!

The instances for the mechanisms can be re-used; they do not save any state, the state is held by SASLStateMachine instead. The different mechanisms require different arguments (the password-based mechanisms generally require a callback which provides credentials).

The mechanisms which are currently supported by aiosasl are summarised below:

ANONYMOUS(token) The ANONYMOUS SASL mechanism (see RFC 4505).
PLAIN(credential_provider, Coroutine[Any, …) The password-based PLAIN SASL mechanism (see RFC 4616).
SCRAM(credential_provider, Coroutine[Any, …) The password-based SCRAM (non-PLUS) SASL mechanism (see RFC 5802).
SCRAMPLUS(credential_provider, …) The password-based SCRAM-PLUS SASL mechanism (see RFC 5802).

Interface for protocols using SASL

To implement SASL on an existing protocol, you need to subclass SASLInterface and implement the abstract methods:

class aiosasl.SASLInterface[source]

This class serves as an abstract base class for interfaces for use with SASLStateMachine. Specific protocols using SASL (such as XMPP, IMAP or SMTP) can subclass this interface to implement SASL on top of the existing protocol.

The interface class does not need to implement any state checking. State checking is done by the SASLStateMachine. The following interface must be implemented by subclasses.

The return values of the methods below are tuples of the following form:

  • (SASLState.SUCCESS, payload) – After successful authentication, success is returned. Depending on the mechanism, a payload (as bytes object) may be attached to the result, otherwise, payload is None.
  • (SASLState.CHALLENGE, payload) – A challenge was sent by the server in reply to the previous command.
  • (SASLState.FAILURE, None) – This is only ever returned by abort(). All other methods must raise errors as SASLFailure.

Changed in version 0.4: The first element of the returned tuples are now elements of SASLState. For compatibility with previous versions of aiosasl the first elements of the string may be one of the strings "success", "failure" or “challenge”. For more information see SASLState.from_reply().

initiate(mechanism: str, payload: Optional[bytes] = None) → Tuple[aiosasl.common.SASLState, Optional[bytes]][source]

Send a SASL initiation request for the given mechanism. Depending on the mechanism, an initial payload may be given. The payload is then a bytes object which needs to be passed as initial payload during the initiation request.

Wait for a reply by the peer and return the reply as a next-state tuple in the format documented at SASLInterface.

respond(payload: bytes) → Tuple[aiosasl.common.SASLState, Optional[bytes]][source]

Send a response to a challenge. The payload is a bytes object which is to be sent as response.

Wait for a reply by the peer and return the reply as a next-state tuple in the format documented at SASLInterface.

abort() → None[source]

Abort the authentication. The result is either the failure tuple ((SASLState.FAILURE, None)) or a SASLFailure exception if the response from the peer did not indicate abortion (e.g. another error was returned by the peer or the peer indicated success).

class aiosasl.SASLState[source]

The states of the SASL state machine.

CHALLENGE

the server sent a SASL challenge

SUCCESS

the authentication was successful

FAILURE

the authentication failed

Internal states used by the state machine:

INITIAL

the state of the state machine before the authentication is started

SUCCESS_SIMULATE_CHALLENGE

used to unwrap success replies that carry final data

These internal states must not be returned by the SASLInterface methods as first component of the result tuple.

The following method is used to process replies returned by the SASLInterface methods:

from_reply()[source]

SASL mechansims

class aiosasl.PLAIN(credential_provider: Callable[[], Coroutine[Any, Any, Tuple[str, str]]])[source]

The password-based PLAIN SASL mechanism (see RFC 4616).

Warning

This is generally unsafe over unencrypted connections and should not be used there. Exclusion of the PLAIN mechanism over unsafe connections is out of scope for aiosasl and needs to be handled by the protocol implementation!

credential_provider must be coroutine which returns a (user, password) tuple.

class aiosasl.SCRAM(credential_provider, *[, after_scram_plus=False][, enforce_minimum_iteration_count=True])[source]

The password-based SCRAM (non-PLUS) SASL mechanism (see RFC 5802).

Parameters:
  • credential_provider – A coroutine function which returns credentials.
  • after_scram_plus (bool) – Flag to indicate that SCRAM-PLUS is supported by your implementation.
  • enforce_minimum_iteration_count (bool) – Enforce the minimum iteration count specified by the SCRAM specifications.

Note

As “non-PLUS” suggests, this does not support channel binding. Use SCRAMPLUS if you want channel binding.

credential_provider must be coroutine function which returns a (user, password) tuple.

If this is used after SCRAMPLUS in a method list, the keyword argument after_scram_plus should be set to True. Then we will use the gs2 header y,, to prevent down-grade attacks by a man-in-the-middle attacker.

enforce_minimum_iteration_count controls the enforcement of the specified minimum iteration count for the key derivation function used in SCRAM. By default, this enforcement is enabled, and you are strongly advised to not disable it: it can be used to make the exchange weaker.

Disabling enforce_minimum_iteration_count only makes sense if the authentication exchange would otherwise fall back to using PLAIN or a similarly weak authentication mechanism.

Changed in version 0.4: The enforce_minimum_iteration_count argument and the behaviour to enforce the minimum iteration count by default was added.

class aiosasl.SCRAMPLUS(credential_provider, cb_provider, *[, enforce_minimum_iteration_count=True])[source]

The password-based SCRAM-PLUS SASL mechanism (see RFC 5802).

Parameters:
  • credential_provider – A coroutine function which returns credentials.
  • cb_provider (ChannelBindingProvider) – Object which provides channel binding data and information.
  • after_scram_plus (bool) – Flag to indicate that SCRAM-PLUS is supported by your implementation.
  • enforce_minimum_iteration_count (bool) – Enforce the minimum iteration count specified by the SCRAM specifications.

credential_provider must be coroutine which returns a (user, password) tuple.

cb_provider must be an instance of ChannelBindingProvider, which specifies and implements the channel binding type to use.

enforce_minimum_iteration_count controls the enforcement of the specified minimum iteration count for the key derivation function used in SCRAM. By default, this enforcement is enabled, and you are strongly advised to not disable it: it can be used to make the exchange weaker.

See also

SCRAM for more information on enforce_minimum_iteration_count.

Changed in version 0.4: The enforce_minimum_iteration_count argument and the behaviour to enforce the minimum iteration count by default was added.

class aiosasl.ANONYMOUS(token: str)[source]

The ANONYMOUS SASL mechanism (see RFC 4505).

New in version 0.3.

Base class

class aiosasl.SASLMechanism[source]

Implementation of a SASL mechanism. Two methods must be implemented by subclasses:

classmethod any_supported(mechanisms: Iterable[str]) → Any[source]

Determine whether this class can perform any SASL mechanism in the set of strings mechanisms.

If the class cannot perform any of the SASL mechanisms in mechanisms, it must return None.

Otherwise, it must return a non-None value. Applications must not assign any meaning to any value (except that None is a sure indicator that the class cannot perform any of the listed mechanisms) and must not alter any value returned by this function. Note that even False indicates success!

The return value must be passed as second argument to authenticate(). authenticate() must not be called with a None value.

authenticate(sm: aiosasl.statemachine.SASLStateMachine, token: Any) → None[source]

Execute the mechanism identified by token (the non-None value which has been returned by any_supported() before) using the given SASLStateMachine sm.

If authentication fails, an appropriate exception is raised (AuthenticationFailure). If the authentication fails for a reason unrelated to credentials, SASLFailure is raised.

Note

Administrative note

Patches for new SASL mechanisms are welcome!

A note for implementers

The SASLStateMachine unwraps (SASLState.SUCCESS, payload) messages passed in from a SASLInterface to the equivalent sequence (SASLState.CHALLENGE, payload) (requiring the empty string as response) and (SASLState.SUCCESS, None). The two forms are equivalent as per the SASL specification and this unwrapping allows uniform treatment of both forms by the SASLMechanism implementations.

SASL state machine

class aiosasl.SASLStateMachine(interface: aiosasl.statemachine.SASLInterface)[source]

A state machine to reduce code duplication during SASL handshake.

The state methods change the state and return the next client state of the SASL handshake, optionally with server-supplied payload.

Note that, with the notable exception of abort(), failure states are never returned but thrown as SASLFailure instead.

The initial state is never returned.

Exception classes

class aiosasl.SASLError(opaque_error: Any, kind: str, text: Optional[str] = None)[source]

Base class for a SASL related error. opaque_error may be anything but None which helps your application re-identify the error at the outer layers. kind is a string which helps identifying the class of the error; this is set implicitly by the constructors of SASLFailure and AuthenticationFailure, which you are encouraged to use.

text may be a human-readable string describing the error condition in more detail.

opaque_error is set to None by SASLMechanism implementations to indicate errors which originate from the local mechanism implementation.

opaque_error

The value passed to the respective constructor argument.

text

The value passed to the respective constructor argument.

class aiosasl.SASLFailure(opaque_error: Any, text: Optional[str] = None)[source]

A SASL protocol failure which is unrelated to the credentials passed. This may be raised by SASLInterface methods.

class aiosasl.AuthenticationFailure(opaque_error: Any, text: Optional[str] = None)[source]

A SASL error which indicates that the provided credentials are invalid. This may be raised by SASLInterface methods.

Version information

aiosasl.__version__ = '0.4.0-a0'

The imported aiosasl version as a string.

The version number is dot-separated; in pre-release or development versions, the version number is followed by a hypen-separated pre-release identifier.

aiosasl.version_info = (0, 4, 0, 'a0')

The imported aiosasl version as a tuple.

The components of the tuple are, in order: major version, minor version, patch level, and pre-release identifier.

Channel binding methods

The module aiosasl.channel_binding provides implementations of the ChannelBindingProvider interface for use with ssl respective OpenSSL.

class aiosasl.channel_binding.ChannelBindingProvider[source]

Interface for a channel binding method.

The needed external information is supplied to the constructors of the specific instances.

class aiosasl.channel_binding.StdlibTLS(connection: ssl.SSLSocket, type_: str)[source]

Provider for channel binding for ssl.

Parameters:
  • connection (ssl.SSLSocket) – the SSL connection
  • type (str) – the channel binding type
class aiosasl.channel_binding.TLSUnique(connection: OpenSSL.SSL.Connection)[source]

Provider for the channel binding tls-unique as specified by RFC 5929 for OpenSSL.

Warning

This only supports connections that were not created by session resumption.

Parameters:connection (OpenSSL.SSL.Connection) – the SSL connection
class aiosasl.channel_binding.TLSServerEndPoint(connection: OpenSSL.SSL.Connection)[source]

Provider for the channel binding tls-server-end-point as specified by RFC 5929 for OpenSSL.

Parameters:connection (OpenSSL.SSL.Connection) – the SSL connection

Indices and tables