Example #1
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,
        )
Example #2
0
    def test_u2f(self):
        rp = RelyingParty('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': 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'))

        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)
Example #3
0
    def test_u2f(self):
        rp = RelyingParty("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": 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"))

        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,
        )
Example #4
0
    def test_u2f_facets(self):
        rp = RelyingParty("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": USER_VERIFICATION.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,
            )
Example #5
0
    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"
            })
Example #6
0
    def test_u2f_facets(self):
        rp = PublicKeyCredentialRpEntity("Example", "example.com")
        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 = CollectedClientData.create(
            CollectedClientData.TYPE.GET,
            "GAZPACHO!",
            "https://oauth.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,
        )

        # Now with something not whitelisted
        client_data = CollectedClientData.create(
            CollectedClientData.TYPE.GET,
            "GAZPACHO!",
            "https://publicthingy.example.com",
        )

        authenticator_data, signature = device.sign(client_data)

        with self.assertRaisesRegex(ValueError,
                                    "Invalid origin in CollectedClientData."):
            server.authenticate_complete(
                state,
                [auth_data],
                device.credential_id,
                client_data,
                authenticator_data,
                signature,
            )
Example #7
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,
     )
Example #8
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)
Example #9
0
    def test_u2f_facets(self):
        rp = RelyingParty('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': USER_VERIFICATION.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)
Example #10
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')
Example #11
0
from fido2.server import U2FFido2Server, RelyingParty
from fido2.ctap2 import AttestationObject, AuthenticatorData
from fido2.ctap1 import RegistrationData
from fido2.utils import sha256, websafe_encode
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 = RelyingParty('localhost', 'Demo server')
# 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'])
def register_begin():
    registration_data, state = server.register_begin(
        {
Example #12
0
    def do_POST(self):
        origin = self.headers.get('Origin')
        host = origin[len('https://'):]

        rp = PublicKeyCredentialRpEntity(host, 'NGINX Auth Server')
        server = U2FFido2Server(origin, rp)

        if self.path == HTTP_PREFIX + "/get_challenge_for_new_key":
            registration_data, state = server.register_begin({ 'id': b'default', 'name': "Default user", 'displayName': "Default user" })
            registration_data["publicKey"]["challenge"] = str(base64.b64encode(registration_data["publicKey"]["challenge"]), 'utf-8')
            registration_data["publicKey"]["user"]["id"] = str(base64.b64encode(registration_data["publicKey"]["user"]["id"]), 'utf-8')

            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            # Save this challenge to a file so you can kill the host to add the lient via CLI
            with open(LASTCHALLENGE, 'w') as f:
                f.write(json.dumps(state))
            self.wfile.write(bytes(json.dumps(registration_data), 'UTF-8'))
            return

        if self.path == HTTP_PREFIX + "/register":    
            self.send_response(401)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(bytes(json.dumps({'error': 'not_configured'}), 'UTF-8'))
            return

        creds = []
        files=glob.glob(CREDENTIALS_DIR + "/*")
        for file in files:
            public_key = ""
            with open(file, 'rb') as f:
                cred, _ = AttestedCredentialData.unpack_from(f.read())
                # TODO add more public-keys which must be stored in AttestedCredentialData
                creds.append(cred)
                auth_data, state =server.authenticate_begin([cred])
                public_key = str(base64.b64encode(auth_data["publicKey"]["allowCredentials"][0]["id"]))[2:-1]
            header_file = HEADERS_DIR + "/" + file.split("/")[-1]
            if os.path.exists(header_file):
                with open(header_file, 'r') as f:
                    HEADER_MANAGER[public_key] = ["Fido-User", "\t".join(f.read().splitlines())]


        if self.path == HTTP_PREFIX + "/get_challenge_for_existing_key":
            auth_data, state = server.authenticate_begin(creds)
            auth_data["publicKey"]["challenge"] = str(base64.b64encode(auth_data["publicKey"]["challenge"]), 'utf-8')
            for el in auth_data["publicKey"]["allowCredentials"]:
                el["id"] = str(base64.b64encode(el["id"]), 'utf-8')
            #auth_data["publicKey"]["allowCredentials"][0]["id"] = str(base64.b64encode(auth_data["publicKey"]["allowCredentials"][0]["id"]), 'utf-8')

            CHALLENGE.update(state)

            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(bytes(json.dumps(auth_data), 'UTF-8'))

        if self.path == HTTP_PREFIX + "/complete_challenge_for_existing_key":
            data = json.loads(self.rfile.read(int(self.headers.get('Content-Length'))))

            credential_id = base64.b64decode(data['id'])
            client_data = ClientData(base64.b64decode(data['clientDataJSON']))
            auth_data = AuthenticatorData(base64.b64decode(data['authenticatorData']))
            signature = base64.b64decode(data['signature'])

            with open(LASTCHALLENGE) as f:
                server.authenticate_complete(
                    CHALLENGE,
                    creds,
                    credential_id,
                    client_data,
                    auth_data,
                    signature
                )

            cookie = http.cookies.SimpleCookie()
            header = HEADER_MANAGER.get(data['id'], ["", ""])
            token = TOKEN_MANAGER.generate()
            cookie["token"] = token
            TOKEN_MANAGER.set_header(token, header)
            cookie["token"]["path"] = "/"
            cookie["token"]["secure"] = True

            self.send_response(200)
            self.send_header('Set-Cookie', cookie.output(header=''))
            if header != 0:
                self.send_header(header[0], header[1])
            #self.send_header('Fido-User', "test")
            # TODO sem připsat hlavičku s uživatelem
            self.end_headers()
            self.wfile.write(bytes(json.dumps({'status': 'ok'}), 'UTF-8'))
Example #13
0
            self.send_response(200)
            self.send_header('Set-Cookie', cookie.output(header=''))
            if header != 0:
                self.send_header(header[0], header[1])
            #self.send_header('Fido-User', "test")
            # TODO sem připsat hlavičku s uživatelem
            self.end_headers()
            self.wfile.write(bytes(json.dumps({'status': 'ok'}), 'UTF-8'))

if len(sys.argv) > 1 and sys.argv[1] == "save-client":
    host = sys.argv[2]
    client_data = ClientData(base64.b64decode(sys.argv[3]))
    attestation_object = AttestationObject(base64.b64decode(sys.argv[4]))

    rp = PublicKeyCredentialRpEntity(host, 'NGINX Auth Server')
    server = U2FFido2Server('https://' + host, rp)

    with open(LASTCHALLENGE) as f:
        auth_data = server.register_complete(json.loads(f.read()), client_data, attestation_object)
        with open(CREDENTIALS_DIR + "/" + sys.argv[5], 'wb') as f:
            f.write(auth_data.credential_data)

    print("Credentials saved successfully")

else:
    socketserver.TCPServer.allow_reuse_address = True
    httpd = socketserver.TCPServer(("", PORT), AuthHandler)
    try:
        print("serving at port", PORT)
        httpd.serve_forever()
    finally:
Example #14
0
)
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"])
def register_begin():
    registration_data, state = server.register_begin(
        {
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()]
Example #16
0
File: models.py Project: g10f/sso
class U2FDevice(Device):
    version = models.TextField(default="U2F_V2")
    public_key = models.TextField()
    credential_id = models.TextField()
    aaguid = models.TextField()
    counter = models.IntegerField(default=0)

    # server = Fido2Server(PublicKeyCredentialRpEntity(settings.SSO_DOMAIN.lower().split(':')[0],
    # f'{settings.SSO_SITE_NAME} Server'))
    u2f_app_id = f"{'https' if settings.SSO_USE_HTTPS else 'http'}://{settings.SSO_DOMAIN.lower().split(':')[0]}"
    server = U2FFido2Server(
        u2f_app_id,
        PublicKeyCredentialRpEntity(settings.SSO_DOMAIN.lower().split(':')[0],
                                    f'{settings.SSO_SITE_NAME} Server'))
    WEB_AUTHN_SALT = 'sso.auth.models.U2FDevice'
    device_id = 1
    Device.devices.add((__qualname__, device_id))

    class Meta:
        ordering = ['order', 'name']

    def __str__(self):
        return "%s:%s:%s" % (self.__class__.__name__, self.user_id, self.pk)

    @classmethod
    def get_device_id(cls):
        return cls.device_id

    @classmethod
    def detail_template(cls):
        return 'sso_auth/u2f/detail.html'

    @classmethod
    def register_begin(cls, request):
        user = request.user
        credentials = U2FDevice.credentials(user)
        icon = absolute_url(
            request,
            get_thumbnail(user.picture, "60x60",
                          crop="center").url) if user.picture else None
        registration_data, state = cls.server.register_begin(
            {
                "id": user.uuid.bytes,
                "name": user.username,
                "displayName": user.get_full_name(),
                "icon": icon
            },
            credentials,
            user_verification="discouraged")
        u2f_request = {
            'req': b64encode(cbor.encode(registration_data)).decode(),
            'state': signing.dumps(state, salt=U2FDevice.WEB_AUTHN_SALT)
        }
        return u2f_request

    @classmethod
    def register_complete(cls, name, response_data, state_data, user):
        data = cbor.decode(b64decode(response_data))
        state = signing.loads(state_data, salt=U2FDevice.WEB_AUTHN_SALT)
        client_data = ClientData(data["clientDataJSON"])
        att_obj = AttestationObject(data["attestationObject"])
        logger.debug("clientData", client_data)
        logger.debug("AttestationObject:", att_obj)

        auth_data = cls.server.register_complete(state, client_data, att_obj)
        logger.debug(auth_data)
        public_key = websafe_encode(
            cbor.encode(auth_data.credential_data.public_key))
        aaguid = websafe_encode(auth_data.credential_data.aaguid)
        credential_id = websafe_encode(auth_data.credential_data.credential_id)

        device = U2FDevice.objects.create(name=name,
                                          user=user,
                                          public_key=public_key,
                                          credential_id=credential_id,
                                          aaguid=aaguid,
                                          confirmed=True,
                                          version="fido2")
        return device

    @classmethod
    def authenticate_complete(cls, response_data, state_data, user):
        response = cbor.decode(b64decode(response_data))
        state = signing.loads(state_data, salt=U2FDevice.WEB_AUTHN_SALT)
        credential_id = response["credentialId"]
        client_data = ClientData(response["clientDataJSON"])
        auth_data = AuthenticatorData(response["authenticatorData"])
        signature = response["signature"]

        credentials = U2FDevice.credentials(user)
        cred = cls.server.authenticate_complete(
            state,
            credentials,
            credential_id,
            client_data,
            auth_data,
            signature,
        )
        credential_id = websafe_encode(cred.credential_id)
        device = U2FDevice.objects.get(user=user, credential_id=credential_id)
        if auth_data.counter <= device.counter:
            # verify counter is increasing
            raise ValueError(
                f"login counter is not increasing. {auth_data.counter} <= {device.counter} "
            )
        device.last_used = timezone.now()
        device.counter = auth_data.counter
        device.save(update_fields=["last_used", "counter"])
        return device

    @classmethod
    def credentials(cls, user):
        u2f_devices = cls.objects.filter(user=user, confirmed=True)
        return [
            AttestedCredentialData.create(
                aaguid=websafe_decode(d.aaguid),
                credential_id=websafe_decode(d.credential_id),
                public_key=CoseKey.parse(
                    cbor.decode(websafe_decode(d.public_key))))
            for d in u2f_devices
        ]

    @classmethod
    def challenges(cls, user):
        credentials = U2FDevice.credentials(user)
        req, state = cls.server.authenticate_begin(
            credentials=credentials,
            user_verification=UserVerificationRequirement.DISCOURAGED)
        sign_request = {
            'req': b64encode(cbor.encode(req)).decode(),
            'state': signing.dumps(state, salt=cls.WEB_AUTHN_SALT)
        }
        return json.dumps(sign_request)

    @classmethod
    def image(cls):
        return 'img/u2f_yubikey.png'

    @classmethod
    def login_form_class(cls):
        return U2FForm

    @classmethod
    def login_form_templates(cls):
        return 'sso_auth/u2f/verify.html'

    @classmethod
    def login_text(cls):
        return _(
            'Please touch the flashing U2F device now. You may be prompted to allow the site permission '
            'to access your security keys. After granting permission, the device will start to blink.'
        )

    @classmethod
    def default_name(cls):
        return _('FIDO2 or U2F Device')