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)
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, )
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') }
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') }
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
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)
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)
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)
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()
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)
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)
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)
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)
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') })
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)
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)
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()
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: