Пример #1
0
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)
Пример #2
0
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"})
Пример #3
0
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"})
Пример #4
0
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"}))
Пример #5
0
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))
Пример #6
0
    def test_apple_attestation(self):
        attestation = Attestation.for_type("apple")()
        self.assertIsInstance(attestation, AppleAttestation)

        statement = {
            "alg":
            -7,
            "x5c": [
                a2b_hex(
                    "30820242308201c9a00302010202060176af5359ff300a06082a8648ce3d0403023048311c301a06035504030c134170706c6520576562417574686e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230313232383136323732345a170d3230313233313136323732345a3081913149304706035504030c4038303966626331313065613835663233613862323435616563363136333530663337646665393632313232373336653431663862646365663334366138306439311a3018060355040b0c114141412043657274696669636174696f6e31133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613059301306072a8648ce3d020106082a8648ce3d030107034200041f46a2f159fde354598cdd47e005f1b6e7c9f00ed2a941ec7a88d222f5bcf55d6b078bc5b0be9552d85a974921f5bb848e2bbc3aecd6f71a386d2c87d6eafd37a3553053300c0603551d130101ff04023000300e0603551d0f0101ff0404030204f0303306092a864886f76364080204263024a1220420e56fb6212b3aae885294464fb10184b7fea62c48a6d78e61194e07ae6dacc132300a06082a8648ce3d040302036700306402301de8f0f238eee4f5ae80c59290b51e8c3f79397bf198e444ba162d4fccaab8558b072cf00a7c662f9058ff2a98af61ae0230149403b9643066e73a98d3659563dc4da49bf84e82b2b5bbeaf57755646fa243f36344d44b80a5798203bca023e030c7"  # noqa E501
                ),
                a2b_hex(
                    "30820234308201baa003020102021056255395c7a7fb40ebe228d8260853b6300a06082a8648ce3d040303304b311f301d06035504030c164170706c6520576562417574686e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333830315a170d3330303331333030303030305a3048311c301a06035504030c134170706c6520576562417574686e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b8104002203620004832e872f261491810225b9f5fcd6bb6378b5f55f3fcb045bc735993475fd549044df9bfe19211765c69a1dda050b38d45083401a434fb24d112d56c3e1cfbfcb9891fec0696081bef96cbc77c88dddaf46a5aee1dd515b5afaab93be9c0b2691a366306430120603551d130101ff040830060101ff020100301f0603551d2304183016801426d764d9c578c25a67d1a7de6b12d01b63f1c6d7301d0603551d0e04160414ebae82c4ffa1ac5b51d4cf24610500be63bd7788300e0603551d0f0101ff040403020106300a06082a8648ce3d0403030368003065023100dd8b1a3481a5fad9dbb4e7657b841e144c27b75b876a4186c2b1475750337227efe554457ef648950c632e5c483e70c102302c8a6044dc201fcfe59bc34d2930c1487851d960ed6a75f1eb4acabe38cd25b897d0c805bef0c7f78b07a571c6e80e07"  # noqa E501
                ),
            ],
        }

        auth_data = AuthenticatorData(
            a2b_hex(
                b"c46cef82ad1b546477591d008b08759ec3e6d2ecb4f39474bfea6969925d03b7450000000000000000000000000000000000000000001473d9429f4052d84debd035eb5bb7e716e3b81863a50102032620012158201f46a2f159fde354598cdd47e005f1b6e7c9f00ed2a941ec7a88d222f5bcf55d2258206b078bc5b0be9552d85a974921f5bb848e2bbc3aecd6f71a386d2c87d6eafd37"  # noqa E501
            ))
        client_param = a2b_hex(
            b"0d3ce80fabbc3adb9dd891deabb8db84603ea1fe2da8b5d4b46d6591aab342f3"
        )

        res = attestation.verify(statement, auth_data, client_param)
        self.assertEqual(res.attestation_type, AttestationType.ANON_CA)
        self.assertEqual(len(res.trust_path), 2)
        verify_x509_chain(res.trust_path)
Пример #7
0
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()
Пример #8
0
    def test_packed_attestation(self):
        attestation = Attestation.for_type('packed')()
        self.assertIsInstance(attestation, PackedAttestation)

        statement = {
            'alg':
            -7,
            'sig':
            a2b_hex(
                b'304502200D15DAF337D727AB4719B4027114A2AC43CD565D394CED62C3D9D1D90825F0B3022100989615E7394C87F4AD91F8FDAE86F7A3326DF332B3633DB088AAC76BFFB9A46B'
            ),  # noqa
            'x5c': [
                a2b_hex(
                    b'308202B73082019FA00302010202041D31330D300D06092A864886F70D01010B0500302A3128302606035504030C1F59756269636F2050726576696577204649444F204174746573746174696F6E301E170D3138303332383036333932345A170D3139303332383036333932345A306E310B300906035504061302534531123010060355040A0C0959756269636F20414231223020060355040B0C1941757468656E74696361746F72204174746573746174696F6E3127302506035504030C1E59756269636F205532462045452053657269616C203438393736333539373059301306072A8648CE3D020106082A8648CE3D030107034200047D71E8367CAFD0EA6CF0D61E4C6A416BA5BB6D8FAD52DB2389AD07969F0F463BFDDDDDC29D39D3199163EE49575A3336C04B3309D607F6160C81E023373E0197A36C306A302206092B0601040182C40A020415312E332E362E312E342E312E34313438322E312E323013060B2B0601040182E51C0201010404030204303021060B2B0601040182E51C01010404120410F8A011F38C0A4D15800617111F9EDC7D300C0603551D130101FF04023000300D06092A864886F70D01010B050003820101009B904CEADBE1F1985486FEAD02BAEAA77E5AB4E6E52B7E6A2666A4DC06E241578169193B63DADEC5B2B78605A128B2E03F7FE2A98EAEB4219F52220995F400CE15D630CF0598BA662D7162459F1AD1FC623067376D4E4091BE65AC1A33D8561B9996C0529EC1816D1710786384D5E8783AA1F7474CB99FE8F5A63A79FF454380361C299D67CB5CC7C79F0D8C09F8849B0500F6D625408C77CBBC26DDEE11CB581BEB7947137AD4F05AAF38BD98DA10042DDCAC277604A395A5B3EAA88A5C8BB27AB59C8127D59D6BBBA5F11506BF7B75FDA7561A0837C46F025FD54DCF1014FC8D17C859507AC57D4B1DEA99485DF0BA8F34D00103C3EEF2EF3BBFEC7A6613DE'
                )
            ]  # noqa
        }
        auth_data = AuthenticatorData(
            a2b_hex(
                b'0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE124100000003F8A011F38C0A4D15800617111F9EDC7D004060A386206A3AACECBDBB22D601853D955FDC5D11ADFBD1AA6A950D966B348C7663D40173714A9F987DF6461BEADFB9CD6419FFDFE4D4CF2EEC1AA605A4F59BDAA50102032620012158200EDB27580389494D74D2373B8F8C2E8B76FA135946D4F30D0E187E120B423349225820E03400D189E85A55DE9AB0F538ED60736EB750F5F0306A80060FE1B13010560D'
            ))  # noqa
        client_param = a2b_hex(
            b'985B6187D042FB1258892ED637CEC88617DDF5F6632351A545617AA2B75261BF'
        )  # noqa

        attestation.verify(statement, auth_data, client_param)

        statement['sig'] = b'a' * len(statement['sig'])
        with self.assertRaises(InvalidSignature):
            attestation.verify(statement, auth_data, client_param)
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"})
Пример #10
0
    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
Пример #11
0
    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",
            )
Пример #12
0
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'})
Пример #13
0
Файл: models.py Проект: g10f/sso
    def authenticate_complete(cls, response_data, state_data, user):
        response = cbor.decode(b64decode(response_data))
        state = signing.loads(state_data, salt=U2FDevice.WEB_AUTHN_SALT)
        credential_id = response["credentialId"]
        client_data = ClientData(response["clientDataJSON"])
        auth_data = AuthenticatorData(response["authenticatorData"])
        signature = response["signature"]

        credentials = U2FDevice.credentials(user)
        cred = cls.server.authenticate_complete(
            state,
            credentials,
            credential_id,
            client_data,
            auth_data,
            signature,
        )
        credential_id = websafe_encode(cred.credential_id)
        device = U2FDevice.objects.get(user=user, credential_id=credential_id)
        if auth_data.counter <= device.counter:
            # verify counter is increasing
            raise ValueError(
                f"login counter is not increasing. {auth_data.counter} <= {device.counter} "
            )
        device.last_used = timezone.now()
        device.counter = auth_data.counter
        device.save(update_fields=["last_used", "counter"])
        return device
Пример #14
0
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'})
Пример #15
0
 def test_parse_bytes_get_assertion(self):
     data = AuthenticatorData(_AUTH_DATA_GA)
     self.assertEqual(data.rp_id_hash, _RP_ID_HASH)
     self.assertEqual(data.flags, 0x01)
     self.assertEqual(data.counter, 29)
     self.assertIsNone(data.credential_data)
     self.assertIsNone(data.extensions)
Пример #16
0
    def test_fido_u2f_attestation(self):
        attestation = Attestation.for_type('fido-u2f')()
        self.assertIsInstance(attestation, FidoU2FAttestation)

        statement = {
            'sig':
            a2b_hex(
                b'30450220324779C68F3380288A1197B6095F7A6EB9B1B1C127F66AE12A99FE8532EC23B9022100E39516AC4D61EE64044D50B415A6A4D4D84BA6D895CB5AB7A1AA7D081DE341FA'
            ),  # noqa
            'x5c': [
                a2b_hex(
                    b'3082024A30820132A0030201020204046C8822300D06092A864886F70D01010B0500302E312C302A0603550403132359756269636F2055324620526F6F742043412053657269616C203435373230303633313020170D3134303830313030303030305A180F32303530303930343030303030305A302C312A302806035504030C2159756269636F205532462045452053657269616C203234393138323332343737303059301306072A8648CE3D020106082A8648CE3D030107034200043CCAB92CCB97287EE8E639437E21FCD6B6F165B2D5A3F3DB131D31C16B742BB476D8D1E99080EB546C9BBDF556E6210FD42785899E78CC589EBE310F6CDB9FF4A33B3039302206092B0601040182C40A020415312E332E362E312E342E312E34313438322E312E323013060B2B0601040182E51C020101040403020430300D06092A864886F70D01010B050003820101009F9B052248BC4CF42CC5991FCAABAC9B651BBE5BDCDC8EF0AD2C1C1FFB36D18715D42E78B249224F92C7E6E7A05C49F0E7E4C881BF2E94F45E4A21833D7456851D0F6C145A29540C874F3092C934B43D222B8962C0F410CEF1DB75892AF116B44A96F5D35ADEA3822FC7146F6004385BCB69B65C99E7EB6919786703C0D8CD41E8F75CCA44AA8AB725AD8E799FF3A8696A6F1B2656E631B1E40183C08FDA53FA4A8F85A05693944AE179A1339D002D15CABD810090EC722EF5DEF9965A371D415D624B68A2707CAD97BCDD1785AF97E258F33DF56A031AA0356D8E8D5EBCADC74E071636C6B110ACE5CC9B90DFEACAE640FF1BB0F1FE5DB4EFF7A95F060733F5'
                )
            ]  # noqa
        }
        auth_data = AuthenticatorData(
            a2b_hex(
                b'1194228DA8FDBDEEFD261BD7B6595CFD70A50D70C6407BCF013DE96D4EFB17DE41000000000000000000000000000000000000000000403EBD89BF77EC509755EE9C2635EFAAAC7B2B9C5CEF1736C3717DA48534C8C6B654D7FF945F50B5CC4E78055BDD396B64F78DA2C5F96200CCD415CD08FE420038A5010203262001215820E87625896EE4E46DC032766E8087962F36DF9DFE8B567F3763015B1990A60E1422582027DE612D66418BDA1950581EBC5C8C1DAD710CB14C22F8C97045F4612FB20C91'
            ))  # noqa
        client_param = a2b_hex(
            b'687134968222EC17202E42505F8ED2B16AE22F16BB05B88C25DB9E602645F141'
        )  # noqa

        attestation.verify(statement, auth_data, client_param)

        statement['sig'] = b'a' * len(statement['sig'])
        with self.assertRaises(InvalidSignature):
            attestation.verify(statement, auth_data, client_param)
Пример #17
0
 def test_parse_bytes_make_credential(self):
     data = AuthenticatorData(_AUTH_DATA_MC)
     self.assertEqual(data.rp_id_hash, _RP_ID_HASH)
     self.assertEqual(data.flags, 0x41)
     self.assertEqual(data.counter, 28)
     self.assertEqual(data.credential_data, _ATT_CRED_DATA)
     self.assertIsNone(data.extensions)
Пример #18
0
    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"}
Пример #19
0
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',
    )
Пример #20
0
 def clean_authenticator_data(self) -> AuthenticatorData:
     """Return decoded authenticator data."""
     value = self.cleaned_data['authenticator_data']
     try:
         return AuthenticatorData(base64.b64decode(value))
     except ValueError:
         raise ValidationError(_('FIDO 2 response is malformed.'),
                               code='invalid')
Пример #21
0
 def authenticate_complete(self, state, data, user):
     auth_data = AuthenticatorData(data['authenticatorData'])
     credential = self.server.authenticate_complete(
         state, credentials=U2fDevice.get_credentials(user),
         credential_id=data['credentialId'],
         client_data=ClientData(data['clientDataJSON']),
         auth_data=auth_data,
         signature=data['signature'])
     return (credential, auth_data)
Пример #22
0
    def sign(self, client_data):
        authenticator_data = AuthenticatorData.create(
            sha256(self.app_id),
            flags=AuthenticatorData.FLAG.USER_PRESENT,
            counter=0)

        signature = self.priv_key.sign(authenticator_data + client_data.hash,
                                       ec.ECDSA(hashes.SHA256()))

        return authenticator_data, signature
Пример #23
0
def _get_assertion_1(seed, rp_id_hash, descriptor, client_data_hash):
    credential_id = descriptor.id
    assert softkey_valid_credential_id(seed, credential_id)
    auth_data = AuthenticatorData.create(
        rp_id_hash=rp_id_hash,
        flags=AuthenticatorData.FLAG.USER_PRESENT,
        counter=0xdeadbeef,
    )
    ed25519priv = softkey_derive_ed25519priv(seed, credential_id)
    signature = ed25519priv.sign(auth_data + client_data_hash)
    return AssertionResponse.create(descriptor, auth_data, signature)
Пример #24
0
    def test_none_attestation(self):
        attestation = Attestation.for_type('none')()
        self.assertIsInstance(attestation, NoneAttestation)

        auth_data = AuthenticatorData(
            a2b_hex(
                b'0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE12410000002BF8A011F38C0A4D15800617111F9EDC7D0040A17370D9C1759005700C8DE77E7DFD3A0A5300E0A26E5213AA40D6DF10EE4028B58B5F34167035D840BEBAE0C5CE8FD05AD9BD33E3BE7D1C558D81AB4803570BA5010203262001215820A5FD5CE1B1C458C530A54FA61B31BF6B04BE8B97AFDE54DD8CBB69275A8A1BE1225820FA3A3231DD9DEED9D1897BE5A6228C59501E4BCD12975D3DFF730F01278EA61C'
            ))  # noqa
        attestation.verify({}, auth_data, b'deadbeef' * 8)

        with self.assertRaises(InvalidData):
            attestation.verify({'not': 'empty'}, auth_data, b'deadbeef' * 8)
Пример #25
0
    def test_none_windows_hello_attestation(self):
        attestation = Attestation.for_type("none")()
        self.assertIsInstance(attestation, NoneAttestation)

        auth_data = AuthenticatorData(
            a2b_hex(
                b"54ce651ed715b4aaa755eecebd4ea0950815b334bd07d109893e963018cddbd945000000006028b017b1d44c02b4b3afcdafc96bb200201decfcd6d6a05c2826d52348afdc70a9800df007845047b1a23706aa6e2f315ca401030339010020590100af59f4ad4f71da800bb91045b267e240e06317f7b2b1d76f78e239a433811faeca58a1869fb00225eb2727f81b6b20cbc18c0ad8d38fa450e8df11b4ad3bc3ee5d13c77ed172fa3af0195ec6ac0c4bac8c950115dfce6d38737cbafefbe117d8401cd56c638043a0d585131bc48a153b17a8dcb96671e15a90ba1b4ff810b138b77ac0a050b039b87b6089dd8dfa45611b992109d554aad3e6b72ac82d801496e4d2d230aa466090bbbf4f5632fe4b588e4f571462378fa6f514a536a5945b223c8d98f730b7cf85de86b98c217090f9e9ebf9643cf3feceeacb837d7a18542e03271cd8c70cf81186cdb63e4cbf4efc0cbbd3c93231b06f19580d0a980264d12143010001"  # noqa
            ))  # noqa
        attestation.verify({}, auth_data, b"deadbeef" * 8)

        with self.assertRaises(InvalidData):
            attestation.verify({"not": "empty"}, auth_data, b"deadbeef" * 8)
Пример #26
0
def authenticate_complete():
    data = cbor.loads(request.get_data())[0]
    credential_id = data['credentialId']
    client_data = ClientData(data['clientDataJSON'])
    auth_data = AuthenticatorData(data['authenticatorData'])
    signature = data['signature']

    credentials = session['userobj']['credentials']
    fidoserver.authenticate_complete(credentials, credential_id,
                                 session.pop('challenge'), client_data,
                                 auth_data, signature)
    return cbor.dumps({'status': 'OK'})
Пример #27
0
    def _make_authenticator_data(
        self, rp_id: str,
        attested_credential_data: Optional[AttestedCredentialData]
    ) -> AuthenticatorData:
        flags = (AuthenticatorData.FLAG.USER_PRESENT
                 | AuthenticatorData.FLAG.USER_VERIFIED)
        if attested_credential_data:
            flags |= AuthenticatorData.FLAG.ATTESTED

        rp_id_hash = sha256(rp_id.encode('utf-8')).digest()
        sig_counter = self._get_timestamp_signature_counter()
        return AuthenticatorData.create(rp_id_hash, flags, sig_counter,
                                        attested_credential_data or b'')
Пример #28
0
def authenticate_complete():
    token = request.cookies.get("token")
    user_identity = current_user.get_id()
    database_id, remember_me = get_current_user_info(token, user_identity)
    if database_id is None:
        abort(401)

    user = User.query.filter_by(did=database_id).first()

    credentials = get_credentials(database_id)
    if not credentials:
        abort(401)

    data = cbor.decode(request.get_data())
    credential_id = data["credentialId"]
    client_data = ClientData(data["clientDataJSON"])
    auth_data = AuthenticatorData(data["authenticatorData"])
    signature = data["signature"]

    server.authenticate_complete(
        session.pop("state"),
        credentials,
        credential_id,
        client_data,
        auth_data,
        signature,
    )

    current_counter = int(auth_data.counter)

    keys = Key.query.filter_by(user_id=database_id).all()

    for key in keys:
        if key.credential_id == credential_id:
            last_counter = key.counter
            if last_counter is None or last_counter >= current_counter:
                # Cloned => untrusted key!
                return cbor.encode({
                    "status": "error",
                    "reason": "invalid counter"
                })
            key.last_access = datetime.utcnow()
            key.counter = current_counter
            break
    db.session.add(key)
    db.session.commit()

    login_user(user, remember=remember_me)

    return cbor.encode({"status": "OK"})
Пример #29
0
 def test_authenticate_invalid_response(self):
     fido2_response = {
         'client_data':
         ClientData(base64.b64decode(AUTHENTICATION_CLIENT_DATA)),
         'credential_id':
         base64.b64decode(CREDENTIAL_ID),
         'authenticator_data':
         AuthenticatorData(base64.b64decode(AUTHENTICATOR_DATA)),
         'signature':
         b'INVALID'
     }
     self.assertIsNone(
         self.backend.authenticate(sentinel.request, self.user, self.server,
                                   self.state, fido2_response))
Пример #30
0
class TestFido2GeneralAuthenticationBackend(TestCase):
    """Test `Fido2GeneralAuthenticationBackend` class."""

    backend = Fido2GeneralAuthenticationBackend()

    server = Fido2Server(PublicKeyCredentialRpEntity(HOSTNAME, HOSTNAME))

    state = {
        'challenge': AUTHENTICATION_CHALLENGE,
        'user_verification': UserVerificationRequirement.PREFERRED
    }
    fido2_response = {
        'client_data':
        ClientData(base64.b64decode(AUTHENTICATION_CLIENT_DATA)),
        'credential_id':
        base64.b64decode(CREDENTIAL_ID),
        'authenticator_data':
        AuthenticatorData(base64.b64decode(AUTHENTICATOR_DATA)),
        'signature':
        base64.b64decode(SIGNATURE)
    }

    def setUp(self):
        self.user = User.objects.create_user(USERNAME, password=PASSWORD)
        self.device = Authenticator.objects.create(
            user=self.user,
            credential_id_data=CREDENTIAL_ID,
            attestation_data=ATTESTATION_OBJECT)

    def test_authenticate(self):
        authenticated_user = self.backend.authenticate(sentinel.request,
                                                       USERNAME, PASSWORD,
                                                       self.server, self.state,
                                                       self.fido2_response)
        self.assertEqual(authenticated_user, self.user)
        self.assertQuerysetEqual(Authenticator.objects.values_list(
            'user', 'counter'), [(self.user.pk, 152)],
                                 transform=tuple)

    def test_authenticate_wrong_password(self):
        authenticated_user = self.backend.authenticate(sentinel.request,
                                                       USERNAME,
                                                       'wrong_password',
                                                       self.server, self.state,
                                                       self.fido2_response)
        self.assertEqual(authenticated_user, None)
        self.assertQuerysetEqual(Authenticator.objects.values_list(
            'user', 'counter'), [(self.user.pk, 0)],
                                 transform=tuple)
Пример #31
0
    def perform_step(self, action):
        current_app.logger.debug('Performing MFA step')
        if current_app.config['MFA_TESTING']:
            current_app.logger.debug('Test mode is on, faking authentication')
            return {
                'success': True,
                'testing': True,
            }

        if action.old_format:
            userid = action.user_id
            user = current_app.central_userdb.get_user_by_id(userid, raise_on_missing=False)
        else:
            eppn = action.eppn
            user = current_app.central_userdb.get_user_by_eppn(eppn, raise_on_missing=False)
        current_app.logger.debug('Loaded User {} from db (in perform_action)'.format(user))

        # Third party service MFA
        if session.mfa_action.success is True:  # Explicit check that success is the boolean True
            issuer = session.mfa_action.issuer
            authn_instant = session.mfa_action.authn_instant
            authn_context = session.mfa_action.authn_context
            current_app.logger.info('User {} logged in using external mfa service {}'.format(user, issuer))
            action.result = {
                'success': True,
                'issuer': issuer,
                'authn_instant': authn_instant,
                'authn_context': authn_context
            }
            current_app.actions_db.update_action(action)
            return action.result

        req_json = request.get_json()
        if not req_json:
            current_app.logger.error('No data in request to authn {}'.format(user))
            raise self.ActionError('mfa.no-request-data')

        # Process POSTed data
        if 'tokenResponse' in req_json:
            # CTAP1/U2F
            token_response = request.get_json().get('tokenResponse', '')
            current_app.logger.debug('U2F token response: {}'.format(token_response))

            challenge = session.get(self.PACKAGE_NAME + '.u2f.challenge')
            current_app.logger.debug('Challenge: {!r}'.format(challenge))

            device, counter, touch = complete_authentication(challenge, token_response,
                                                             current_app.config['U2F_VALID_FACETS'])
            current_app.logger.debug('U2F authentication data: {}'.format({
                'keyHandle': device['keyHandle'],
                'touch': touch,
                'counter': counter,
            }))

            for this in user.credentials.filter(U2F).to_list():
                if this.keyhandle == device['keyHandle']:
                    current_app.logger.info('User {} logged in using U2F token {} (touch: {}, counter {})'.format(
                        user, this, touch, counter))
                    action.result = {'success': True,
                                     'touch': touch,
                                     'counter': counter,
                                     RESULT_CREDENTIAL_KEY_NAME: this.key,
                                     }
                    current_app.actions_db.update_action(action)
                    return action.result
        elif 'authenticatorData' in req_json:
            # CTAP2/Webauthn
            req = {}
            for this in ['credentialId', 'clientDataJSON', 'authenticatorData', 'signature']:
                try:
                    req_json[this] += ('=' * (len(req_json[this]) % 4))
                    req[this] = base64.urlsafe_b64decode(req_json[this])
                except:
                    current_app.logger.error('Failed to find/b64decode Webauthn parameter {}: {}'.format(
                        this, req_json.get(this)))
                    raise self.ActionError('mfa.bad-token-response')  # XXX add bad-token-response to frontend
            current_app.logger.debug('Webauthn request after decoding:\n{}'.format(pprint.pformat(req)))
            client_data = ClientData(req['clientDataJSON'])
            auth_data = AuthenticatorData(req['authenticatorData'])

            credentials = _get_user_credentials(user)
            fido2state = json.loads(session[self.PACKAGE_NAME + '.webauthn.state'])

            rp_id = current_app.config['FIDO2_RP_ID']
            fido2rp = RelyingParty(rp_id, 'eduID')
            fido2server = _get_fido2server(credentials, fido2rp)
            matching_credentials = [(v['webauthn'], k) for k,v in credentials.items()
                                    if v['webauthn'].credential_id == req['credentialId']]

            if not matching_credentials:
                current_app.logger.error('Could not find webauthn credential {} on user {}'.format(
                    req['credentialId'], user))
                raise self.ActionError('mfa.unknown-token')

            authn_cred = fido2server.authenticate_complete(
                fido2state,
                [mc[0] for mc in matching_credentials],
                req['credentialId'],
                client_data,
                auth_data,
                req['signature'],
            )
            current_app.logger.debug('Authenticated Webauthn credential: {}'.format(authn_cred))

            cred_key = [mc[1] for mc in matching_credentials][0]

            touch = auth_data.flags
            counter = auth_data.counter
            current_app.logger.info('User {} logged in using Webauthn token {} (touch: {}, counter {})'.format(
                user, cred_key, touch, counter))
            action.result = {'success': True,
                             'touch': auth_data.is_user_present() or auth_data.is_user_verified(),
                             'user_present': auth_data.is_user_present(),
                             'user_verified': auth_data.is_user_verified(),
                             'counter': counter,
                             RESULT_CREDENTIAL_KEY_NAME: cred_key,
                             }
            current_app.actions_db.update_action(action)
            return action.result

        else:
            current_app.logger.error('Neither U2F nor Webauthn data in request to authn {}'.format(user))
            current_app.logger.debug('Request: {}'.format(req_json))
            raise self.ActionError('mfa.no-token-response')

        raise self.ActionError('mfa.unknown-token')