Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def assertion_dict(self):
if not isinstance(self.webauthn_users, list) or len(self.webauthn_users) < 1:
raise AuthenticationRejectedException('Invalid user list.')
if len(set([u.rp_id for u in self.webauthn_users])) != 1:
raise AuthenticationRejectedException('Invalid (mutliple) RP IDs in user list.')
for user in self.webauthn_users:
if not isinstance(user, WebAuthnUser):
raise AuthenticationRejectedException('Invalid user type.')
if not user.credential_id:
raise AuthenticationRejectedException('Invalid credential ID.')
if not self.challenge:
raise AuthenticationRejectedException('Invalid challenge.')
acceptable_credentials = []
for user in self.webauthn_users:
acceptable_credentials.append({
'type': 'public-key',
'id': user.credential_id,
'transports': ['usb', 'nfc', 'ble', 'internal'],
})
assertion_dict = {
'challenge': self.challenge,
'allowCredentials': acceptable_credentials,
# used on that TLS connection, also verify that
# C.tokenBinding.id matches the base64url encoding of the
# Token Binding ID for the connection.
# XXX: Chrome does not currently supply token binding in the clientDataJSON
# if not _verify_token_binding_id(c):
# raise AuthenticationRejectedException('Unable to verify token binding ID.')
# Step 11.
#
# Verify that the rpIdHash in aData is the SHA-256 hash of
# the RP ID expected by the Relying Party.
auth_data_rp_id_hash = _get_auth_data_rp_id_hash(decoded_a_data)
if not _verify_rp_id_hash(auth_data_rp_id_hash,
self.webauthn_user.rp_id):
raise AuthenticationRejectedException(
'Unable to verify RP ID hash.')
# Step 12.
#
# Verify that the User Present bit of the flags in authData
# is set.
# Authenticator data flags.
# https://www.w3.org/TR/webauthn/#authenticator-data
flags = struct.unpack('!B', decoded_a_data[32:33])[0]
if (flags & const.USER_PRESENT) != 0x01:
raise AuthenticationRejectedException(
'Malformed request received.')
# Step 13.
if not self.webauthn_user.username:
raise WebAuthnUserDataMissing("username missing")
user_handle = self.assertion_response.get('userHandle')
if user_handle:
if not user_handle == self.webauthn_user.username:
raise AuthenticationRejectedException(
'Invalid credential.')
# Step 3.
#
# Using credential's id attribute (or the corresponding rawId, if
# base64url encoding is inappropriate for your use case), look up
# the corresponding credential public key.
if not _validate_credential_id(self.webauthn_user.credential_id):
raise AuthenticationRejectedException('Invalid credential ID.')
if not isinstance(self.webauthn_user, WebAuthnUser):
raise AuthenticationRejectedException('Invalid user type.')
if not self.webauthn_user.public_key:
raise WebAuthnUserDataMissing("public_key missing")
credential_public_key = self.webauthn_user.public_key
public_key_alg, user_pubkey = _load_cose_public_key(
_webauthn_b64_decode(credential_public_key))
# Step 4.
#
# Let cData, aData and sig denote the value of credential's
# response's clientDataJSON, authenticatorData, and signature
# respectively.
# Step 7.
#
# Verify that the value of C.type is the string webauthn.get.
received_type = c.get('type')
if not _verify_type(received_type, TYPE_GET):
raise RegistrationRejectedException('Invalid type.')
# Step 8.
#
# Verify that the value of C.challenge matches the challenge
# that was sent to the authenticator in the
# PublicKeyCredentialRequestOptions passed to the get() call.
received_challenge = c.get('challenge')
if not _verify_challenge(received_challenge, self.challenge):
raise AuthenticationRejectedException(
'Unable to verify challenge.')
# Step 9.
#
# Verify that the value of C.origin matches the Relying
# Party's origin.
if not _verify_origin(c, self.origin):
raise AuthenticationRejectedException(
'Unable to verify origin.')
# Step 10.
#
# Verify that the value of C.tokenBinding.status matches
# the state of Token Binding for the TLS connection over
# which the attestation was obtained. If Token Binding was
# used on that TLS connection, also verify that
# updates the stored signature counter value in this
# case, or not, or fails the authentication ceremony
# or not, is Relying Party-specific.
sc = decoded_a_data[33:37]
sign_count = struct.unpack('!I', sc)[0]
if not sign_count:
raise AuthenticationRejectedException('Unable to parse sign_count.')
if (isinstance(self.webauthn_user.sign_count, int) and
self.webauthn_user.sign_count < 0) or not isinstance(
self.webauthn_user.sign_count, int):
raise WebAuthnUserDataMissing('sign_count missing from WebAuthnUser.')
if sign_count <= self.webauthn_user.sign_count:
raise AuthenticationRejectedException(
'Duplicate authentication detected.')
# Step 18.
#
# If all the above steps are successful, continue with the
# authentication ceremony as appropriate. Otherwise, fail the
# authentication ceremony.
return sign_count
except Exception as e:
raise AuthenticationRejectedException(
'Authentication rejected. Error: {}.'.format(e))
# values in the clientExtensionResults and the extensions in
# authData MUST be also be present as extension identifier values
# in the extensions member of options, i.e., no extensions are
# present that were not requested. In the general case, the meaning
# of "are as expected" is specific to the Relying Party and which
# extensions are in use.
assertion_client_extensions = self.assertion_response.get(
'assertionClientExtensions')
if assertion_client_extensions:
ace = json.loads(assertion_client_extensions)
if not _verify_client_extensions(ace, self.expected_assertion_client_extensions):
raise AuthenticationRejectedException(
'Unable to verify client extensions.')
if not _verify_authenticator_extensions(
c, self.expected_assertion_authenticator_extensions):
raise AuthenticationRejectedException(
'Unable to verify authenticator extensions.')
# Step 15.
#
# Let hash be the result of computing a hash over the cData
# using SHA-256.
client_data_hash = _get_client_data_hash(decoded_cd)
# Step 16.
#
# Using the credential public key looked up in step 3, verify
# that sig is a valid signature over the binary concatenation
# of aData and hash.
bytes_to_verify = b''.join([decoded_a_data, client_data_hash])
try:
# clientExtensionResults and the authenticator extension outputs
# in the extensions in authData are as expected, considering the
# client extension input values that were given as the extensions
# option in the get() call. In particular, any extension identifier
# values in the clientExtensionResults and the extensions in
# authData MUST be also be present as extension identifier values
# in the extensions member of options, i.e., no extensions are
# present that were not requested. In the general case, the meaning
# of "are as expected" is specific to the Relying Party and which
# extensions are in use.
assertion_client_extensions = self.assertion_response.get(
'assertionClientExtensions')
if assertion_client_extensions:
ace = json.loads(assertion_client_extensions)
if not _verify_client_extensions(ace, self.expected_assertion_client_extensions):
raise AuthenticationRejectedException(
'Unable to verify client extensions.')
if not _verify_authenticator_extensions(
c, self.expected_assertion_authenticator_extensions):
raise AuthenticationRejectedException(
'Unable to verify authenticator extensions.')
# Step 15.
#
# Let hash be the result of computing a hash over the cData
# using SHA-256.
client_data_hash = _get_client_data_hash(decoded_cd)
# Step 16.
#
# Using the credential public key looked up in step 3, verify
# that sig is a valid signature over the binary concatenation
# to be the value of adata.signCount.
# less than or equal to the signature counter value
# stored in conjunction with credential's id attribute.
# This is a signal that the authenticator may be
# cloned, i.e. at least two copies of the credential
# private key may exist and are being used in parallel.
# Relying Parties should incorporate this information
# into their risk scoring. Whether the Relying Party
# updates the stored signature counter value in this
# case, or not, or fails the authentication ceremony
# or not, is Relying Party-specific.
sc = decoded_a_data[33:37]
sign_count = struct.unpack('!I', sc)[0]
if not sign_count:
raise AuthenticationRejectedException('Unable to parse sign_count.')
if (isinstance(self.webauthn_user.sign_count, int) and
self.webauthn_user.sign_count < 0) or not isinstance(
self.webauthn_user.sign_count, int):
raise WebAuthnUserDataMissing('sign_count missing from WebAuthnUser.')
if sign_count <= self.webauthn_user.sign_count:
raise AuthenticationRejectedException(
'Duplicate authentication detected.')
# Step 18.
#
# If all the above steps are successful, continue with the
# authentication ceremony as appropriate. Otherwise, fail the
# authentication ceremony.
return sign_count
self.webauthn_user.sign_count, int):
raise WebAuthnUserDataMissing('sign_count missing from WebAuthnUser.')
if sign_count <= self.webauthn_user.sign_count:
raise AuthenticationRejectedException(
'Duplicate authentication detected.')
# Step 18.
#
# If all the above steps are successful, continue with the
# authentication ceremony as appropriate. Otherwise, fail the
# authentication ceremony.
return sign_count
except Exception as e:
raise AuthenticationRejectedException(
'Authentication rejected. Error: {}.'.format(e))
"""
webauthn_users = _get_webauthn_users(user, rp_id=rp_id)
cred_ids = [cred.credential_id for cred in webauthn_users]
encoded_challenge = _webauthn_b64encode(challenge.encode()).decode()
for webauthn_user in webauthn_users:
response = pywebauthn.WebAuthnAssertionResponse(
webauthn_user,
assertion,
encoded_challenge,
origin,
allow_credentials=cred_ids,
)
try:
return (webauthn_user.credential_id, response.verify())
except _AuthenticationRejectedException:
pass
# If we exit the loop, then we've failed to verify the assertion against
# any of the user's WebAuthn credentials. Fail.
raise AuthenticationRejectedException("Invalid WebAuthn credential")