Exemplo n.º 1
0
    async def login_challenge(self, request, context):
        try:
            email = request.email
            user_info = self.user_service.get_user_by_auth_source(
                email, "account")
            if not user_info:
                raise Exception(Message.AUTH_USER_NOT_FOUND)
            password_verifier = bytes.fromhex(user_info.password_verifier)
            salt = bytes.fromhex(user_info.salt)
            client_public = bytes.fromhex(request.client_public)

            srv = srp.Verifier(email, salt, password_verifier, client_public)
            s, B = srv.get_challenge()

            server_private = srv.get_ephemeral_secret().hex()
            logger.info(server_private)
            user_info.srp_server_private = server_private
            user_info.update()

            public_challenge_b = B.hex()
            auth_challenge_res = auth_messages.AuthChallengeRes(
                salt=user_info.salt, public_challenge_b=public_challenge_b)
            return auth_challenge_res

        except Exception as e:
            logger.error(e)
            if not e.args or e.args[0] not in Message.msg_dict:
                errors = [
                    Message.get_error_object(Message.AUTHENTICATION_FAILED)
                ]
            else:
                errors = [Message.get_error_object(e.args[0])]
            context.set_details(
                json.dumps(errors, default=lambda x: x.__dict__))
            context.set_code(grpc.StatusCode.INTERNAL)
Exemplo n.º 2
0
 def get_srp_verifier(self, username: str, srp_a: str) -> srp.Verifier:
     return srp.Verifier(
         username,
         self.request.app.state.srp_salt,
         self.request.app.state.srp_verifier_key,
         srp_a,
     )
Exemplo n.º 3
0
  def srp_verify(self, user_attrs, srp_params):

    """Verify phase

    1. Gets M1
    2. Computes
    3. Gives M2
    """

    srp.rfc5054_enable()

    A = b64decode(srp_params['srpA'])
    M1 = b64decode(srp_params['M1'])

    srp_salt = b64decode(user_attrs.srp_salt)
    srp_verifier = b64decode(user_attrs.srp_verifier)

    srp_instance = srp.Verifier(user_attrs.username, srp_salt, srp_verifier, A, ng_type=srp.NG_4096, hash_alg=srp.SHA256)

    M2 = srp_instance.verify_session(M1)

    if M2 is None:
      raise HTTPException(
        status_code=400,
        detail="SRP M2 parameter is not valid."
      )
    return {
      "M2": b64encode(M2).decode('utf-8')
    }
Exemplo n.º 4
0
  def srp_challenge(self, user_attrs, params):

    """Challenge phase

    1. Gets A # base64-encoded
    2. Computes
    3. Gives B
    """

    srp.rfc5054_enable()

    A = b64decode(params['srpA'])
    srp_salt = b64decode(user_attrs.srp_salt)
    srp_verifier = b64decode(user_attrs.srp_verifier)


    srp_instance = srp.Verifier(user_attrs.username, srp_salt, srp_verifier, A, ng_type=srp.NG_4096, hash_alg=srp.SHA256)
    s, B = srp_instance.get_challenge()

    if s is None or B is None:
      raise HTTPException(
        status_code=401,
        detail="Failed SRP challenge."
      )

    return {
      "s": b64encode(s).decode('utf-8'),
      "B": b64encode(B).decode('utf-8')
    }
Exemplo n.º 5
0
    async def handle_authenticate(self, dg: Datagram):
        # Credentials
        if not self.verify_credentials(dg.data):
            await self.send_error(constants.ERR_CREDENTIALS)
            return
        else:
            await self.send(
                Datagram(command=constants.CMD_AUTH, recipient=dg.data))

        # HMAC
        response = await self.recv()

        if response and response.data and \
           self.verify_HMAC(
               response.data.encode(),
               dg.data.encode(),
               self._hmac_key):
            await self.send_response(True)
        else:
            await self.send_error(constants.ERR_HMAC)
            return

        # Challenge
        response = await self.recv()

        if response and response.data:
            svr = srp.Verifier(
                dg.data.encode(),
                *srp.create_salted_verification_key(dg.data.encode(),
                                                    self._challenge_key),
                bytes.fromhex(response.data))
        else:
            await self.send_error(constants.ERR_CHALLENGE)
            return

        s, B = svr.get_challenge()
        if s and B:
            await self.send_response([s.hex(), B.hex()])
        else:
            await self.send_error(constants.ERR_CHALLENGE)
            return

        # Verification
        response = await self.recv()

        if response and response.data:
            HAMK = svr.verify_session(bytes.fromhex(response.data))
            if HAMK and svr.authenticated():
                await self.send_response(HAMK.hex())
                self.counter_cipher = svr.get_session_key()
                self.name = dg.data
            else:
                await self.send_error(constants.ERR_VERIFICATION)
                return
        else:
            await self.send_error(constants.ERR_VERIFICATION)
            return
Exemplo n.º 6
0
def get_challenge(request_id, client_seed, username, salt, vkey, ukey):
    verifier = srp.Verifier(username,
                            salt,
                            vkey,
                            client_seed,
                            hash_alg=srp.SHA256,
                            ng_type=srp.NG_2048)

    nonce = urandom(12)
    key = _get_challenge_key(verifier.M, request_id, ukey)
    token = nonce + AESGCM(key).encrypt(nonce, verifier.get_ephemeral_secret(),
                                        username)
    _, server_seed = verifier.get_challenge()
    return (server_seed, token)
Exemplo n.º 7
0
    def render_POST(self, request):
        """
        Handles POST requests.
        """
        user = get_user(request)

        if not user:
            # XXX get real error from demo provider
            return json.dumps({'errors': 'no such user'})

        A = request.args['A'][0]

        _A = safe_unhexlify(A)
        _salt = safe_unhexlify(user.salt)
        _verifier = safe_unhexlify(user.verifier)

        svr = srp.Verifier(user.login,
                           _salt,
                           _verifier,
                           _A,
                           hash_alg=srp.SHA256,
                           ng_type=srp.NG_1024)

        s, B = svr.get_challenge()

        _B = binascii.hexlify(B)

        print '[server] login = %s' % user.login
        print '[server] salt = %s' % user.salt
        print '[server] len(_salt) = %s' % len(_salt)
        print '[server] vkey = %s' % user.verifier
        print '[server] len(vkey) = %s' % len(_verifier)
        print '[server] s = %s' % binascii.hexlify(s)
        print '[server] B = %s' % _B
        print '[server] len(B) = %s' % len(_B)

        # override Request.getSession
        request.getSession = getSession.__get__(request, Request)
        session = request.getSession()

        user.set_session(session)
        user.set_server_verifier(svr)

        # yep, this is tricky.
        # some things are *already* unhexlified.
        data = {'salt': user.salt, 'B': _B, 'errors': None}

        return json.dumps(data)
Exemplo n.º 8
0
    def post(self, request):
        try:
            email = request.data['email']
            client_proof = binascii.unhexlify(request.data['clientProof'])

            validate_email(email)
            user = User.objects.get(username=email)
        except KeyError:
            return JsonResponse({'error': "BadInput"}, status=400)
        except User.DoesNotExist:
            return JsonResponse({'error': "UserNotExists"}, status=403)
        except ValidationError:
            return JsonResponse({'error': 'EmailNotValid'}, status=400)
        except binascii.Error:
            return JsonResponse({'error': 'ClientProofNotValid'}, status=400)

        svr = srp.Verifier(email,
                           user.auth_profile.salt,
                           user.auth_profile.verifier,
                           user.auth_profile.client_challenge,
                           bytes_b=user.auth_profile.server_challenge_id,
                           hash_alg=srp.SHA256)

        HAMK = svr.verify_session(client_proof)

        user.auth_profile.client_challenge = b''
        user.auth_profile.server_challenge_id = b''
        user.auth_profile.save()

        if HAMK is None:
            return JsonResponse({'error': "ClientProofIncorrect"}, status=403)
        else:
            token = Token.objects.get_or_create(user=user)[0]

            try:
                user.container_password_storage_key.delete()
            except:
                pass

            result = {}
            result['salt'] = binascii.hexlify(
                user.auth_profile.salt).decode('utf-8')
            result['serverProof'] = binascii.hexlify(HAMK).decode('utf-8')
            result['sessionId'] = str(token)
            return JsonResponse(result)
Exemplo n.º 9
0
def verify_challenge(request_id, proof, client_seed, token, username, salt,
                     vkey, ukey):
    nonce = token[0:12]
    token = token[12:]
    key = _get_challenge_key(proof, request_id, ukey)
    try:
        secret = AESGCM(key).decrypt(nonce, token, username)
    except InvalidTag:
        return None
    verifier = srp.Verifier(username,
                            salt,
                            vkey,
                            client_seed,
                            bytes_b=secret,
                            hash_alg=srp.SHA256,
                            ng_type=srp.NG_2048)
    verifier.verify_session(proof)
    return verifier.get_session_key()
Exemplo n.º 10
0
    async def mfa_validate_password(self, request, context):
        try:
            header_data = dict(context.invocation_metadata())
            introspect_token = KeyCloakUtils.introspect_token(
                header_data['access_token'])
            client_id = introspect_token['sub']
            user_info = self.service.get_user_by_id(client_id)

            client_session_key_proof = request.client_session_key_proof
            user_name = introspect_token['preferred_username']
            phone_number = user_info.phone_number
            password_verifier = bytes.fromhex(user_info.password_verifier)
            salt = bytes.fromhex(user_info.salt)
            client_session_key_proof_bytes = bytes.fromhex(
                client_session_key_proof)

            srv = srp.Verifier(username=user_name,
                               bytes_s=salt,
                               bytes_v=password_verifier,
                               bytes_A=bytes.fromhex(request.client_public),
                               bytes_b=bytes.fromhex(
                                   user_info.srp_server_private))
            srv.verify_session(client_session_key_proof_bytes)
            authenticated = srv.authenticated()

            if not authenticated:
                raise Exception(Message.AUTHENTICATION_FAILED)
            success, next_step = self.service.mfa_request_otp(
                client_id, phone_number)
            return user_messages.MfaBaseResponse(success=success,
                                                 next_step=next_step)

        except Exception as e:
            logger.error(e)
            if not e.args or e.args[0] not in Message.msg_dict:
                errors = [
                    Message.get_error_object(Message.OTP_SERVER_NOT_RESPONDING)
                ]
            else:
                errors = [Message.get_error_object(e.args[0])]
            context.set_details(
                json.dumps(errors, default=lambda x: x.__dict__))
            context.set_code(grpc.StatusCode.INTERNAL)
Exemplo n.º 11
0
def srp_login_server_simulator_mock(url, request):
    global verifier

    data = parse_qs(request.body)
    if 'login' in data:
        # SRP Authentication Step 1
        A = binascii.unhexlify(data.get('A')[0])

        verifier = srp.Verifier('username',
                                salt_bytes,
                                verification_key_bytes,
                                A,
                                hash_alg=srp.SHA256,
                                ng_type=srp.NG_1024)
        (salt, B) = verifier.get_challenge()

        content = {'salt': binascii.hexlify(salt), 'B': binascii.hexlify(B)}

        return {'status_code': 200, 'content': json.dumps(content)}

    else:
        # SRP Authentication Step 2
        data = parse_qs(request.body)
        client_auth = binascii.unhexlify(data.get('client_auth')[0])

        M2 = verifier.verify_session(client_auth)

        if not verifier.authenticated():
            return {'status_code': 404, 'content': ''}

        content = {
            'M2': binascii.hexlify(M2),
            'id': 'some id',
            'token': 'some token'
        }
        headers = {
            'Content-Type': 'application/json',
            'Set-Cookie': '_session_id=some_session_id;'
        }
        return response(200, content, headers, None, 5, request)
Exemplo n.º 12
0
    async def request_change_password(self, request, context):
        try:
            header_data = dict(context.invocation_metadata())
            introspect_token = KeyCloakUtils.introspect_token(
                header_data['access_token'])
            user_name = introspect_token['preferred_username']
            user_info = self.service.get_user_by_auth_source(
                user_name, "account")
            if not user_info:
                raise Exception(Message.AUTH_USER_NOT_FOUND)
            password_verifier = bytes.fromhex(user_info.password_verifier)
            salt = bytes.fromhex(user_info.salt)
            client_public = bytes.fromhex(request.client_public)

            srv = srp.Verifier(user_name, salt, password_verifier,
                               client_public)
            s, B = srv.get_challenge()
            # need store private b of server

            server_private = srv.get_ephemeral_secret().hex()
            user_info.srp_server_private = server_private
            user_info.update()

            public_challenge_b = B.hex()
            auth_challenge_res = user_messages.RequestChangePasswordRes(
                salt=user_info.salt, public_challenge_b=public_challenge_b)
            return auth_challenge_res

        except Exception as e:
            logger.error(e)
            if not e.args or e.args[0] not in Message.msg_dict:
                errors = [
                    Message.get_error_object(Message.CHANGE_PASSWORD_FAILED)
                ]
            else:
                errors = [Message.get_error_object(e.args[0])]
            context.set_details(
                json.dumps(errors, default=lambda x: x.__dict__))
            context.set_code(grpc.StatusCode.INTERNAL)
Exemplo n.º 13
0
    async def mfa_auth_challenge(self, request, context):
        try:
            header_data = dict(context.invocation_metadata())
            introspect_token = KeyCloakUtils.introspect_token(
                header_data['access_token'])
            client_id = introspect_token['sub']
            email = introspect_token['preferred_username']

            self.service.mfa_validate_password_flow(client_id)
            user_info = self.service.get_user_by_id(client_id)
            password_verifier = bytes.fromhex(user_info.password_verifier)
            salt = bytes.fromhex(user_info.salt)
            client_public = bytes.fromhex(request.client_public)

            srv = srp.Verifier(email, salt, password_verifier, client_public)
            s, B = srv.get_challenge()

            server_private = srv.get_ephemeral_secret().hex()
            user_info.srp_server_private = server_private
            user_info.update()

            public_challenge_b = B.hex()

            auth_challenge_res = user_messages.MfaAuthChallengeResponse(
                salt=user_info.salt, public_challenge_b=public_challenge_b)
            return auth_challenge_res
        except Exception as e:
            logger.error(e)
            if not e.args or e.args[0] not in Message.msg_dict:
                errors = [
                    Message.get_error_object(Message.GET_MFA_STATE_FALED)
                ]
            else:
                errors = [Message.get_error_object(e.args[0])]
            context.set_details(
                json.dumps(errors, default=lambda x: x.__dict__))
            context.set_code(grpc.StatusCode.INTERNAL)
Exemplo n.º 14
0
    def post(self, request):

        try:
            email = request.data['email']
            challenge = binascii.unhexlify(request.data['clientChallenge'])

            validate_email(email)
            user = User.objects.get(username=email)
        except KeyError:
            return JsonResponse({'error': "BadInput"}, status=400)
        except User.DoesNotExist:
            return JsonResponse({'error': "UserNotExists"}, status=403)
        except ValidationError:
            return JsonResponse({'error': 'EmailNotValid'}, status=400)
        except binascii.Error:
            return JsonResponse({'error': 'BadInput'}, status=400)

        if not user.is_active:
            return JsonResponse({'error': "UserNotExists"}, status=403)

        svr = srp.Verifier(email,
                           user.auth_profile.salt,
                           user.auth_profile.verifier,
                           challenge,
                           hash_alg=srp.SHA256)
        salt, server_challenge = svr.get_challenge()

        user.auth_profile.client_challenge = challenge
        user.auth_profile.server_challenge_id = svr.get_ephemeral_secret()
        user.auth_profile.save()

        return JsonResponse({
            'salt':
            binascii.hexlify(salt).decode('utf-8'),
            'serverChallenge':
            binascii.hexlify(server_challenge).decode('utf-8')
        })
Exemplo n.º 15
0
    async def change_password(self, request, context):
        try:
            header_data = dict(context.invocation_metadata())
            introspect_token = KeyCloakUtils.introspect_token(
                header_data['access_token'])
            user_name = introspect_token['preferred_username']
            client_session_key_proof = request.client_session_key_proof
            user_info = self.service.get_user_by_auth_source(
                user_name, "account")

            if not user_info:
                raise Exception(Message.AUTH_USER_NOT_FOUND)
            password_verifier = bytes.fromhex(user_info.password_verifier)
            salt = bytes.fromhex(user_info.salt)
            client_session_key_proof_bytes = bytes.fromhex(
                client_session_key_proof)

            srv = srp.Verifier(username=user_name,
                               bytes_s=salt,
                               bytes_v=password_verifier,
                               bytes_A=bytes.fromhex(request.client_public),
                               bytes_b=bytes.fromhex(
                                   user_info.srp_server_private))
            srv.verify_session(client_session_key_proof_bytes)
            authenticated = srv.authenticated()
            if not authenticated:
                raise Exception(Message.AUTHENTICATION_FAILED)

            self.service.change_password(
                request, user_info.password_verifier, request.hash_password,
                introspect_token['sub'])  # update for keycloak

            try:
                old_identity_key_encrypted = SignalService(
                ).client_update_identity_key(introspect_token["sub"],
                                             request.identity_key_encrypted)
            except Exception as e:
                logger.error(e)
                self.service.change_password(request, request.hash_password,
                                             user_info.password_verifier,
                                             introspect_token['sub'])
                raise Exception(Message.REGISTER_CLIENT_SIGNAL_KEY_FAILED)
            try:
                salt, iv_parameter = self.service.update_hash_pass(
                    introspect_token["sub"], request.hash_password,
                    request.salt, request.iv_parameter)
            except Exception as e:
                logger.error(e)
                self.service.change_password(request, request.hash_password,
                                             request.password_verifier,
                                             introspect_token['sub'])
                SignalService().client_update_identity_key(
                    introspect_token["sub"], old_identity_key_encrypted)
                raise Exception(Message.REGISTER_CLIENT_SIGNAL_KEY_FAILED)
            user_sessions = KeyCloakUtils.get_sessions(
                user_id=introspect_token["sub"])
            for user_session in user_sessions:
                if user_session['id'] != introspect_token['session_state']:
                    KeyCloakUtils.remove_session(session_id=user_session['id'])
            return user_messages.BaseResponse()

        except Exception as e:
            logger.error(e)
            if not e.args or e.args[0] not in Message.msg_dict:
                errors = [
                    Message.get_error_object(Message.CHANGE_PASSWORD_FAILED)
                ]
            else:
                errors = [Message.get_error_object(e.args[0])]
            context.set_details(
                json.dumps(errors, default=lambda x: x.__dict__))
            context.set_code(grpc.StatusCode.INTERNAL)
Exemplo n.º 16
0
    async def verify_pincode(self, request, context):
        try:
            exists_user = self.service.get_user_by_email(request.user_name)
            user_info = self.user_service.get_user_by_id(exists_user["id"])
            if not user_info:
                raise Exception(Message.AUTH_USER_NOT_FOUND)
            password_verifier = bytes.fromhex(user_info.password_verifier)
            salt = bytes.fromhex(user_info.salt)
            client_session_key_proof = request.client_session_key_proof
            client_session_key_proof_bytes = bytes.fromhex(
                client_session_key_proof)

            srv = srp.Verifier(username=request.user_name,
                               bytes_s=salt,
                               bytes_v=password_verifier,
                               bytes_A=bytes.fromhex(request.client_public),
                               bytes_b=bytes.fromhex(
                                   user_info.srp_server_private))
            srv.verify_session(client_session_key_proof_bytes)
            authenticated = srv.authenticated()

            if not authenticated:
                raise Exception(Message.AUTHENTICATION_FAILED)

            token = self.service.token(request.user_name,
                                       user_info.password_verifier)
            introspect_token = KeyCloakUtils.introspect_token(
                token['access_token'])
            if not token:
                raise Exception(Message.VERIFY_PINCODE_FAILED)

            client_key_obj = SignalService().peer_get_client_key(
                introspect_token['sub'])
            client_key_peer = auth_messages.PeerGetClientKeyResponse(
                clientId=introspect_token['sub'],
                workspace_domain=get_owner_workspace_domain(),
                registrationId=client_key_obj.registration_id,
                deviceId=client_key_obj.device_id,
                identityKeyPublic=client_key_obj.identity_key_public,
                preKeyId=client_key_obj.prekey_id,
                preKey=client_key_obj.prekey,
                signedPreKeyId=client_key_obj.signed_prekey_id,
                signedPreKey=client_key_obj.signed_prekey,
                signedPreKeySignature=client_key_obj.signed_prekey_signature,
                identityKeyEncrypted=client_key_obj.identity_key_encrypted)
            self.user_service.update_last_login(
                user_id=introspect_token['sub'])
            return auth_messages.AuthRes(
                workspace_domain=get_owner_workspace_domain(),
                workspace_name=get_system_config()['server_name'],
                access_token=token["access_token"],
                expires_in=token['expires_in'],
                refresh_expires_in=token['refresh_expires_in'],
                refresh_token=token['refresh_token'],
                token_type=token['token_type'],
                session_state=token['session_state'],
                scope=token['scope'],
                salt=user_info.salt,
                client_key_peer=client_key_peer,
                iv_parameter=user_info.iv_parameter)
        except Exception as e:
            logger.error(e)
            if not e.args or e.args[0] not in Message.msg_dict:
                errors = [
                    Message.get_error_object(Message.VERIFY_PINCODE_FAILED)
                ]
            else:
                errors = [Message.get_error_object(e.args[0])]
            context.set_details(
                json.dumps(errors, default=lambda x: x.__dict__))
            context.set_code(grpc.StatusCode.INTERNAL)
Exemplo n.º 17
0
	def on_frame(self, frame: str) -> None:
                try:
                    message = json.loads(frame)
                except:
                    logger.exception("Could not decode JSON message: {}".format(frame))
                    self.transport.close()
                    return
                mtype = message.get('type').upper()
                if mtype == 'HEY':
                    self.text_to_sign = message.get('data')
                    self.server_cert = base64.b16encode(self.getCert()).decode()
                    print(self.server_cert)
                    self._send({'type','CERT_SERVER', 'data', self.server_cert })
                    self.server_signature = base64.b64encode(self.getSignature()).decode()
                    self._send({'type','SIGN_SERVER', 'data', self.server_signature })
                if mtype == 'SERVER_OK':
                    self.client_text = message.get('data')
                if mtype == 'SERVER_OK':
                    self.client_text = base64.b64decode(message.get('data')).encode()
                if mtype == 'CERT_CLIENT':
                    self.client_cert = base64.b64decode(message.get('data')).encode()
                if mtype == 'SIGN_CLIENT':
                    self.sign_client = message.get('data')
                    if not self.verifyClient():
                        return
                    self._send({'type','START_LOGIN'})
                if mtype == 'USER':
                    uname = message.get('uname')
                    A = base64.b64decode(message.get('A')).encode()
                    password = self.getPassword(uname)
                    salt, vkey = srp.create_salted_verification_key( uname, password )
                    self.svr = srp.Verifier( uname, salt, vkey, A)
                    s,B = self.svr.get_challenge()
                    self._send({'type','s', 'data', base64.b64encode(s).decode() })
                    self._send({'type','B', 'data', base64.b64encode(B).decode() })
                if mtype == 'M':
                    M = base64.b64decode(message.get('data')).encode()
                    HAMK = self.svr.verify_session(M)
                    if HAMK:
                        self._send({'type': 'OKOK'})
                if mtype == 'HELLO':
                    self.algorithms = message.get('data').split('_')
                    
                    if self.algorithms:
                        self.keyPair()
                        logger.info("Send public Key")
                        self._send({'type': 'PUBLIC_KEY', 'data': base64.b64encode(self.pem_public_key).decode()})
                        ret = True
                    else:
                        ret = False
                
                if mtype == 'SECURE':
                    self.encriptkey = base64.b64decode(message.get('data'))
                    if self.encriptkey != '':
                        logger.info("Key")
                        self.getKey()
                        ret = True
                    else:
                        ret = False
                if mtype == 'SECURE_IV':
                    logger.info("iv")
                    self.iv=base64.b64decode(message.get('data'))
                    if self.iv != '':
                        ret = True
                    else:
                        ret= False
                if mtype == 'OPEN':
                    ret = self.process_open(message)
                if mtype == 'DATA':
                    ret = self.process_data(message)
                if mtype == 'CLOSE':
                    ret = self.process_close(message)
                    logger.info("Decrypt file")
                    self.decryptFile()
                else:
                    logger.warning("Invalid message type: {}".format(message['type']))
                    ret = False
                if not ret:
                    try:
                        self._send({'type': 'ERROR', 'message': 'See server'})
                    except:
                        pass # Silently ignore
                    logger.info("Closing transport")
                    if self.file is not None:
                        self.file.close()
                        self.file = None
                    self.state = STATE_CLOSE
                    self.transport.close()
Exemplo n.º 18
0
class AuthenticationFailed(Exception):
    pass


# ~~~ Begin Authentication ~~~

usr = srp.User('testuser', 'testpassword')
uname, A = usr.start_authentication()

# The authentication process can fail at each step from this
# point on. To comply with the SRP protocol, the authentication
# process should be aborted on the first failure.

# Client => Server: username, A
svr = srp.Verifier(uname, salt, vkey, A)
s, B = svr.get_challenge()

if s is None or B is None:
    raise AuthenticationFailed()

# Server => Client: s, B
M = usr.process_challenge(s, B)

if M is None:
    raise AuthenticationFailed()

# Client => Server: M
HAMK = svr.verify_session(M)

if HAMK is None: