Пример #1
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))
Пример #2
0
def fido2_api_register_begin(request):
    rp = PublicKeyCredentialRpEntity(get_domain(request),
                                     settings.FIDO2_RP_NAME)
    fido2 = Fido2Server(rp)

    all_devices = Fido2Device.objects.filter(user=request.user)

    registration_data, state = fido2.register_begin(
        {
            "id": request.user.email.encode(),
            "name": request.user.email,
            "displayName": request.user.email,
            "icon": "",
        },
        # Pass existing fido2 credentials to prevent duplicate
        credentials=[
            AttestedCredentialData(cbor2.loads(d.authenticator_data))
            for d in all_devices
        ],
        user_verification="discouraged",
        authenticator_attachment="cross-platform",
    )

    request.session[FIDO2_REGISTER_STATE] = state

    return HttpResponse(cbor2.dumps(registration_data),
                        content_type="application/octet-stream")
Пример #3
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",
            )
Пример #4
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)
Пример #5
0
    def verify_token(self, token):
        data = cbor2.loads(base64.b64decode(token["token"]))
        credential_id = data["credentialId"]
        client_data = ClientData(data["clientDataJSON"])
        auth_data = AuthenticatorData(data["authenticatorData"])
        signature = data["signature"]
        state = token["state"]
        domain = token["domain"]

        credentials_query = Fido2Device.objects.filter(user=self.user)
        credentials = [
            AttestedCredentialData(cbor2.loads(c.authenticator_data))
            for c in credentials_query
        ]

        rp = PublicKeyCredentialRpEntity(domain, settings.FIDO2_RP_NAME)
        fido2 = Fido2Server(rp)

        try:
            fido2.authenticate_complete(
                state,
                credentials,
                credential_id,
                client_data,
                auth_data,
                signature,
            )

            return True
        except ValueError:
            logger.exception("Error in FIDO2 final authentication")
            return False
Пример #6
0
    def test_authenticate_complete_invalid_signature(self):
        rp = RelyingParty("example.com", "Example")
        server = Fido2Server(rp)

        state = {
            "challenge": "GAZPACHO!",
            "user_verification": USER_VERIFICATION.PREFERRED,
        }
        client_data_dict = {
            "challenge": "GAZPACHO!",
            "origin": "https://example.com",
            "type": WEBAUTHN_TYPE.GET_ASSERTION,
        }
        client_data = ClientData(json.dumps(client_data_dict).encode("utf-8"))
        _AUTH_DATA = a2b_hex(
            "A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947010000001D"
        )
        with six.assertRaisesRegex(self, ValueError, "Invalid signature."):
            server.authenticate_complete(
                state,
                [AttestedCredentialData(_ATT_CRED_DATA)],
                _CRED_ID,
                client_data,
                AuthenticatorData(_AUTH_DATA),
                b"INVALID",
            )
Пример #7
0
    def test_register_begin_custom_challenge_too_short(self):
        rp = RelyingParty("example.com", "Example")
        server = Fido2Server(rp)

        challenge = b"123456789012345"
        with self.assertRaises(ValueError):
            request, state = server.register_begin({}, challenge=challenge)
Пример #8
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)
Пример #9
0
    def test_register_begin_custom_challenge(self):
        rp = RelyingParty("example.com", "Example")
        server = Fido2Server(rp)

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

        self.assertEqual(request["publicKey"]["challenge"], challenge)
Пример #10
0
    def test_register_begin_rp_no_icon(self):
        rp = RelyingParty('example.com', 'Example')
        server = Fido2Server(rp)

        request, state = server.register_begin({})

        self.assertEqual(request['publicKey']['rp'],
                         {'id': 'example.com', 'name': 'Example'})
Пример #11
0
    def test_register_begin_rp_no_icon(self):
        rp = RelyingParty("example.com", "Example")
        server = Fido2Server(rp)

        request, state = server.register_begin({})

        self.assertEqual(
            request["publicKey"]["rp"], {"id": "example.com", "name": "Example"}
        )
Пример #12
0
def main():
    server = Fido2Server({
        "id": "example.com",
        "name": "Example RP"
    },
                         attestation="direct")
    client = get_client()
    # add_credentials(server, client)
    authenticate_device(server, client)
Пример #13
0
    def __init__(self, params):
        """
        Create an instance of the class.
        :param params: Dictionary containing the following parameters
        db_path: Path to the user database
        rp_id: Relying Party identifier of the server.
        pre_share_eph_username: Indicates whether or not the server
                tries to establish an ephemeral user name for the next handshake
        db_encryption_key: The key the database is encrypted with.
        modes: List of allowed FIDO2 operation modes
            during authentication.
        force_fido2: Flag indicating whether or not to accept only FIDO2
            authenticated users.
        """
        # check arguments
        self._valid = False
        db_path = rp_id = encryption_key = None
        pre_share_eph_username = force_fido2 = False
        modes = FIDO2Mode.all
        if 'db_path' in params and isinstance(params['db_path'], str):
            db_path = params['db_path']
        if 'rp_id' in params and isinstance(params['rp_id'], str):
            rp_id = params['rp_id'].lower()
            if not is_valid_hostname(rp_id):
                rp_id = None
        if 'db_encryption_key' in params and \
                isinstance(params['db_encryption_key'], RSAKey):
            encryption_key = params['db_encryption_key']
        if 'pre_share_eph_user_name' in params and \
                isinstance(params['pre_share_eph_user_name'], bool):
            pre_share_eph_username = params['pre_share_eph_user_name']
        if 'modes' in params and \
                isinstance(params['modes'], list):
            modes = params['modes']
        if 'force_fido2' in params and isinstance(params['force_fido2'], bool):
            force_fido2 = params['force_fido2']

        # check if mandatory arguments are set
        if not db_path or not rp_id:
            return

        self.state = ServerState.init
        self.mode = None
        self.allowed_modes = modes

        self._db_connection = self._get_db_connection(db_path, encryption_key)
        relying_party = RelyingParty(rp_id)
        self._server = Fido2Server(relying_party)
        self.pre_share_eph_user_name = pre_share_eph_username
        self.force_fido2 = force_fido2

        self._auth_state = None
        self._user_id = None
        self._eph_user_name_server_share = None
        self._allow_credentials = []

        self._valid = bool(self._db_connection is not None)
Пример #14
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"
        })
Пример #15
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,
     )
Пример #16
0
    def test_register_begin_rp_icon(self):
        rp = RelyingParty('example.com', 'Example',
                          'http://example.com/icon.svg')
        server = Fido2Server(rp)

        request, state = server.register_begin({})

        data = {'id': 'example.com', 'name': 'Example',
                'icon': 'http://example.com/icon.svg'}
        self.assertEqual(request['publicKey']['rp'], data)
Пример #17
0
def _get_fido2server(credentials, fido2rp):
    # See if any of the credentials is a legacy U2F credential with an app-id
    # (assume all app-ids are the same - authenticating with a mix of different
    # app-ids isn't supported in current Webauthn)
    app_id = None
    for k, v in credentials.items():
        if v['app_id']:
            app_id = v['app_id']
            break
    if app_id:
        return U2FFido2Server(app_id, fido2rp)
    return Fido2Server(fido2rp)
Пример #18
0
    def test_register_begin_rp_icon(self):
        rp = RelyingParty("example.com", "Example", "http://example.com/icon.svg")
        server = Fido2Server(rp)

        request, state = server.register_begin({})

        data = {
            "id": "example.com",
            "name": "Example",
            "icon": "http://example.com/icon.svg",
        }
        self.assertEqual(request["publicKey"]["rp"], data)
Пример #19
0
def begin(request):
    rp_host = urlparse(request.build_absolute_uri()).hostname
    rp = RelyingParty(rp_host, 'Demo server')
    server = Fido2Server(rp)

    existing_credentials = AttestedCredentialData.objects.filter(
        user=request.user).all()
    auth_data, state = server.authenticate_begin(existing_credentials)
    request.session['state'] = {
        'challenge': state['challenge'],
        'user_verification': state['user_verification'].value,
    }
    return Response(auth_data, content_type="application/cbor")
Пример #20
0
def do_register_user(user, rp_id, resident_key=False):
    """
    FIDO2 registration process
    :param user: The user to register
    :param rp_id: Relying Party identifier
    :param resident_key: Boolean indicating whether or not to store a
    resident key
    :return: Newly created credentials
    """
    # begin registration
    relying_part = RelyingParty(rp_id)
    server = Fido2Server(relying_part)

    registration_data, state = server.register_begin(user)

    # make credential
    dev = next(CtapHidDevice.list_devices(), None)
    if not dev:
        print('No FIDO device found')
        sys.exit(1)

    client = Fido2Client(dev, 'https://' + rp_id)
    rp = {'id': rp_id, 'name': rp_id}
    challenge = websafe_encode(registration_data['publicKey']['challenge'])

    if resident_key:
        user['name'] = "."
        user_string = "(id: {0})".format(user['id'].hex())
    else:
        user_string = "(name: {0}, display name: {1})".format(
            user['name'], user['displayName'])

    print("\nRegistration request for user: "******"From service: (Address: {0}, Name: {1})".format(rp['id'],
                                                           rp['name']))
    print('Touch your authenticator device now to consent to registration...\n')
    try:
        attestation_object, client_data = client.make_credential(
            rp, user, challenge, rk=resident_key)
    except Exception as e:
        print("Registration failed")
        raise e

    # complete registration
    registration_data = server.register_complete(state, client_data,
                                                 attestation_object)
    credential = registration_data.credential_data
    print("Registration complete")

    return credential
Пример #21
0
class TestFido2GeneralAuthenticationBackend(TestCase):
    """Test `Fido2GeneralAuthenticationBackend` class."""

    backend = Fido2GeneralAuthenticationBackend()

    server = Fido2Server(PublicKeyCredentialRpEntity(HOSTNAME, HOSTNAME))

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

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

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

    def test_authenticate_wrong_password(self):
        authenticated_user = self.backend.authenticate(sentinel.request,
                                                       USERNAME,
                                                       'wrong_password',
                                                       self.server, self.state,
                                                       self.fido2_response)
        self.assertEqual(authenticated_user, None)
        self.assertQuerysetEqual(Authenticator.objects.values_list(
            'user', 'counter'), [(self.user.pk, 0)],
                                 transform=tuple)
Пример #22
0
    def test_authenticate_complete_invalid_signature(self):
        rp = RelyingParty('example.com', 'Example')
        server = Fido2Server(rp)

        state = {'challenge': 'GAZPACHO!',
                 'user_verification': USER_VERIFICATION.PREFERRED}
        client_data_dict = {'challenge': 'GAZPACHO!',
                            'origin': 'https://example.com',
                            'type': WEBAUTHN_TYPE.GET_ASSERTION}
        client_data = ClientData(json.dumps(client_data_dict).encode('utf-8'))
        _AUTH_DATA = a2b_hex('A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947010000001D')  # noqa
        with six.assertRaisesRegex(self, ValueError, 'Invalid signature.'):
            server.authenticate_complete(
                state, [AttestedCredentialData(_ATT_CRED_DATA)], _CRED_ID,
                client_data, AuthenticatorData(_AUTH_DATA), b'INVALID')
Пример #23
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'])
Пример #24
0
def fido2_api_login_begin(request):
    user = request.user
    credentials_query = Fido2Device.objects.filter(user=user)
    credentials = [
        AttestedCredentialData(cbor2.loads(c.authenticator_data))
        for c in credentials_query
    ]

    rp = PublicKeyCredentialRpEntity(get_domain(request),
                                     settings.FIDO2_RP_NAME)
    fido2 = Fido2Server(rp)
    auth_data, state = fido2.authenticate_begin(
        credentials, user_verification="discouraged")

    request.session["fido2_state"] = state
    request.session["fido2_domain"] = get_domain(request)
    return HttpResponse(cbor2.dumps(auth_data),
                        content_type="application/cbor")
Пример #25
0
def authenticate(request):
    rp_host = urlparse(request.build_absolute_uri()).hostname
    rp = RelyingParty(rp_host, 'Demo server')
    server = Fido2Server(rp)

    data = request.data[0]
    credential_id = data['credentialId']
    credentials = AttestedCredentialData.objects.filter(
        user=request.user, ).all()
    client_data = ClientData(data['clientDataJSON'])
    auth_data = AuthenticatorData(data['authenticatorData'])
    signature = data['signature']

    state = request.session['state']

    cred = server.authenticate_complete(state, credentials, credential_id,
                                        client_data, auth_data, signature)
    return cred
Пример #26
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)
Пример #27
0
def fido2_api_register_finish(request):
    data = cbor2.loads(request.body)
    client_data = ClientData(data["clientDataJSON"])
    att_obj = AttestationObject(data["attestationObject"])

    rp = PublicKeyCredentialRpEntity(get_domain(request),
                                     settings.FIDO2_RP_NAME)
    fido2 = Fido2Server(rp)
    auth_data = fido2.register_complete(request.session[FIDO2_REGISTER_STATE],
                                        client_data, att_obj)

    device = Fido2Device(
        authenticator_data=cbor2.dumps(auth_data.credential_data))
    device.user = request.user
    device.name = data["name"] or "Fido key"
    device.confirmed = True
    device.save()

    return HttpResponse(cbor2.dumps({"status": "OK"}),
                        content_type="application/cbor")
Пример #28
0
def complete(request):
    rp_host = urlparse(request.build_absolute_uri()).hostname
    rp = RelyingParty(rp_host, 'Demo server')
    server = Fido2Server(rp)

    data = request.data[0]
    client_data = ClientData(data['clientDataJSON'])
    att_obj = AttestationObject(data['attestationObject'])

    state = request.session['state']

    auth_data = server.register_complete(state, client_data, att_obj)

    cred = AttestedCredentialData.objects.create(
        aaguid=auth_data.credential_data.aaguid,
        credential_id=auth_data.credential_data.credential_id,
        public_key=cbor.dump_dict(auth_data.credential_data.public_key),
        user=request.user,
    )

    verify(request, cred, backend='apps.fido.auth.backends.FIDO2Backend')
    return Response({'status': 'OK'})
Пример #29
0
    # Set up a FIDO 2 client using the origin https://example.com
    client = Fido2Client(dev, "https://example.com")

    # Prefer UV if supported
    if client.info.options.get("uv"):
        uv = "preferred"
        print("Authenticator supports User Verification")
    elif client.info.options.get("clientPin"):
        # Prompt for PIN if needed
        pin = getpass("Please enter PIN: ")
    else:
        print("PIN not set, won't use")

server = Fido2Server({
    "id": "example.com",
    "name": "Example RP"
},
                     attestation="direct")

user = {"id": b"user_id", "name": "A. User"}

# Prepare parameters for makeCredential
create_options, state = server.register_begin(
    user,
    resident_key=True,
    user_verification=uv,
    authenticator_attachment="cross-platform",
)

# Create a credential
if use_prompt:
Пример #30
0
def getServer():
    rp = RelyingParty(settings.FIDO_SERVER_ID, settings.FIDO_SERVER_NAME)
    return Fido2Server(rp)