Beispiel #1
0
    def test_rp_entity(self):
        o = PublicKeyCredentialRpEntity("Example", "example.com")
        self.assertEqual(o, {"id": "example.com", "name": "Example"})
        self.assertEqual(o.id, "example.com")
        self.assertEqual(o.name, "Example")

        with self.assertRaises(TypeError):
            PublicKeyCredentialRpEntity(id="example.com")

        with self.assertRaises(TypeError):
            PublicKeyCredentialRpEntity()
Beispiel #2
0
    def test_update_value(self):
        o = PublicKeyCredentialRpEntity("example.com", "Example")
        self.assertEqual(o, {"id": "example.com", "name": "Example"})
        self.assertEqual(o.id, "example.com")
        self.assertEqual(o.name, "Example")

        o.id = "new-id.com"
        self.assertEqual(o, {"id": "new-id.com", "name": "Example"})
        self.assertEqual(o.id, "new-id.com")

        o["name"] = "New Name"
        self.assertEqual(o, {"id": "new-id.com", "name": "New Name"})
        self.assertEqual(o.name, "New Name")
Beispiel #3
0
    def test_authenticate_complete_invalid_signature(self):
        rp = PublicKeyCredentialRpEntity("example.com", "Example")
        server = Fido2Server(rp)

        state = {
            "challenge": "GAZPACHO!",
            "user_verification": UserVerificationRequirement.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",
            )
Beispiel #4
0
    def test_u2f(self):
        rp = PublicKeyCredentialRpEntity("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": UserVerificationRequirement.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,
        )
Beispiel #5
0
    def test_authenticate_complete_invalid_signature(self):
        rp = PublicKeyCredentialRpEntity("Example", "example.com")
        server = Fido2Server(rp)

        state = {
            "challenge": "GAZPACHO!",
            "user_verification": UserVerificationRequirement.PREFERRED,
        }
        client_data = CollectedClientData.create(
            CollectedClientData.TYPE.GET,
            "GAZPACHO!",
            "https://example.com",
        )
        _AUTH_DATA = bytes.fromhex(
            "A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947010000001D"
        )
        with self.assertRaisesRegex(ValueError, "Invalid signature."):
            server.authenticate_complete(
                state,
                [AttestedCredentialData(_ATT_CRED_DATA)],
                _CRED_ID,
                client_data,
                AuthenticatorData(_AUTH_DATA),
                b"INVALID",
            )
Beispiel #6
0
    def test_u2f(self):
        rp = PublicKeyCredentialRpEntity("Example", "example.com")
        app_id = b"https://example.com"
        server = U2FFido2Server(app_id=app_id.decode("ascii"), rp=rp)

        state = {
            "challenge": "GAZPACHO!",
            "user_verification": UserVerificationRequirement.PREFERRED,
        }
        client_data = CollectedClientData.create(
            CollectedClientData.TYPE.GET,
            "GAZPACHO!",
            "https://example.com",
        )

        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,
        )
Beispiel #7
0
    def test_register_begin_custom_challenge_too_short(self):
        rp = PublicKeyCredentialRpEntity("Example", "example.com")
        server = Fido2Server(rp)

        challenge = b"123456789012345"
        with self.assertRaises(ValueError):
            request, state = server.register_begin(USER, challenge=challenge)
Beispiel #8
0
    def test_u2f_facets(self):
        rp = PublicKeyCredentialRpEntity("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": UserVerificationRequirement.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,
            )
Beispiel #9
0
    def test_register_begin_custom_challenge(self):
        rp = PublicKeyCredentialRpEntity("Example", "example.com")
        server = Fido2Server(rp)

        challenge = b"1234567890123456"
        request, state = server.register_begin(USER, challenge=challenge)

        self.assertEqual(request["publicKey"]["challenge"], challenge)
Beispiel #10
0
    def test_register_begin_rp(self):
        rp = PublicKeyCredentialRpEntity("Example", "example.com")
        server = Fido2Server(rp)

        request, state = server.register_begin(USER)

        self.assertEqual(request["publicKey"]["rp"], {
            "id": "example.com",
            "name": "Example"
        })
Beispiel #11
0
 def setUp(self):
     self.u2f = U2fInterface()
     self.login_as(user=self.user)
     rp = PublicKeyCredentialRpEntity("richardmasentry.ngrok.io", "Sentry")
     self.test_registration_server = Fido2Server(rp, verify_origin=verifiy_origin)
     self.test_authentication_server = U2FFido2Server(
         app_id="http://richardmasentry.ngrok.io/auth/2fa/u2fappid.json",
         rp={"id": "richardmasentry.ngrok.io", "name": "Sentry"},
         verify_u2f_origin=verifiy_origin,
     )
Beispiel #12
0
 def server(self) -> Fido2Server:
     """Return FIDO 2 server instance."""
     rp = PublicKeyCredentialRpEntity(self.get_rp_id(), self.get_rp_name())
     if AttestationVerifier is None:
         return Fido2Server(rp, attestation=self.attestation, attestation_types=self.attestation_types)
     elif self.verify_attestation is None:
         if self.attestation_types is not None:
             warnings.warn('You have defined `attestation_types` but not `verify_attestation`, this means that the '
                           '`attestation_types` setting is being iognored.', DeprecationWarning)
         return Fido2Server(rp, attestation=self.attestation)
     else:
         return Fido2Server(rp, attestation=self.attestation,
                            verify_attestation=self.verify_attestation(self.attestation_types))
Beispiel #13
0
    def test_register_begin_rp_icon(self):
        rp = PublicKeyCredentialRpEntity("example.com", "Example",
                                         "http://example.com/icon.svg")
        server = Fido2Server(rp)

        request, state = server.register_begin(USER)

        data = {
            "id": "example.com",
            "name": "Example",
            "icon": "http://example.com/icon.svg",
        }
        self.assertEqual(request["publicKey"]["rp"], data)
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)
Beispiel #15
0
def test_authenticate():
    """test authentication"""

    device = SoftWebauthnDevice()
    device.cred_init('example.org', b'randomhandle')
    registered_credential = device.cred_as_attested()

    server = Fido2Server(
        PublicKeyCredentialRpEntity('example.org', 'test server'))
    options, state = server.authenticate_begin([registered_credential])
    assertion = device.get(options, 'https://example.org')
    server.authenticate_complete(
        state, [registered_credential], assertion['rawId'],
        ClientData(assertion['response']['clientDataJSON']),
        AuthenticatorData(assertion['response']['authenticatorData']),
        assertion['response']['signature'])
Beispiel #16
0
    def server(self):
        rp_id = getattr(settings, 'OTP_U2F_RP_ID', None)
        rp_name = getattr(settings, 'OTP_U2F_RP_NAME', None)
        app_id = getattr(settings, 'OTP_U2F_APP_ID', None)

        if rp_id is None:
            site = get_current_site(self.request)
            rp_id = site.domain
            rp_name = site.name

        if app_id is None:
            if self.request is not None:
                app_id = self.request.build_absolute_uri('/')[:-1]
            else:
                app_id = f'https://{rp_id}'

        return U2FFido2Server(
            app_id, rp=PublicKeyCredentialRpEntity(rp_id, rp_name),
            attestation='direct')
    def _get_make_credential_request(
        algorithm: int = cose.RS256.ALGORITHM,
        user_id: str = '*****@*****.**',
        user_name: str = 'pasten',
        rp_id: str = 'pasten.com',
        rp_name: str = 'Pasten LTD',
        client_data: Optional[ClientData] = None,
        excluded_cred_ids: Optional[List[bytes]] = None,
    ) -> dict:

        client_data = client_data or ClientData.build(
            typ=WEBAUTHN_TYPE.MAKE_CREDENTIAL,
            origin=rp_id,
            challenge=websafe_encode(b'pasten-challenge'),
        )

        req = {
            CtapMakeCredentialRequest.CLIENT_DATA_HASH_KEY: hashlib.sha256(
                client_data
            ).digest(),
            CtapMakeCredentialRequest.RP_KEY: PublicKeyCredentialRpEntity(
                rp_id, rp_name
            ),
            CtapMakeCredentialRequest.USER_KEY: PublicKeyCredentialUserEntity(
                user_id, user_name
            ),
            CtapMakeCredentialRequest.PUBLIC_KEY_CREDENTIAL_PARAMS_KEY: [
                PublicKeyCredentialParameters(
                    PublicKeyCredentialType.PUBLIC_KEY, algorithm
                )
            ],
        }

        if excluded_cred_ids:
            req[CtapMakeCredentialRequest.EXCLUDE_LIST_KEY] = [
                PublicKeyCredentialDescriptor(
                    PublicKeyCredentialType.PUBLIC_KEY, cred_id
                )
                for cred_id in excluded_cred_ids
            ]

        return req
Beispiel #18
0
def test_register():
    """test registering generated credential"""

    device = SoftWebauthnDevice()

    server = Fido2Server(
        PublicKeyCredentialRpEntity('example.org', 'test server'))
    exclude_credentials = []
    options, state = server.register_begin(
        {
            'id': b'randomhandle',
            'name': 'username',
            'displayName': 'User Name'
        }, exclude_credentials)
    attestation = device.create(options, 'https://example.org')
    auth_data = server.register_complete(
        state, ClientData(attestation['response']['clientDataJSON']),
        AttestationObject(attestation['response']['attestationObject']))

    assert isinstance(auth_data, AuthenticatorData)
Beispiel #19
0
 def create(cls, make_credential_request: dict):
     # noinspection PyProtectedMember
     return CtapMakeCredentialRequest(
         client_data_hash=make_credential_request.get(
             cls.CLIENT_DATA_HASH_KEY),
         rp=PublicKeyCredentialRpEntity._wrap(
             make_credential_request.get(cls.RP_KEY)),
         user=PublicKeyCredentialUserEntity._wrap(
             make_credential_request.get(cls.USER_KEY)),
         public_key_credential_params=PublicKeyCredentialParameters.
         _wrap_list(
             make_credential_request.get(
                 cls.PUBLIC_KEY_CREDENTIAL_PARAMS_KEY)),
         exclude_list=PublicKeyCredentialDescriptor._wrap_list(
             make_credential_request.get(cls.EXCLUDE_LIST_KEY)),
         extensions=make_credential_request.get(cls.EXTENSIONS_KEY),
         options=make_credential_request.get(cls.OPTIONS_KEY),
         pin_auth=make_credential_request.get(cls.PIN_AUTH_KEY),
         pin_protocol=make_credential_request.get(cls.PIN_PROTOCOL_KEY),
     )
Beispiel #20
0
import segno

POST_LOGIN_ROUTES = (
    "hc-checks",
    "hc-details",
    "hc-log",
    "hc-channels",
    "hc-add-slack",
    "hc-add-pushover",
    "hc-add-telegram",
    "hc-project-settings",
    "hc-uncloak",
)

FIDO2_SERVER = Fido2Server(
    PublicKeyCredentialRpEntity(settings.RP_ID, "healthchecks"))


def _allow_redirect(redirect_url):
    if not redirect_url:
        return False

    parsed = urlparse(redirect_url)
    if parsed.netloc:
        # Allow redirects only to relative URLs
        return False

    try:
        match = resolve(parsed.path)
    except Resolver404:
        return False
Beispiel #21
0
class U2fInterface(AuthenticatorInterface):
    type = 3
    interface_id = "u2f"
    configure_button = _("Configure")
    name = _("U2F (Universal 2nd Factor)")
    description = _(
        "Authenticate with a U2F hardware device. This is a "
        "device like a Yubikey or something similar which "
        "supports FIDO's U2F specification. This also requires "
        "a browser which supports this system (like Google "
        "Chrome)."
    )
    allow_multi_enrollment = True
    # rp is a relying party for webauthn, this would be sentry.io for SAAS
    # and the prefix for on-premise / dev environments
    rp_id = urlparse(options.get("system.url-prefix")).hostname
    rp = PublicKeyCredentialRpEntity(rp_id, "Sentry")
    webauthn_registration_server = Fido2Server(rp)

    def __init__(self, authenticator=None, status=EnrollmentStatus.EXISTING):
        super().__init__(authenticator, status)

        self.webauthn_authentication_server = U2FFido2Server(
            app_id=self.u2f_app_id, rp={"id": self.rp_id, "name": "Sentry"}
        )

    @classproperty
    def u2f_app_id(cls):
        rv = options.get("u2f.app-id")
        return rv or absolute_uri(reverse("sentry-u2f-app-id"))

    @classproperty
    def u2f_facets(cls):
        facets = options.get("u2f.facets")
        if not facets:
            return [options.get("system.url-prefix")]
        return [x.rstrip("/") for x in facets]

    @classproperty
    def is_available(cls):
        url_prefix = options.get("system.url-prefix")
        return url_prefix and url_prefix.startswith("https://")

    def _get_kept_devices(self, key):
        def _key_does_not_match(device):
            if type(device["binding"]) == AuthenticatorData:
                return decode_credential_id(device) != key
            else:
                return device["binding"]["keyHandle"] != key

        return [device for device in self.config.get("devices", ()) if _key_does_not_match(device)]

    def generate_new_config(self):
        return {}

    def start_enrollment(self, user, has_webauthn_register):
        if has_webauthn_register:
            credentials = []
            for registeredKey in self.get_u2f_devices():
                if type(registeredKey) == AuthenticatorData:
                    credentials.append(registeredKey.credential_data)
                else:
                    c = create_credential_object(registeredKey)
                    credentials.append(c)

            registration_data, state = self.webauthn_registration_server.register_begin(
                user={
                    "id": user.id.to_bytes(64, byteorder="big"),
                    "name": user.username,
                    "displayName": user.username,
                },
                credentials=credentials,
                # user_verification is where the authenticator verifies that the user is authorized
                # to use the authenticator, this isn't needed for our usecase so set a discouraged
                user_verification="discouraged",
            )
            return cbor.encode(registration_data), state

        return u2f.begin_registration(self.u2f_app_id, self.get_u2f_devices()).data_for_client

    def get_u2f_devices(self):
        rv = []
        for data in self.config.get("devices", ()):
            # XXX: The previous version of python-u2flib-server didn't store
            # the `version` in the device binding. Defaulting to `U2F_V2` here
            # so that we don't break existing u2f registrations.
            if type(data["binding"]) == AuthenticatorData:
                rv.append(data["binding"])
            else:
                data["binding"].setdefault("version", "U2F_V2")
                rv.append(DeviceRegistration(data["binding"]))
        return rv

    def remove_u2f_device(self, key):
        """Removes a U2F device but never removes the last one.  This returns
        False if the last device would be removed.
        """
        devices = self._get_kept_devices(key)

        if devices:
            self.config["devices"] = devices
            return True
        return False

    def get_device_name(self, key):
        for device in self.config.get("devices", ()):
            if type(device["binding"]) == AuthenticatorData:
                if decode_credential_id(device) == key:
                    return device["name"]
            elif device["binding"]["keyHandle"] == key:
                return device["name"]

    def get_registered_devices(self):
        rv = []
        for device in self.config.get("devices", ()):
            if type(device["binding"]) == AuthenticatorData:
                rv.append(
                    {
                        "timestamp": to_datetime(device["ts"]),
                        "name": device["name"],
                        "key_handle": decode_credential_id(device),
                        "app_id": self.rp_id,
                    }
                )
            else:
                rv.append(
                    {
                        "timestamp": to_datetime(device["ts"]),
                        "name": device["name"],
                        "key_handle": device["binding"]["keyHandle"],
                        "app_id": device["binding"]["appId"],
                    }
                )
        rv.sort(key=lambda x: x["name"])
        return rv

    def try_enroll(
        self, enrollment_data, response_data, has_webauthn_register, device_name=None, state=None
    ):
        if has_webauthn_register:
            data = json.loads(response_data)
            client_data = ClientData(websafe_decode(data["response"]["clientDataJSON"]))
            att_obj = base.AttestationObject(websafe_decode(data["response"]["attestationObject"]))
            binding = self.webauthn_registration_server.register_complete(
                state, client_data, att_obj
            )
        else:
            data, cert = u2f.complete_registration(enrollment_data, response_data, self.u2f_facets)
            binding = dict(data)
        devices = self.config.setdefault("devices", [])
        devices.append(
            {"name": device_name or "Security Key", "ts": int(time()), "binding": binding}
        )

    def activate(self, request: Request, is_webauthn_signin_ff_enabled):
        if not is_webauthn_signin_ff_enabled:
            challenge = dict(u2f.begin_authentication(self.u2f_app_id, self.get_u2f_devices()))
            # XXX: Upgrading python-u2flib-server to 5.0.0 changes the response
            # format. Our current js u2f library expects the old format, so
            # massaging the data to include the old `authenticateRequests` key here.

            authenticate_requests = []
            for registered_key in challenge["registeredKeys"]:
                authenticate_requests.append(
                    {
                        "challenge": challenge["challenge"],
                        "version": registered_key["version"],
                        "keyHandle": registered_key["keyHandle"],
                        "appId": registered_key["appId"],
                    }
                )
            challenge["authenticateRequests"] = authenticate_requests

            return ActivationChallengeResult(challenge=challenge)

        credentials = []

        for device in self.get_u2f_devices():
            if type(device) == AuthenticatorData:
                credentials.append(device.credential_data)
            else:
                credentials.append(create_credential_object(device))
        challenge, state = self.webauthn_authentication_server.authenticate_begin(
            credentials=credentials
        )
        request.session["webauthn_authentication_state"] = state

        return ActivationChallengeResult(challenge=cbor.encode(challenge["publicKey"]))

    def validate_response(self, request: Request, challenge, response, has_webauthn_register):
        try:
            if not has_webauthn_register:
                u2f.complete_authentication(challenge, response, self.u2f_facets)
                return True
            credentials = []
            for device in self.get_u2f_devices():
                if type(device) == AuthenticatorData:
                    credentials.append(device.credential_data)
                else:
                    credentials.append(create_credential_object(device))
            self.webauthn_authentication_server.authenticate_complete(
                state=request.session["webauthn_authentication_state"],
                credentials=credentials,
                credential_id=websafe_decode(response["keyHandle"]),
                client_data=ClientData(websafe_decode(response["clientData"])),
                auth_data=AuthenticatorData(websafe_decode(response["authenticatorData"])),
                signature=websafe_decode(response["signatureData"]),
            )
        except (InvalidSignature, InvalidKey, StopIteration):
            return False
        return True
Beispiel #22
0
def make_fido2_server() -> Fido2Server:
    rp_id = request.host
    logger.debug("Using %r as relaying party ID", rp_id)
    return Fido2Server(PublicKeyCredentialRpEntity(rp_id, "Checkmk"))
Beispiel #23
0
    PublicKeyCredentialRpEntity,
    AttestationObject,
    AuthenticatorData,
)
from fido2.server import U2FFido2Server
from fido2.ctap1 import RegistrationData
from fido2.utils import sha256, websafe_encode, websafe_decode
from fido2 import cbor
from flask import Flask, session, request, redirect, abort

import os

app = Flask(__name__, static_url_path="")
app.secret_key = os.urandom(32)  # Used for session.

rp = PublicKeyCredentialRpEntity(name="Demo server", id="localhost")
# By using the U2FFido2Server class, we can support existing credentials
# registered by the legacy u2f.register API for an appId.
server = U2FFido2Server("https://*****:*****@app.route("/")
def index():
    return redirect("/index-u2f.html")


@app.route("/api/register/begin", methods=["POST"])
Beispiel #24
0
 def __init__(self, fido2Origin, fido2Name):
     rp = PublicKeyCredentialRpEntity(fido2Origin, fido2Name)
     self.server = Fido2Server(rp, verify_origin=verify_origin_for_rp_openvpn)
     self.credentials = {}
     self.credsfile = 'creds.pickle'
     self.loadCredentials(self.credsfile)
Beispiel #25
0
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET, require_POST

from fido2.webauthn import PublicKeyCredentialRpEntity
from fido2.client import ClientData
from fido2.server import Fido2Server
from fido2.ctap2 import AttestationObject, AuthenticatorData
from fido2 import cbor

from .models import FIDOCredential

if hasattr(settings, 'FIDO2_APP_ID'):
    appId = settings.FIDO2_APP_ID
else:
    appId = "localhost"
rp = PublicKeyCredentialRpEntity(appId, "Ledger Web")
fido_server = Fido2Server(rp)


class LoginView(auth_views.LoginView):
    def post(self, request):
        user = authenticate(request=request,
                            username=request.POST['username'],
                            password=request.POST['password'])
        if user is not None:
            fido_credentials = user.fidocredential_set.count()
            if fido_credentials > 0:
                request.session['user'] = user.id
                return render(
                    request,
                    'registration/login-fido.html',
Beispiel #26
0
 def server(self) -> Fido2Server:
     """Return FIDO 2 server instance."""
     rp = PublicKeyCredentialRpEntity(self.get_rp_id(), self.get_rp_name())
     return Fido2Server(rp,
                        attestation=self.attestation,
                        attestation_types=self.attestation_types)
Beispiel #27
0
from .db.schema import User, WebAuthnEntry

# the api router which will be later registered in the main file
router = APIRouter()

# the cookie used to store the session token
cookie_sec = APIKeyCookie(name="session")

# Used to store the state while doing webauthn
webauthn_state = APIKeyCookie(name="_state", auto_error=True)

# argon2 password hasher and verifier
ph = PasswordHasher()

# webauthn
rp = PublicKeyCredentialRpEntity(config.WEBAUTHN_RP_ID, config.WEBAUTHN_RP_NAME)
fido2server = Fido2Server(rp)


# on get render the html page
@router.get("/login", response_class=HTMLResponse)
async def view_login_page(request: Request):
    return main.templates.TemplateResponse("login_form.html", {"request": request})


# on post try to log the user in
@router.post("/login", response_class=HTMLResponse)
async def login(
    request: Request, email: EmailStr = Form(...), password: SecretStr = Form(...)
):
    successful = False
class TestFido2AuthenticationBackend(TestCase):
    """Test `Fido2AuthenticationBackend` class."""

    backend = Fido2AuthenticationBackend()

    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)
        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,
                                                       self.user, 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_counter(self):
        self.device.counter = 160
        self.device.save()
        request = RequestFactory().get('/dummy/')
        request._messages = CookieStorage(request)

        self.assertRaisesMessage(PermissionDenied, "Counter didn't increase.",
                                 self.backend.authenticate, request, self.user,
                                 self.server, self.state, self.fido2_response)

        self.assertQuerysetEqual(Authenticator.objects.values_list(
            'user', 'counter'), [(self.user.pk, 160)],
                                 transform=tuple)

    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))

    def test_mark_device_used(self):
        self.backend.mark_device_used(self.device, 42)

        self.assertQuerysetEqual(Authenticator.objects.values_list(
            'user', 'counter'), [(self.user.pk, 42)],
                                 transform=tuple)

    def test_mark_device_used_equal(self):
        # Test device returned the same counter.
        self.device.counter = 42
        self.device.save()

        self.assertRaisesMessage(ValueError, "Counter didn't increase.",
                                 self.backend.mark_device_used, self.device,
                                 42)

        self.assertQuerysetEqual(Authenticator.objects.values_list(
            'user', 'counter'), [(self.user.pk, 42)],
                                 transform=tuple)

    def test_mark_device_used_decrease(self):
        # Test device returned lower counter.
        self.device.counter = 42
        self.device.save()

        self.assertRaisesMessage(ValueError, "Counter didn't increase.",
                                 self.backend.mark_device_used, self.device,
                                 41)

        self.assertQuerysetEqual(Authenticator.objects.values_list(
            'user', 'counter'), [(self.user.pk, 42)],
                                 transform=tuple)

    def test_get_user(self):
        self.assertEqual(self.backend.get_user(self.user.pk), self.user)

    def test_get_user_unknown(self):
        self.assertIsNone(self.backend.get_user(42))
from fido2 import cbor
from fido2.client import ClientData
from fido2.ctap2 import AttestedCredentialData, AuthenticatorData
from fido2.server import U2FFido2Server
from fido2.utils import websafe_decode
from fido2.webauthn import PublicKeyCredentialRpEntity

from assemblyline.common.security import get_totp_token
from assemblyline_ui.config import config, APP_ID
from assemblyline_ui.http_exceptions import AuthenticationException

rp = PublicKeyCredentialRpEntity(config.ui.fqdn, "Assemblyline server")
server = U2FFido2Server(f"https://{config.ui.fqdn}", rp)


# noinspection PyBroadException
def validate_2fa(username, otp_token, state, webauthn_auth_resp, storage):
    security_token_enabled = False
    otp_enabled = False
    security_token_error = False
    otp_error = False
    report_errors = False

    # Get user
    user_data = storage.user.get(username)

    # Test Security Tokens
    if config.auth.allow_security_tokens:
        security_tokens = user_data.security_tokens

        credentials = [AttestedCredentialData(websafe_decode(x)) for x in security_tokens.values()]
Beispiel #30
0
from fido2 import cbor
from fido2.client import ClientData
from fido2.ctap2 import AttestationObject, AuthenticatorData, AttestedCredentialData
from fido2.server import Fido2Server
from fido2.webauthn import PublicKeyCredentialRpEntity
from flask import request, session, flash
from flask_login import current_user, login_required, login_user
from flask_socketio import emit
from peewee import DoesNotExist

from home.settings import BASE_URL
from home.web import app, send_message, socketio, ws_login_required
from home.web.models import FIDOToken, User

rp = PublicKeyCredentialRpEntity(
    re.match(r'https?:\/\/([a-zA-Z0-9.-]+)(?::[0-9]{1,5})?\/.*', BASE_URL)[1],
    "Home server")
server = Fido2Server(rp)


def get_all_tokens(user):
    return [AttestedCredentialData(token.data) for token in user.fido_tokens]

@app.route("/api/user/fido2/register", methods=["POST"])
@login_required
def register_begin():
    registration_data, state = server.register_begin(
        {
            "id": str(current_user.id).encode(),
            "name": current_user.username,
            "displayName": current_user.username,