def test_client_data(self): client_data = ClientData( b'{"typ":"navigator.id.finishEnrollment","challenge":"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}' ) # noqa self.assertEqual( client_data.hash, a2b_hex( '4142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfacb' )) # noqa self.assertEqual(client_data.get('origin'), 'http://example.com') self.assertEqual(client_data, ClientData.from_b64(client_data.b64)) self.assertEqual( client_data.data, { 'typ': 'navigator.id.finishEnrollment', 'challenge': 'vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo', 'cid_pubkey': { 'kty': 'EC', 'crv': 'P-256', 'x': 'HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8', 'y': 'XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4' }, 'origin': 'http://example.com' })
def test_client_data(self): client_data = ClientData( b'{"typ":"navigator.id.finishEnrollment","challenge":"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}' # noqa E501 ) self.assertEqual( client_data.hash, a2b_hex( "4142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfacb" ), ) self.assertEqual(client_data.get("origin"), "http://example.com") self.assertEqual(client_data, ClientData.from_b64(client_data.b64)) self.assertEqual( client_data.data, { "typ": "navigator.id.finishEnrollment", "challenge": "vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo", "cid_pubkey": { "kty": "EC", "crv": "P-256", "x": "HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8", "y": "XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4", }, "origin": "http://example.com", }, )
def test_u2f_facets(self): rp = RelyingParty("example.com", "Example", "http://example.com/icon.svg") app_id = b"https://www.example.com/facets.json" def verify_u2f_origin(origin): return origin in ("https://oauth.example.com", "https://admin.example.com") server = U2FFido2Server(app_id=app_id.decode("ascii"), rp=rp, verify_u2f_origin=verify_u2f_origin) state = { "challenge": "GAZPACHO!", "user_verification": USER_VERIFICATION.PREFERRED, } client_data_dict = { "challenge": "GAZPACHO!", "origin": "https://oauth.example.com", "type": WEBAUTHN_TYPE.GET_ASSERTION, } client_data = ClientData(json.dumps(client_data_dict).encode("utf-8")) param = b"TOMATO GIVES " device = U2FDevice(param, app_id) auth_data = AttestedCredentialData.from_ctap1(param, device.public_key_bytes) authenticator_data, signature = device.sign(client_data) server.authenticate_complete( state, [auth_data], device.credential_id, client_data, authenticator_data, signature, ) # Now with something not whitelisted client_data_dict = { "challenge": "GAZPACHO!", "origin": "https://publicthingy.example.com", "type": WEBAUTHN_TYPE.GET_ASSERTION, } client_data = ClientData(json.dumps(client_data_dict).encode("utf-8")) authenticator_data, signature = device.sign(client_data) with six.assertRaisesRegex(self, ValueError, "Invalid origin in " "ClientData."): server.authenticate_complete( state, [auth_data], device.credential_id, client_data, authenticator_data, signature, )
def test_u2f_facets(self): rp = RelyingParty('example.com', 'Example', 'http://example.com/icon.svg') app_id = b'https://www.example.com/facets.json' def verify_u2f_origin(origin): return origin in ('https://oauth.example.com', 'https://admin.example.com') server = U2FFido2Server(app_id=app_id.decode('ascii'), rp=rp, verify_u2f_origin=verify_u2f_origin) state = { 'challenge': 'GAZPACHO!', 'user_verification': USER_VERIFICATION.PREFERRED } client_data_dict = { 'challenge': 'GAZPACHO!', 'origin': 'https://oauth.example.com', 'type': WEBAUTHN_TYPE.GET_ASSERTION } client_data = ClientData(json.dumps(client_data_dict).encode('utf-8')) param = b'TOMATO GIVES ' device = U2FDevice(param, app_id) auth_data = AttestedCredentialData.from_ctap1(param, device.public_key_bytes) authenticator_data, signature = device.sign(client_data) server.authenticate_complete(state, [auth_data], device.credential_id, client_data, authenticator_data, signature) # Now with something not whitelisted client_data_dict = { 'challenge': 'GAZPACHO!', 'origin': 'https://publicthingy.example.com', 'type': WEBAUTHN_TYPE.GET_ASSERTION } client_data = ClientData(json.dumps(client_data_dict).encode('utf-8')) authenticator_data, signature = device.sign(client_data) with six.assertRaisesRegex(self, ValueError, 'Invalid origin in ' 'ClientData.'): server.authenticate_complete(state, [auth_data], device.credential_id, client_data, authenticator_data, signature)
def profile_webauthn_register_route(): """register credential for current user""" user = User.query.get(current_user.id) form = WebauthnRegisterForm() if form.validate_on_submit(): try: attestation = cbor.decode(b64decode(form.attestation.data)) auth_data = webauthn.register_complete( session.pop('webauthn_register_state'), ClientData(attestation['clientDataJSON']), AttestationObject(attestation['attestationObject'])) db.session.add(WebauthnCredential( user_id=user.id, user_handle=session.pop('webauthn_register_user_handle'), credential_data=cbor.encode(auth_data.credential_data.__dict__), name=form.name.data)) db.session.commit() return redirect(url_for('auth.profile_route')) except (KeyError, ValueError) as e: current_app.logger.exception(e) flash('Error during registration.', 'error') return render_template('auth/profile/webauthn_register.html', form=form)
def authenticate_complete(): if not User.query.count(): abort(404) try: data = cbor.decode(request.get_data()) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] except IndexError: abort(400) user = User.get_by(credential_id) if user is None: abort(401) server.authenticate_complete( session.pop("state"), [user.create_data], credential_id, client_data, auth_data, signature, ) return cbor.encode({"status": "OK"})
def authenticate_complete(request): data = cbor.decode(request.body) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] credentials = [ row.credential for row in User.objects.get( pk=request.session['user']).fidocredential_set.all() ] fido_server.authenticate_complete( request.session.pop("state"), credentials, credential_id, client_data, auth_data, signature, ) try: user = User.objects.get(pk=request.session.pop('user')) except User.DoesNotExist: raise Http404 else: # If we're here, we've already confirmed the user has the # username and the password. login(request, user) return HttpResponse( cbor.encode({"status": "OK"}), content_type='application/cbor', )
def test_authenticate_complete_invalid_signature(self): rp = RelyingParty("example.com", "Example") server = Fido2Server(rp) state = { "challenge": "GAZPACHO!", "user_verification": USER_VERIFICATION.PREFERRED, } client_data_dict = { "challenge": "GAZPACHO!", "origin": "https://example.com", "type": WEBAUTHN_TYPE.GET_ASSERTION, } client_data = ClientData(json.dumps(client_data_dict).encode("utf-8")) _AUTH_DATA = a2b_hex( "A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947010000001D" ) with six.assertRaisesRegex(self, ValueError, "Invalid signature."): server.authenticate_complete( state, [AttestedCredentialData(_ATT_CRED_DATA)], _CRED_ID, client_data, AuthenticatorData(_AUTH_DATA), b"INVALID", )
def login_webauthn_route(): """login webauthn route""" user = User.query.filter( User.id == session.get('webauthn_login_user_id')).one_or_none() if not user: return login_manager.unauthorized() form = WebauthnLoginForm() if form.validate_on_submit(): try: assertion = cbor.decode(b64decode(form.assertion.data)) webauthn.authenticate_complete( session.pop('webauthn_login_state'), webauthn_credentials(user), assertion['credentialRawId'], ClientData(assertion['clientDataJSON']), AuthenticatorData(assertion['authenticatorData']), assertion['signature']) regenerate_session() login_user(user) return redirect_after_login() except (KeyError, ValueError) as e: current_app.logger.exception(e) flash('Login error during Webauthn authentication.', 'error') return render_template('auth/login_webauthn.html', form=form)
def page(self) -> CBORPageResult: assert user.id is not None if not is_two_factor_login_enabled(user.id): raise MKGeneralException( _("Two-factor authentication not enabled")) data: dict[str, object] = cbor.decode(request.get_data()) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] logger.debug("ClientData: %r", client_data) logger.debug("AuthenticatorData: %r", auth_data) make_fido2_server().authenticate_complete( session.session_info.webauthn_action_state, [ AttestedCredentialData.unpack_from(v["credential_data"])[0] for v in load_two_factor_credentials(user.id) ["webauthn_credentials"].values() ], credential_id, client_data, auth_data, signature, ) session.session_info.webauthn_action_state = None set_two_factor_completed() return {"status": "OK"}
def test_authenticate(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = a2b_hex('0100000001304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f') + b'\x90\x00' # noqa client_param = a2b_hex(b'ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57') # noqa app_param = a2b_hex(b'4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca') # noqa key_handle = b'\3'*64 resp = ctap.authenticate(client_param, app_param, key_handle) ctap.device.call.assert_called_with(0x03, b'\0\2\3\0\0\0\x81' + client_param + app_param + b'\x40' + key_handle + b'\0\0') self.assertEqual(resp.user_presence, 1) self.assertEqual(resp.counter, 1) self.assertEqual(resp.signature, a2b_hex('304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f')) # noqa public_key = a2b_hex(b'04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d') # noqa client_data = ClientData(b'{"typ":"navigator.id.getAssertion","challenge":"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}') # noqa resp.verify(app_param, client_param, public_key) key_handle = b'\4'*8 ctap.authenticate(client_param, app_param, key_handle) ctap.device.call.assert_called_with(0x03, b'\0\2\3\0\0\0\x49' + client_param + app_param + b'\x08' + key_handle + b'\0\0') ctap.authenticate(client_param, app_param, key_handle, True) ctap.device.call.assert_called_with(0x03, b'\0\2\7\0\0\0\x49' + client_param + app_param + b'\x08' + key_handle + b'\0\0')
def page(self) -> CBORPageResult: assert user.id is not None user.need_permission("general.manage_2fa") raw_data = request.get_data() logger.debug("Raw request: %r", raw_data) data: dict[str, object] = cbor.decode(raw_data) client_data = ClientData(data["clientDataJSON"]) att_obj = AttestationObject(data["attestationObject"]) logger.debug("Client data: %r", client_data) logger.debug("Attestation object: %r", att_obj) auth_data = make_fido2_server().register_complete( session.session_info.webauthn_action_state, client_data, att_obj ) ident = auth_data.credential_data.credential_id.hex() credentials = load_two_factor_credentials(user.id, lock=True) if ident in credentials["webauthn_credentials"]: raise MKGeneralException(_("Your WebAuthn credetial is already in use")) credentials["webauthn_credentials"][ident] = WebAuthnCredential( { "credential_id": ident, "registered_at": int(time.time()), "alias": "", "credential_data": bytes(auth_data.credential_data), } ) save_two_factor_credentials(user.id, credentials) flash(_("Registration successful")) return {"status": "OK"}
def verify_token(self, token): data = cbor2.loads(base64.b64decode(token["token"])) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] state = token["state"] domain = token["domain"] credentials_query = Fido2Device.objects.filter(user=self.user) credentials = [ AttestedCredentialData(cbor2.loads(c.authenticator_data)) for c in credentials_query ] rp = PublicKeyCredentialRpEntity(domain, settings.FIDO2_RP_NAME) fido2 = Fido2Server(rp) try: fido2.authenticate_complete( state, credentials, credential_id, client_data, auth_data, signature, ) return True except ValueError: logger.exception("Error in FIDO2 final authentication") return False
def _add_token_to_user(self, registration_data, state): data = registration_data + (b'=' * (len(registration_data) % 4)) data = base64.urlsafe_b64decode(data) data = cbor.decode(data) client_data = ClientData(data['clientDataJSON']) attestation = data['attestationObject'] att_obj = AttestationObject(attestation) server = get_webauthn_server(self.app.config.fido2_rp_id) auth_data = server.register_complete(state, client_data, att_obj) cred_data = auth_data.credential_data cred_id = cred_data.credential_id credential = Webauthn.from_dict( dict( keyhandle=cred_id.hex(), credential_data=base64.urlsafe_b64encode(cred_data).decode( 'ascii'), app_id=self.app.config.fido2_rp_id, attest_obj=base64.b64encode(attestation).decode('ascii'), description='ctap1 token', created_by='test_security', )) self.test_user.credentials.add(credential) self.app.central_userdb.save(self.test_user, check_sync=False) return credential
def authenticate_complete(request): credentials = [] username=request.session.get("base_username",request.user.username) server=getServer() credentials=getUserCredentials(username) data = cbor.loads(request.body)[0] credential_id = data['credentialId'] client_data = ClientData(data['clientDataJSON']) auth_data = AuthenticatorData(data['authenticatorData']) signature = data['signature'] cred = server.authenticate_complete( request.session.pop('fido_state'), credentials, credential_id, client_data, auth_data, signature ) keys = User_Keys.objects.filter(username=username, key_type="FIDO2",enabled=1) import random for k in keys: if AttestedCredentialData(websafe_decode(k.properties["device"])).credential_id == cred.credential_id: k.last_used = timezone.now() k.save() mfa = {"verified": True, "method": "FIDO2"} if getattr(settings, "MFA_RECHECK", False): mfa["next_check"] = int((datetime.datetime.now()+ datetime.timedelta( seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))).strftime("%s")) request.session["mfa"] = mfa res=login(request) return HttpResponse(simplejson.dumps({'status':"OK","redirect":res["location"]}),content_type="application/json") return HttpResponse(simplejson.dumps({'status': "err"}))
def complete_reg(request): try: data = cbor.decode(request.body) client_data = ClientData(data['clientDataJSON']) att_obj = AttestationObject((data['attestationObject'])) server = get_server() auth_data = server.register_complete(request.session['fido_state'], client_data, att_obj) encoded = websafe_encode(auth_data.credential_data) UserKey.objects.create( username=request.user.get_username(), properties={ "device": encoded, "type": att_obj.fmt }, key_type="FIDO2", ) return JsonResponse({'status': 'OK'}) except: return JsonResponse({ 'status': 'ERR', "message": "Error on server, please try again later", })
def authenticate_complete(request): credentials = [] username = request.session.get("base_username", request.user.get_username()) server = get_server() credentials = get_user_credentials(username) data = cbor.decode(request.body) credential_id = data['credentialId'] client_data = ClientData(data['clientDataJSON']) auth_data = AuthenticatorData(data['authenticatorData']) signature = data['signature'] cred = server.authenticate_complete(request.session.pop('fido_state'), credentials, credential_id, client_data, auth_data, signature) for k in UserKey.objects.filter(username=username, key_type="FIDO2", enabled=1): if AttestedCredentialData(websafe_decode( k.properties["device"])).credential_id == cred.credential_id: k.last_used = timezone.now() k.save() mfa = {"verified": True, "method": "FIDO2", 'id': k.id} if getattr(settings, "MFA_RECHECK", False): mfa["next_check"] = next_check() request.session["mfa"] = mfa res = login(request) return JsonResponse({'status': "OK", "redirect": res["location"]}) return JsonResponse({'status': "err"})
def post(self, request, format=None): logger.info(f'webauthn-register.{format}: {request.data}') clientData = request.data.get("clientDataJSON", None) attestationData = request.data.get("attestationObject", None) if clientData and attestationData: client = ClientData(clientData) attestation = AttestationObject(attestationData) state = request.session.get(settings.FIDO2.SESSION_STATE_KEY) if client and attestation and state: try: auth_data = SERVER.register_complete( state, client, attestation) cred_data = auth_data.credential_data authenticator = Authenticator.objects.create( user=request.user, credential=cred_data) logger.info( f'register fido2 for "{request.user.username}": {cred_data.credential_id}' ) return Response( dict(detail="OK", user=getuser(request.user))) except Exception as e: logger.warn( f'Exception webauthn-register.{format} {request.user.username}: {e}' ) return Response(dict(detail="Bad request"), status=status.HTTP_400_BAD_REQUEST)
def test_u2f(self): rp = RelyingParty("example.com", "Example", "http://example.com/icon.svg") app_id = b"https://example.com" server = U2FFido2Server(app_id=app_id.decode("ascii"), rp=rp) state = { "challenge": "GAZPACHO!", "user_verification": USER_VERIFICATION.PREFERRED, } client_data_dict = { "challenge": "GAZPACHO!", "origin": "https://example.com", "type": WEBAUTHN_TYPE.GET_ASSERTION, } client_data = ClientData(json.dumps(client_data_dict).encode("utf-8")) param = b"TOMATO GIVES " device = U2FDevice(param, app_id) auth_data = AttestedCredentialData.from_ctap1(param, device.public_key_bytes) authenticator_data, signature = device.sign(client_data) server.authenticate_complete( state, [auth_data], device.credential_id, client_data, authenticator_data, signature, )
def registration_complete(user, credential_id, attestation_object, client_data, description): security_user = SecurityUser.from_user(user, current_app.private_userdb) server = get_webauthn_server(current_app.config.fido2_rp_id) att_obj = AttestationObject(urlsafe_b64decode(attestation_object)) cdata_obj = ClientData(urlsafe_b64decode(client_data)) state = session['_webauthn_state_'] auth_data = server.register_complete(state, cdata_obj, att_obj) cred_data = auth_data.credential_data current_app.logger.debug('Proccessed Webauthn credential data: {}.'.format(cred_data)) credential = Webauthn.from_dict( dict( keyhandle=credential_id, credential_data=base64.urlsafe_b64encode(cred_data).decode('ascii'), app_id=current_app.config.fido2_rp_id, attest_obj=base64.b64encode(attestation_object.encode('utf-8')).decode('ascii'), description=description, created_by='security', ) ) security_user.credentials.add(credential) save_and_sync_user(security_user) current_app.stats.count(name='webauthn_register_complete') current_app.logger.info('User {} has completed registration of a webauthn token'.format(security_user)) credentials = compile_credential_list(security_user) return success_response(payload=dict(credentials=credentials), message=SecurityMsg.webauthn_success)
def authenticate(): global credential, last_challenge if not credential: return HTML.format(content='No credential registered!') if request.method == 'POST': data = request.get_json(force=True) client_data = ClientData(from_js_array(data['clientData'])) auth_data = AuthenticatorData(from_js_array(data['authenticatorData'])) signature = from_js_array(data['signature']) print('clientData', client_data) print('AuthenticatorData', auth_data) # Verify the challenge if client_data.challenge != last_challenge: raise ValueError('Challenge mismatch!') # Verify the signature credential.public_key.verify(auth_data + client_data.hash, signature) print('ASSERTION OK') return 'OK' last_challenge = os.urandom(32) return AUTH_HTML.format(challenge=to_js_array(last_challenge), credential_id=to_js_array( credential.credential_id))
def complete_reg(request): try: data = cbor.decode(request.body) client_data = ClientData(data['clientDataJSON']) att_obj = AttestationObject((data['attestationObject'])) server = get_server() auth_data = server.register_complete(request.session['fido_state'], client_data, att_obj) encoded = websafe_encode(auth_data.credential_data) key = UserKey.objects.create( user=request.user, properties={ "device": encoded, "type": att_obj.fmt, "domain": request.get_host(), }, key_type=KEY_TYPE_FIDO2, ) write_session(request, key) messages.success(request, 'FIDO2 Token added!') return JsonResponse({'status': 'OK'}) except Exception: logger.exception("Error completing FIDO2 registration.") return JsonResponse({ 'status': 'ERR', "message": "Error on server, please try again later", })
def fido2_keys_user_validate(user_id): keys = list_fido2_keys(user_id) credentials = list(map(lambda k: pickle.loads(base64.b64decode(k.key)), keys)) data = request.get_json() cbor_data = cbor.decode(base64.b64decode(data["payload"])) credential_id = cbor_data['credentialId'] client_data = ClientData(cbor_data['clientDataJSON']) auth_data = AuthenticatorData(cbor_data['authenticatorData']) signature = cbor_data['signature'] Config.FIDO2_SERVER.authenticate_complete( get_fido2_session(user_id), credentials, credential_id, client_data, auth_data, signature ) user_to_verify = get_user_by_id(user_id=user_id) user_to_verify.current_session_id = str(uuid.uuid4()) user_to_verify.logged_in_at = datetime.utcnow() user_to_verify.failed_login_count = 0 save_model_user(user_to_verify) return jsonify({'status': 'OK'})
def fido2_login_complete(req): user = None logreq('fido2_login_complete', req) username = req.session.get(FIDO2_USER_KEY, None) if username: user = get_user_model().objects.get(username=username) if user: creds = get_fido2_credentials(user) data = cbor.decode(req.body) crid = data['credentialId'] cdat = ClientData(data['clientDataJSON']) adat = AuthenticatorData(data['authenticatorData']) csig = data['signature'] stat = get_fido2_state(req) try: SERVER.authenticate_complete(stat, creds, crid, cdat, adat, csig) except ValueError as e: logger.warn('Failed fido2 authentication', e) return HttpResponseForbidden() found = False for a in user.authenticators.all(): if a.crid == crid: a.inc_counter() found = True break if not found: logger.warn( f'Failed to bump fido2 counter for cred-id "{crid}" owned by "{user.username}".' ) auth.login(req, user) messages.info( req, f'Welcome back {user.get_full_name() or user.username}.') logger.info(f'login_complete successful for user "{user.username}".') return get_cbor_resp(redirect='/') return HttpResponseForbidden()
def assertion_response(): app.logger.debug("/assertion/response") data = cbor.decode(request.get_data()) app.logger.debug('post_data:\n%s', pp.pformat(data)) credential_id = data['id'] raw_id = data['rawId'] client_data = ClientData(data['clientDataJSON']) app.logger.debug('client_data:\n%s', pp.pformat(client_data)) auth_data = AuthenticatorData(data['authenticatorData']) app.logger.debug('auth_data:\n%s', pp.pformat(auth_data)) signature = data['signature'] credential = Credential.get(credential_id) if not credential: abort(404) credential_data = credential.to_credential_data() server.authenticate_complete(session.pop('state'), [credential_data], raw_id, client_data, auth_data, signature) # Checking signCount if auth_data.counter > credential.counter: # TODO: flask_ldapconn is not allow to store integer value credential.counter = str(auth_data.counter) credential.save() else: app.logger.warn('wrong counter: stored counter=%d but %d asserted', credential.counter, auth_data.counter) abort(404) user = credential.user if not user: abort(404) login_user(user) return cbor.encode({'status': 'OK'})
def test_u2f(self): rp = RelyingParty('example.com', 'Example', 'http://example.com/icon.svg') app_id = b'https://example.com' server = U2FFido2Server(app_id=app_id.decode('ascii'), rp=rp) state = { 'challenge': 'GAZPACHO!', 'user_verification': USER_VERIFICATION.PREFERRED } client_data_dict = { 'challenge': 'GAZPACHO!', 'origin': 'https://example.com', 'type': WEBAUTHN_TYPE.GET_ASSERTION } client_data = ClientData(json.dumps(client_data_dict).encode('utf-8')) param = b'TOMATO GIVES ' device = U2FDevice(param, app_id) auth_data = AttestedCredentialData.from_ctap1(param, device.public_key_bytes) authenticator_data, signature = device.sign(client_data) server.authenticate_complete(state, [auth_data], device.credential_id, client_data, authenticator_data, signature)
def authenticate_complete(request): server = get_server() data = cbor.decode(request.body) credential_id = data['credentialId'] client_data = ClientData(data['clientDataJSON']) auth_data = AuthenticatorData(data['authenticatorData']) signature = data['signature'] cred = server.authenticate_complete(request.session.pop('fido_state'), get_user_credentials(request), credential_id, client_data, auth_data, signature) keys = UserKey.objects.filter( user=request.user, key_type=KEY_TYPE_FIDO2, enabled=True, ) for key in keys: if AttestedCredentialData(websafe_decode( key.properties["device"])).credential_id == cred.credential_id: write_session(request, key) res = login(request) return JsonResponse({'status': "OK", "redirect": res["location"]}) return JsonResponse({'status': "err"})
def complete_reg(request): """Completes the registeration, called by API""" try: data = cbor.decode(request.body) client_data = ClientData(data['clientDataJSON']) att_obj = AttestationObject((data['attestationObject'])) server = getServer() auth_data = server.register_complete(request.session['fido_state'], client_data, att_obj) encoded = websafe_encode(auth_data.credential_data) uk = User_Keys() uk.username = request.user.username uk.properties = { "device": encoded, "type": att_obj.fmt, } uk.owned_by_enterprise = getattr(settings, "MFA_OWNED_BY_ENTERPRISE", False) uk.key_type = "FIDO2" uk.save() return HttpResponse(simplejson.dumps({'status': 'OK'})) except Exception as exp: try: from raven.contrib.django.raven_compat.models import client client.captureException() except: pass return HttpResponse( simplejson.dumps({ 'status': 'ERR', "message": "Error on server, please try again later" }))
def complete_reg(request): try: data = cbor.loads(request.body)[0] client_data = ClientData(data['clientDataJSON']) att_obj = AttestationObject((data['attestationObject'])) server = getServer() auth_data = server.register_complete(request.session['fido_state'], client_data, att_obj) encoded = websafe_encode(auth_data.credential_data) uk = User_Keys() uk.username = request.user.username uk.properties = { "device": encoded, "type": att_obj.fmt, } uk.key_type = "FIDO2" uk.save() return HttpResponse(simplejson.dumps({'status': 'OK'})) except Exception as exp: from raven.contrib.django.raven_compat.models import client import traceback client.captureException() return HttpResponse( simplejson.dumps({ 'status': 'ERR', "message": "Error on server, please try again later" }))
def _get_assertion_request( rp_id: str = 'pasten.com', client_data: Optional[ClientData] = None, allowed_cred_ids: Optional[List[bytes]] = None, user_verification_required=False, ) -> dict: client_data = client_data or ClientData.build( typ=WEBAUTHN_TYPE.GET_ASSERTION, origin=rp_id, challenge=websafe_encode(b'pasten-challenge'), ) req = { CtapGetAssertionRequest.RP_ID_KEY: rp_id, CtapGetAssertionRequest.CLIENT_DATA_HASH_KEY: client_data.hash, } if user_verification_required: req[CtapGetAssertionRequest.OPTIONS_KEY] = { CtapOptions.USER_VERIFICATION: True } if allowed_cred_ids: req[CtapGetAssertionRequest.ALLOW_LIST_KEY] = [ PublicKeyCredentialDescriptor( PublicKeyCredentialType.PUBLIC_KEY, cred_id ) for cred_id in allowed_cred_ids ] return req