""" Authentication related methods. """ import base64 import hmac from typing import Tuple, Union __all__ = ("auth_crammd5_verify", "auth_plain_encode", "auth_login_encode") def _ensure_bytes(value: Union[str, bytes]) -> bytes: if isinstance(value, bytes): return value return value.encode("utf-8") def auth_crammd5_verify( username: Union[str, bytes], password: Union[str, bytes], challenge: Union[str, bytes], /, ) -> bytes: """ CRAM-MD5 auth uses the password as a shared secret to MD5 the server's response, and sends the username combined with that (base64 encoded). """ username_bytes = _ensure_bytes(username) password_bytes = _ensure_bytes(password) decoded_challenge = base64.b64decode(challenge) md5_digest = hmac.new(password_bytes, msg=decoded_challenge, digestmod="md5") verification = username_bytes + b" " + md5_digest.hexdigest().encode("ascii") encoded_verification = base64.b64encode(verification) return encoded_verification def auth_plain_encode( username: Union[str, bytes], password: Union[str, bytes], /, ) -> bytes: """ PLAIN auth base64 encodes the username and password together. """ username_bytes = _ensure_bytes(username) password_bytes = _ensure_bytes(password) username_and_password = b"\0" + username_bytes + b"\0" + password_bytes encoded = base64.b64encode(username_and_password) return encoded def auth_login_encode( username: Union[str, bytes], password: Union[str, bytes], /, ) -> Tuple[bytes, bytes]: """ LOGIN auth base64 encodes the username and password and sends them in sequence. """ username_bytes = _ensure_bytes(username) password_bytes = _ensure_bytes(password) encoded_username = base64.b64encode(username_bytes) encoded_password = base64.b64encode(password_bytes) return encoded_username, encoded_password