Esempio n. 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()
Esempio n. 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")
Esempio n. 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",
            )
Esempio n. 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,
        )
Esempio n. 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",
            )
Esempio n. 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,
        )
Esempio n. 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)
Esempio n. 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,
            )
Esempio n. 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)
Esempio n. 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"
        })
Esempio n. 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,
     )
Esempio n. 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))
Esempio n. 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)
Esempio n. 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'])
Esempio n. 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
Esempio n. 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)
Esempio n. 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),
     )
Esempio n. 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
Esempio n. 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
Esempio n. 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"))
Esempio n. 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"])
Esempio n. 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)
Esempio n. 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',
Esempio n. 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)
Esempio n. 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()]
Esempio n. 30
0
File: fido2.py Progetto: k3an3/home
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,