Пример #1
0
def verify_registration_response(response, challenge, *, rp_id, origin):
    """
    Validates the challenge and attestation information
    sent from the client during device registration.

    Returns a WebAuthnCredential on success.
    Raises RegistrationRejectedError on failire.
    """
    # NOTE: We re-encode the challenge below, because our
    # response's clientData.challenge is encoded twice:
    # first for the entire clientData payload, and then again
    # for the individual challenge.
    encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()
    response = pywebauthn.WebAuthnRegistrationResponse(
        rp_id,
        origin,
        response,
        encoded_challenge,
        self_attestation_permitted=True,
        none_attestation_permitted=True,
    )
    try:
        return response.verify()
    except _RegistrationRejectedError as e:
        raise RegistrationRejectedError(str(e))
Пример #2
0
def webauthn_verify_credential_info(request):
    challenge = request.session["challenge"]
    ukey = request.session["register_ukey"]

    registration_response = request.POST
    trust_anchor_dir = settings.WEBAUTHN_TRUSTED_CERTIFICATES
    trusted_attestation_cert_required = (
        settings.WEBAUTHN_TRUSTED_ATTESTATION_CERT_REQUIRED)
    self_attestation_permitted = settings.WEBAUTHN_SELF_ATTESTATION_PERMITTED
    none_attestation_permitted = settings.WEBAUTHN_NONE_ATTESTATION_PERMITTED

    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        settings.RELYING_PARTY_ID,
        util.get_origin(request),
        registration_response,
        challenge,
        trust_anchor_dir,
        trusted_attestation_cert_required,
        self_attestation_permitted,
        none_attestation_permitted,
        uv_required=False,  # User validation
    )

    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        return JsonResponse(
            {"fail": "Registration failed. Error: {}".format(e)}, status=400)

    # W3C spec. Step 17.
    #
    # Check that the credentialId is not yet registered to any other user.
    # If registration is requested for a credential that is already registered
    # to a different user, the Relying Party SHOULD fail this registration
    # ceremony, or it MAY decide to accept the registration, e.g. while deleting
    # the older registration.
    credential_id_exists = WebAuthnKey.objects.filter(
        credential_id=webauthn_credential.credential_id.decode(
            "utf-8")).first()
    if credential_id_exists:
        return JsonResponse({"fail": "Credential ID already exists."},
                            status=400)

    WebAuthnKey.objects.create(
        user=request.user,
        key_name=request.session.get("key_name", ""),
        ukey=ukey,
        public_key=webauthn_credential.public_key.decode("utf-8"),
        credential_id=webauthn_credential.credential_id.decode("utf-8"),
        sign_count=webauthn_credential.sign_count,
    )

    try:
        del request.session["challenge"]
        del request.session["register_ukey"]
        del request.session["key_name"]
    except KeyError:  # pragma: no cover
        pass

    return JsonResponse({"success": "User successfully registered."})
Пример #3
0
def register():
    challenge = session['challenge']
    user_id = session['user_id']
    username = session['username']

    client_data, att_obj, registration_client_extensions = request.json.get('clientData'), request.json.get('attObj'), request.json.get('registrationClientExtensions')
    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        rp_id=RELYING_PARTY_ID,
        origin=ORIGIN,
        registration_response={'clientData': client_data, 'attObj': att_obj, 'registrationClientExtensions': registration_client_extensions},
        challenge=challenge,
        trusted_attestation_cert_required=True,
    )
    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        return jsonify(status='failure', error=str(e))

    if any(u['credential_id'] == webauthn_credential.credential_id for u in USERS.values()):
        return jsonify(status='failure', error='Credential ID already in use')
    if username in USERS:
        return jsonify(status='failure', error='Username already in use')

    user = {
        'id': user_id,
        'username': username,
        'credential_id': webauthn_credential.credential_id.decode('utf8'),
        'public_key': webauthn_credential.public_key.decode('utf8'),
    }
    USERS[username] = user
    return jsonify(status='success', user=user)
Пример #4
0
def verify_credential_info(request):
    print("verify_credential_info")
    # user = authenticate(request, username=username)
    # global username
    challenge = request.session['challenge']
    username = request.session['register_username']
    display_name = request.session['register_display_name']
    ukey = request.session['register_ukey']
    # user = User.objects.get(username=username)
    # print("user {}".format(user))
    print("challenge {}".format(challenge))
    print("username {}".format(username))
    print("display_name {}".format(display_name))
    print("ukey {}".format(ukey))

    registration_response = request.POST
    trust_anchor_dir = os.path.join(
        os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR)
    trusted_attestation_cert_required = True
    self_attestation_permitted = True
    none_attestation_permitted = True
    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        RP_ID,
        ORIGIN,
        registration_response,
        challenge,
        trust_anchor_dir,
        trusted_attestation_cert_required,
        self_attestation_permitted,
        none_attestation_permitted,
        uv_required=False)
    print(webauthn_registration_response)
    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        return JsonResponse({'fail': 'Registration failed. Error: {}'.format(e)})

    credential_id_exists = User.objects.filter(
        credential_id=webauthn_credential.credential_id).first()
    if credential_id_exists:
        return JsonResponse({'fail': 'Credential ID already exists.'})
    existing_user = User.objects.filter(username=username).first()
    if not existing_user:
        print("create")
        if sys.version_info >= (3, 0):
            webauthn_credential.credential_id = str(
                webauthn_credential.credential_id, "utf-8")
        user = User.objects.create(
            ukey=ukey,
            username=username,
            display_name=username,
            pub_key=webauthn_credential.public_key,
            credential_id=webauthn_credential.credential_id,
            sign_count=webauthn_credential.sign_count,
            rp_id=RP_ID,
            icon_url='https://example.com')
    else:
        return JsonResponse({'fail': 'User already exists.'})
    print('Successfully registered as {}.'.format(username))
    return JsonResponse({'success': 'User successfully registered.'})
Пример #5
0
def register():
    challenge = session['challenge']
    user_id = session['user_id']
    username = session['username']
    password_hash = session['password_hash']
    logger.info(f"Register submitted for user {username} with challenge {challenge}")

    client_data, att_obj, registration_client_extensions = request.json.get('clientData'), request.json.get('attObj'), request.json.get('registrationClientExtensions')
    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        rp_id=RELYING_PARTY_ID,
        origin=ORIGIN,
        registration_response={'clientData': client_data, 'attObj': att_obj, 'registrationClientExtensions': registration_client_extensions},
        challenge=challenge,
        trusted_attestation_cert_required=True,
    )
    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        logger.info(f"Register failed: {e}")
        return jsonify(status='failure', error=str(e))

    logger.info(f"Register completed for user {username}")
    user = {
        'id': user_id,
        'username': username,
        'password_hash': password_hash,
        'credential_id': webauthn_credential.credential_id.decode('utf8'),
        'public_key': webauthn_credential.public_key.decode('utf8'),
    }
    return jsonify(status='success', user=user)
Пример #6
0
    def test_validate_registration(self):
        registration_response = webauthn.WebAuthnRegistrationResponse(
            RP_ID,
            ORIGIN,
            copy(REGISTRATION_RESPONSE_TMPL),
            REGISTRATION_CHALLENGE,
            TRUST_ANCHOR_DIR,
            True,
            True,
            uv_required=False)

        return registration_response.verify()
Пример #7
0
    def test_validate_registration(self):
        registration_response = webauthn.WebAuthnRegistrationResponse(
            self.RP_ID,
            self.ORIGIN,
            copy(self.REGISTRATION_RESPONSE_TMPL),
            self.REGISTRATION_CHALLENGE,
            TRUST_ANCHOR_DIR,
            True,
            True,
            uv_required=False,
            none_attestation_permitted=True,
        )

        return registration_response.verify()
Пример #8
0
    def test_registration_invalid_user_verification(self):
        registration_response = webauthn.WebAuthnRegistrationResponse(
            RP_ID,
            ORIGIN,
            copy(REGISTRATION_RESPONSE_TMPL),
            REGISTRATION_CHALLENGE,
            TRUST_ANCHOR_DIR,
            True,
            True,
            uv_required=True
        )

        with self.assertRaises(webauthn.webauthn.RegistrationRejectedException):
            registration_response.verify()
Пример #9
0
def verify_credential_info():
    name = session['register_username']
    display_name = session['register_display_name']
    ukey = session['register_ukey']
    challenge = session['challenge']
    registration_response = request.form
    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        rp["id"],
        request.host_url[:-1],  # This aims to remove the ending slash
        registration_response,
        challenge,
        self_attestation_permitted=True,
        none_attestation_permitted=True)
    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        return make_response(jsonify({"status": "failed", "msg": str(e)}), 401)
    if PublicKeyCredential.query.filter_by(
            credential_id=webauthn_credential.credential_id).first():
        return make_response(
            jsonify({
                "status": "failed",
                "msg": "Key already exists."
            }), 401)
    if User.query.filter_by(email=name).first():
        return make_response(
            jsonify({
                "status": "failed",
                "msg": "User already exists."
            }), 401)

    webauthn_credential.credential_id = str(webauthn_credential.credential_id,
                                            "utf-8")
    webauthn_credential.public_key = str(webauthn_credential.public_key,
                                         "utf-8")
    user = User(display_name=display_name, email=name)
    db.session.add(user)
    publickey_redential = PublicKeyCredential(
        ukey=ukey,
        credential_id=webauthn_credential.credential_id,
        pub_key=webauthn_credential.public_key,
        rp_id=rp["id"],
        email=name,
        user=user)
    db.session.add(publickey_redential)
    db.session.commit()
    login_user(user)
    return make_response(jsonify({"status": "success"}))
Пример #10
0
def webauthn_end_activate():
    jsonData = request.get_json()
    AuthenticatorAttestationResponse = jsonData['AuthenticatorAttestationResponse']
    clientDataJSON = AuthenticatorAttestationResponse['clientDataJSON']
    clientDataJSON_padding = clientDataJSON.ljust((int)(math.ceil(len(clientDataJSON) / 4)) * 4, '=')
    clientDataJSON = base64.b64decode(clientDataJSON_padding).decode('utf8')
    clientDataJSONparsed = json.loads(clientDataJSON)
    retrievedChallenge = clientDataJSONparsed['challenge']
    try:
        data = database.query_db("select * from PublicKeyCredentialCreationOptions where challenge=?",[retrievedChallenge])[0]
    except:
        return jsonify({"Error:","Could not find challenge"}),500
    trusted_attestation_cert_required = True
    self_attestation_permitted = True
    none_attestation_permitted = True

    registration_response = {'clientData':AuthenticatorAttestationResponse['clientDataJSON'],'attObj':AuthenticatorAttestationResponse['attestationObject']}
    trust_anchor_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR)
    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        RP_ID,
        ORIGIN,
        registration_response,
        data[5],
        trust_anchor_dir,
        trusted_attestation_cert_required,
        self_attestation_permitted,
        none_attestation_permitted,
        uv_required=False)  # User Verification

    try:
        webauthn_credential = webauthn_registration_response.verify()
    except (RuntimeError, TypeError, NameError):
        print(RuntimeError)
        return jsonify({'fail': 'Registration failed. Error: {}'.format(RuntimeError)})
    credential_id=webauthn_credential.credential_id.decode("utf-8")
    duplicatedId = database.query_db("select credential_id from Users where credential_id=?",[credential_id])
    if len(duplicatedId)!=0:
        return jsonify({"Error":"Error with register, try again"}),500

    existing_user = database.query_db("select user_id from Users where username=?",[data[4]])
    if len(existing_user)!=0:
        return jsonify({"Error":"Error with register, try again"}),500
    #Add user to database
    database.insert_db("insert into Users VALUES (?,?,?,?,?,?,?,?)",[data[2],data[4],data[3],webauthn_credential.public_key,credential_id,webauthn_credential.sign_count,'http://localhost',data[1]])
    #Remove from PublicKeyCredentialCreationOptions
    database.delete_db("delete from PublicKeyCredentialCreationOptions where challenge=?",[retrievedChallenge])
    return jsonify({'success': 'User successfully registered.'})
Пример #11
0
def register():
    claims = current_custom_claims()
    if not claims.get("is_registration", False):
        return "", 401

    data = RegisterSchema().load(request.get_json())

    response = webauthn.WebAuthnRegistrationResponse(
        rp_id=app.config["WEBAUTHN_RP_ID"],
        origin=app.config["WEBAUTHN_ORIGIN"],
        registration_response=data,
        challenge=claims["challenge"],
        none_attestation_permitted=True,
    )

    try:
        credential = response.verify()
    except Exception as exc:
        return {"error": str(exc)}, 400

    user = User(
        id=UUID(current_user_id()), email=claims["email"], name=claims["name"],
    )

    authenticator = Authenticator(
        name=data["name"],
        credential_id=str(credential.credential_id, "utf-8"),
        public_key=str(credential.public_key, "utf-8"),
        sign_count=credential.sign_count,
    )

    user.authenticators = [authenticator]
    db.session.add(user)

    try:
        db.session.commit()
    except IntegrityError:
        return "", 409

    return "", 201
Пример #12
0
 def confirm_register(self, request, validated_data):
     validated_data = dict(validated_data)
     challenge = request.session['challenge'].rstrip('=')
     webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
         self._get_rp_id(request),
         self._get_origin(request),
         validated_data['data'],
         challenge,
         'trusted_keys',  # directory
         trusted_attestation_cert_required=False,
         self_attestation_permitted=True,
         none_attestation_permitted=True,
         uv_required=False)  # User Verification
     webauthn_credential = webauthn_registration_response.verify()
     validated_data['data'] = {
         'pub_key': webauthn_credential.public_key.decode('utf-8'),
         'credential_id': webauthn_credential.credential_id.decode('utf-8'),
         'rp_id': self._get_rp_id(request),
         'icon_url': self._get_origin(request),
     }
     validated_data['counter'] = webauthn_credential.sign_count
     return validated_data
Пример #13
0
    def post(self, request, *args, **kwargs):
        if not request.session.get('webauthn_attest'):
            return HttpResponseBadRequest()

        if 'credential' not in request.POST or len(
                request.POST['credential']) > 65536:
            return HttpResponseBadRequest(_('Invalid WebAuthn response'))

        if 'name' not in request.POST or len(request.POST['name']) > 100:
            return HttpResponseBadRequest(_('Invalid name'))

        credential = json.loads(request.POST['credential'])

        response = webauthn.WebAuthnRegistrationResponse(
            rp_id=settings.WEBAUTHN_RP_ID,
            origin='https://' + request.get_host(),
            registration_response=credential['response'],
            challenge=request.session['webauthn_attest'],
            none_attestation_permitted=True,
        )

        try:
            credential = response.verify()
        except Exception as e:
            return HttpResponseBadRequest(str(e))

        model = WebAuthnCredential(
            user=request.profile,
            name=request.POST['name'],
            cred_id=credential.credential_id.decode('ascii'),
            public_key=credential.public_key.decode('ascii'),
            counter=credential.sign_count,
        )
        model.save()

        if not request.profile.is_webauthn_enabled:
            request.profile.is_webauthn_enabled = True
            request.profile.save(update_fields=['is_webauthn_enabled'])
        return HttpResponse('OK')
def verify_register_webauthn_user(request):
    current_site_details = get_current_site(request)
    webauthn_ukey = request.session["webauthn_ukey"]
    registration_response = json.loads(request.POST["signedCredentials"])
    webauthn_challenge = request.session["challenge"]
    origin = request.build_absolute_uri('/').strip("/")
    if DEBUG:  # For angular dev server frontend - need to remove this later
        origin = origin.replace("8000", "4200").replace("http", "https")
    webauthn_verify_challege = webauthn.WebAuthnRegistrationResponse(
        rp_id=current_site_details.domain.split(":")[0],
        origin=origin,
        registration_response=registration_response,
        challenge=webauthn_challenge,
        self_attestation_permitted=True,
        none_attestation_permitted=True,
        uv_required=False)
    try:
        verified_webauthn_credentials = webauthn_verify_challege.verify()
    except Exception as e:
        return JsonResponse({"error": f"Registration failed due to {e}"},
                            status=400)
    django_user = User()
    username = request.session["username"]
    user_display_name = request.session["user_display_name"]
    django_user.username = username
    django_user.save()
    newwebauthnprofile = WebAuthnProfile()
    newwebauthnprofile.user = django_user
    newwebauthnprofile.credential_id = str(
        verified_webauthn_credentials.credential_id, 'utf-8')
    newwebauthnprofile.user_public_key = str(
        verified_webauthn_credentials.public_key, 'utf-8')
    newwebauthnprofile.display_name = user_display_name
    newwebauthnprofile.webauthn_ukey = webauthn_ukey
    newwebauthnprofile.save()
    return JsonResponse(newwebauthnprofile.hackyDict())
Пример #15
0
def verify_registration_credentials():
    '''Verify the credential attestation generated during the registration process'''
    register_info = session['registration']
    challenge = register_info['challenge']
    ukey = register_info['ukey']
    email = register_info['email']
    display_name = register_info['display_name']
    registration_response = request.form

    # Craft the response from the
    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        rp_id=RP_ID,
        origin=ORIGIN,
        registration_response=registration_response,
        challenge=challenge,
        trust_anchor_dir=TRUST_ANCHOR_DIR,
        trusted_attestation_cert_required=True,
        self_attestation_permitted=True,
        none_attestation_permitted=True,
        uv_required=False,  # User Verification
    )

    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        flash(f'Registration failed. Error: {e}', 'error')
        return make_response(jsonify({'redirect': url_for('register')}), 401)

    # Step 17.
    #
    # Check that the credentialId is not yet registered to any other user.
    # If registration is requested for a credential that is already registered
    # to a different user, the Relying Party SHOULD fail this registration
    # ceremony, or it MAY decide to accept the registration, e.g. while deleting
    # the older registration.
    credential_id_exists = User.query.filter_by(
        credential_id=webauthn_credential.credential_id
    ).first()

    if credential_id_exists:
        flash('Credential ID already exists.', 'error')
        return make_response(jsonify({'redirect': url_for('register')}), 401)

    # Ensure the user's email isn't already in use (TODO: refactor into a function for
    # both registration stages)
    if User.query.filter_by(email=email).first():
        flash('Email address is already in use', 'error')
        return make_response(jsonify({'redirect': url_for('register')}), 401)

    # Create a new user
    user = User(
        ukey=ukey,
        email=email,
        display_name=display_name,
        public_key=webauthn_credential.public_key,
        credential_id=str(webauthn_credential.credential_id, "utf-8"),
        sign_count=webauthn_credential.sign_count,
        rp_id=RP_ID,
        icon_url='https://example.com',
    )
    db.session.add(user)
    db.session.commit()

    # Clear registration session info
    session.pop('registration', None)

    flash(f'Successfully registered with email {email}')
    return jsonify({'redirect': url_for('login')})
Пример #16
0
def fido_setup():
    if current_user.fido_enabled():
        flash("You have already registered your security key", "warning")
        return redirect(url_for("dashboard.index"))

    if not current_user.can_use_fido:
        flash(
            "This feature is currently in invitation-only beta. Please send us an email if you want to try",
            "warning",
        )
        return redirect(url_for("dashboard.index"))

    fido_token_form = FidoTokenForm()

    # Handling POST requests
    if fido_token_form.validate_on_submit():
        try:
            sk_assertion = json.loads(fido_token_form.sk_assertion.data)
        except Exception as e:
            flash("Key registration failed. Error: Invalid Payload", "warning")
            return redirect(url_for("dashboard.index"))

        fido_uuid = session["fido_uuid"]
        challenge = session["fido_challenge"]

        fido_reg_response = webauthn.WebAuthnRegistrationResponse(
            RP_ID,
            URL,
            sk_assertion,
            challenge,
            trusted_attestation_cert_required=False,
            none_attestation_permitted=True,
        )

        try:
            fido_credential = fido_reg_response.verify()
        except Exception as e:
            LOG.error(
                f"An error occurred in WebAuthn registration process: {e}")
            flash("Key registration failed.", "warning")
            return redirect(url_for("dashboard.index"))

        current_user.fido_pk = str(fido_credential.public_key, "utf-8")
        current_user.fido_uuid = fido_uuid
        current_user.fido_sign_count = fido_credential.sign_count
        current_user.fido_credential_id = str(fido_credential.credential_id,
                                              "utf-8")
        db.session.commit()

        flash("Security key has been activated", "success")

        return redirect(url_for("dashboard.index"))

    # Prepare information for key registration process
    fido_uuid = str(uuid.uuid4())
    challenge = secrets.token_urlsafe(32)

    credential_create_options = webauthn.WebAuthnMakeCredentialOptions(
        challenge,
        "SimpleLogin",
        RP_ID,
        fido_uuid,
        current_user.email,
        current_user.name if current_user.name else current_user.email,
        False,
        attestation="none",
        user_verification="discouraged",
    )

    # Don't think this one should be used, but it's not configurable by arguments
    # https://www.w3.org/TR/webauthn/#sctn-location-extension
    registration_dict = credential_create_options.registration_dict
    del registration_dict["extensions"]["webauthn.loc"]

    session["fido_uuid"] = fido_uuid
    session["fido_challenge"] = challenge.rstrip("=")

    return render_template(
        "dashboard/fido_setup.html",
        fido_token_form=fido_token_form,
        credential_create_options=registration_dict,
    )
Пример #17
0
def verify_credential_info():
    challenge = session['challenge']
    username = session['register_username']
    display_name = session['register_display_name']
    ukey = session['register_ukey']

    registration_response = request.form
    trust_anchor_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                    TRUST_ANCHOR_DIR)
    trusted_attestation_cert_required = True
    self_attestation_permitted = True
    none_attestation_permitted = True

    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        RP_ID,
        ORIGIN,
        registration_response,
        challenge,
        trust_anchor_dir,
        trusted_attestation_cert_required,
        self_attestation_permitted,
        none_attestation_permitted,
        uv_required=False)  # User Verification

    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        return jsonify({'fail': 'Registration failed. Error: {}'.format(e)})

    # Step 17.
    #
    # Check that the credentialId is not yet registered to any other user.
    # If registration is requested for a credential that is already registered
    # to a different user, the Relying Party SHOULD fail this registration
    # ceremony, or it MAY decide to accept the registration, e.g. while deleting
    # the older registration.
    credential_id_exists = User.query.filter_by(
        credential_id=webauthn_credential.credential_id).first()
    if credential_id_exists:
        return make_response(
            jsonify({'fail': 'Credential ID already exists.'}), 401)

    existing_user = User.query.filter_by(username=username).first()
    if not existing_user:
        if sys.version_info >= (3, 0):
            webauthn_credential.credential_id = str(
                webauthn_credential.credential_id, "utf-8")
            webauthn_credential.public_key = str(
                webauthn_credential.public_key, "utf-8")
        user = User(ukey=ukey,
                    username=username,
                    display_name=display_name,
                    pub_key=webauthn_credential.public_key,
                    credential_id=webauthn_credential.credential_id,
                    sign_count=webauthn_credential.sign_count,
                    rp_id=RP_ID,
                    icon_url='https://example.com')
        db.session.add(user)
        db.session.commit()
    else:
        return make_response(jsonify({'fail': 'User already exists.'}), 401)

    flash('Successfully registered as {}.'.format(username))

    return jsonify({'success': 'User successfully registered.'})
Пример #18
0
    def post(self, request, *args, **kwargs):
        try:
            challenge = self.request.session['webauthn_challenge']
            ukey = self.request.session['webauthn_register_ukey']
            resp = json.loads(self.request.POST.get("token"))
            trust_anchor_dir = os.path.normpath(os.path.join(
                os.path.dirname(os.path.abspath(__file__)),
                '../../static/webauthn_trusted_attestation_roots'  # currently does not exist
            ))
            # We currently do not check attestation certificates, since there's no real risk
            # and we do not have any policies specifying what devices can be used. (Also, we
            # didn't get it to work.)
            # Read more: https://fidoalliance.org/fido-technotes-the-truth-about-attestation/
            trusted_attestation_cert_required = False
            self_attestation_permitted = True
            none_attestation_permitted = True

            webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
                urlparse(settings.SITE_URL).netloc,
                settings.SITE_URL,
                resp,
                challenge,
                trust_anchor_dir,
                trusted_attestation_cert_required,
                self_attestation_permitted,
                none_attestation_permitted,
                uv_required=False
            )
            webauthn_credential = webauthn_registration_response.verify()

            # Check that the credentialId is not yet registered to any other user.
            # If registration is requested for a credential that is already registered
            # to a different user, the Relying Party SHOULD fail this registration
            # ceremony, or it MAY decide to accept the registration, e.g. while deleting
            # the older registration.
            credential_id_exists = WebAuthnDevice.objects.filter(
                credential_id=webauthn_credential.credential_id
            ).first()
            if credential_id_exists:
                messages.error(request, _('This security device is already registered.'))
                return redirect(reverse('control:user.settings.2fa.confirm.webauthn', kwargs={
                    'device': self.device.pk
                }))

            webauthn_credential.credential_id = str(webauthn_credential.credential_id, "utf-8")
            webauthn_credential.public_key = str(webauthn_credential.public_key, "utf-8")

            self.device.credential_id = webauthn_credential.credential_id
            self.device.ukey = ukey
            self.device.pub_key = webauthn_credential.public_key
            self.device.sign_count = webauthn_credential.sign_count
            self.device.rp_id = urlparse(settings.SITE_URL).netloc
            self.device.icon_url = settings.SITE_URL
            self.device.confirmed = True
            self.device.save()
            self.request.user.log_action('pretix.user.settings.2fa.device.added', user=self.request.user, data={
                'id': self.device.pk,
                'devicetype': 'u2f',
                'name': self.device.name,
            })
            notices = [
                _('A new two-factor authentication device has been added to your account.')
            ]
            activate = request.POST.get('activate', '')
            if activate == 'on' and not self.request.user.require_2fa:
                self.request.user.require_2fa = True
                self.request.user.save()
                self.request.user.log_action('pretix.user.settings.2fa.enabled', user=self.request.user)
                notices.append(
                    _('Two-factor authentication has been enabled.')
                )
            self.request.user.send_security_notice(notices)
            self.request.user.update_session_token()
            update_session_auth_hash(self.request, self.request.user)

            note = ''
            if not self.request.user.require_2fa:
                note = ' ' + str(_('Please note that you still need to enable two-factor authentication for your '
                                   'account using the buttons below to make a second factor required for logging '
                                   'into your account.'))
            messages.success(request, str(_('The device has been verified and can now be used.')) + note)
            return redirect(reverse('control:user.settings.2fa'))
        except Exception:
            messages.error(request, _('The registration could not be completed. Please try again.'))
            logger.exception('WebAuthn registration failed')
            return redirect(reverse('control:user.settings.2fa.confirm.webauthn', kwargs={
                'device': self.device.pk
            }))
Пример #19
0
def webauthn_finish_register():
    user = User()
    cookie = None
    
    challenge = session['challenge']
    username = session['register_username']
    display_name = session['register_display_name']
    password = session['register_password']
    ukey = session['register_ukey']

    registration_response = request.form
    trust_anchor_dir = os.path.join(
        os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR)
    trusted_attestation_cert_required = False
    self_attestation_permitted = True
    none_attestation_permitted = True

    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        RP_ID,
        ORIGIN,
        registration_response,
        challenge,
        trust_anchor_dir,
        trusted_attestation_cert_required,
        self_attestation_permitted,
        none_attestation_permitted,
        uv_required=False)  # User Verification

    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        return make_response(
            jsonify({'fail': 'Registration failed. Error: {}'.format(e)}), 401)

    # Step 17.
    #
    # Check that the credentialId is not yet registered to any other user.
    # If registration is requested for a credential that is already registered
    # to a different user, the Relying Party SHOULD fail this registration
    # ceremony, or it MAY decide to accept the registration, e.g. while deleting
    # the older registration.
    if auth.credentialIDExists(webauthn_credential.credential_id):
        return make_response(
            jsonify({'fail': 'Credential ID already exists.'}), 401)

    if sys.version_info >= (3, 0):
        webauthn_credential.credential_id = str(
            webauthn_credential.credential_id, "utf-8")
        webauthn_credential.public_key = str(
            webauthn_credential.public_key, "utf-8")
        
    cookie = user.addRegistration(ukey=ukey,
                                  username=username,
                                  password=password,
                                  display_name=display_name,
                                  pub_key=webauthn_credential.public_key,
                                  credential_id=webauthn_credential.credential_id,
                                  sign_count=webauthn_credential.sign_count,
                                  rp_id=RP_ID,
                                  icon_url='https://localhost:8080')

    if not cookie:
        return make_response(jsonify({'fail': 'User already exists.'}), 401)

    nexturl = request.values.get('nexturl', https_url_for('index'))
    response = make_response(jsonify({'nexturl': nexturl}), 200)

    ## Be careful not to include semicolons in cookie value; see
    ## https://github.com/mitsuhiko/werkzeug/issues/226 for more
    ## details.
    response.set_cookie('PyZoobarLogin', cookie)
    return response
Пример #20
0
def fido_setup():
    if current_user.fido_uuid is not None:
        fidos = Fido.filter_by(uuid=current_user.fido_uuid).all()
    else:
        fidos = []

    fido_token_form = FidoTokenForm()

    # Handling POST requests
    if fido_token_form.validate_on_submit():
        try:
            sk_assertion = json.loads(fido_token_form.sk_assertion.data)
        except Exception:
            flash("Key registration failed. Error: Invalid Payload", "warning")
            return redirect(url_for("dashboard.index"))

        fido_uuid = session["fido_uuid"]
        challenge = session["fido_challenge"]

        fido_reg_response = webauthn.WebAuthnRegistrationResponse(
            RP_ID,
            URL,
            sk_assertion,
            challenge,
            trusted_attestation_cert_required=False,
            none_attestation_permitted=True,
        )

        try:
            fido_credential = fido_reg_response.verify()
        except Exception as e:
            LOG.warning(
                f"An error occurred in WebAuthn registration process: {e}")
            flash("Key registration failed.", "warning")
            return redirect(url_for("dashboard.index"))

        if current_user.fido_uuid is None:
            current_user.fido_uuid = fido_uuid
            db.session.flush()

        Fido.create(
            credential_id=str(fido_credential.credential_id, "utf-8"),
            uuid=fido_uuid,
            public_key=str(fido_credential.public_key, "utf-8"),
            sign_count=fido_credential.sign_count,
            name=fido_token_form.key_name.data,
        )
        db.session.commit()

        LOG.d(
            f"credential_id={str(fido_credential.credential_id, 'utf-8')} added for {fido_uuid}"
        )

        flash("Security key has been activated", "success")
        if not RecoveryCode.query.filter_by(user_id=current_user.id).all():
            return redirect(url_for("dashboard.recovery_code_route"))
        else:
            return redirect(url_for("dashboard.fido_manage"))

    # Prepare information for key registration process
    fido_uuid = (str(uuid.uuid4())
                 if current_user.fido_uuid is None else current_user.fido_uuid)
    challenge = secrets.token_urlsafe(32)

    credential_create_options = webauthn.WebAuthnMakeCredentialOptions(
        challenge,
        "SimpleLogin",
        RP_ID,
        fido_uuid,
        current_user.email,
        current_user.name if current_user.name else current_user.email,
        False,
        attestation="none",
        user_verification="discouraged",
    )

    # Don't think this one should be used, but it's not configurable by arguments
    # https://www.w3.org/TR/webauthn/#sctn-location-extension
    registration_dict = credential_create_options.registration_dict
    del registration_dict["extensions"]["webauthn.loc"]

    # Prevent user from adding duplicated keys
    for fido in fidos:
        registration_dict["excludeCredentials"].append({
            "type":
            "public-key",
            "id":
            fido.credential_id,
            "transports": ["usb", "nfc", "ble", "internal"],
        })

    session["fido_uuid"] = fido_uuid
    session["fido_challenge"] = challenge.rstrip("=")

    return render_template(
        "dashboard/fido_setup.html",
        fido_token_form=fido_token_form,
        credential_create_options=registration_dict,
    )
Пример #21
0
def verify_credential_info(election):
    challenge = session['challenge']
    email = session['email']
    name = session['name']
    ukey = session['register_ukey']

    registration_response = request.form
    trust_anchor_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                    TRUST_ANCHOR_DIR)
    trusted_attestation_cert_required = True
    self_attestation_permitted = True
    none_attestation_permitted = True

    http = request.url.split("://")
    origin = f"{http[0]}://{election}.{ORIGIN}"

    webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
        RP_ID,
        origin,
        registration_response,
        challenge,
        trust_anchor_dir,
        trusted_attestation_cert_required,
        self_attestation_permitted,
        none_attestation_permitted,
        uv_required=False)  # User Verification

    try:
        webauthn_credential = webauthn_registration_response.verify()
    except Exception as e:
        return jsonify({'fail': 'Registration failed. Error: {}'.format(e)})

    credential_id_exists = Authority.query.filter_by(
        credential_id=webauthn_credential.credential_id).first()
    if credential_id_exists:
        return make_response(
            jsonify({'fail': 'Credential ID already exists.'}), 401)

    if not current_user.is_authenticated:
        if Election.query.filter_by(name=election).first():
            return make_response(
                jsonify({
                    'fail':
                    'Attempted to create initial authority for existing election.'
                }), 401)

    existing_user = Authority.query.filter(Election.name == election,
                                           Authority.email == email).first()
    if not existing_user:
        webauthn_credential.credential_id = str(
            webauthn_credential.credential_id, "utf-8")
        # Create the election
        election_data = Election(election)
        # Create the user
        user = Authority(ukey=ukey,
                         email=email,
                         name=name,
                         election=election_data,
                         webauthn=True,
                         pub_key=webauthn_credential.public_key,
                         credential_id=webauthn_credential.credential_id,
                         sign_count=webauthn_credential.sign_count,
                         rp_id=RP_ID,
                         icon_url=origin)
        db.session.add(election_data)
        db.session.add(user)
        db.session.commit()
    else:
        return make_response(jsonify({'fail': 'User already exists.'}), 401)

    login_user(user)

    return jsonify({'success': 'User successfully registered.'})