Add custom challenge argument to register_begin.

This commit is contained in:
Dain Nilsson 2019-10-15 09:41:04 +02:00
parent aeb3fdff10
commit fba27d4479
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
2 changed files with 36 additions and 10 deletions

View File

@ -94,6 +94,17 @@ def _default_attestations():
]
def _validata_challenge(challenge):
if challenge is None:
challenge = os.urandom(32)
else:
if not isinstance(challenge, bytes):
raise TypeError("Custom challenge must be of type 'bytes'.")
if len(challenge) < 16:
raise ValueError("Custom challenge length must be >= 16.")
return challenge
class Fido2Server(object):
"""FIDO2 server
@ -127,6 +138,7 @@ class Fido2Server(object):
resident_key=False,
user_verification=USER_VERIFICATION.PREFERRED,
authenticator_attachment=None,
challenge=None,
):
"""Return a PublicKeyCredentialCreationOptions registration object and
the internal state dictionary that needs to be passed as is to the
@ -138,12 +150,14 @@ class Fido2Server(object):
:param user_verification: The desired USER_VERIFICATION level.
:param authenticator_attachment: The desired AUTHENTICATOR_ATTACHMENT
or None to not provide a preference (and get both types).
:param challenge: A custom challenge to sign and verify or None to use
OS-specific random bytes.
:return: Registration data, internal state."""
if not self.allowed_algorithms:
raise ValueError("Server has no allowed algorithms.")
uv = USER_VERIFICATION(user_verification)
challenge = os.urandom(32)
challenge = _validata_challenge(challenge)
# Serialize RP
rp_data = {"id": self.rp.ident, "name": self.rp.name}
@ -237,9 +251,9 @@ class Fido2Server(object):
def authenticate_begin(
self, credentials, user_verification=USER_VERIFICATION.PREFERRED, challenge=None
):
"""Return a PublicKeyCredentialRequestOptions assertion object and
the internal state dictionary that needs to be passed as is to the
corresponding `authenticate_complete` call.
"""Return a PublicKeyCredentialRequestOptions assertion object and the internal
state dictionary that needs to be passed as is to the corresponding
`authenticate_complete` call.
:param credentials: The list of previously registered credentials.
:param user_verification: The desired USER_VERIFICATION level.
@ -247,12 +261,7 @@ class Fido2Server(object):
OS-specific random bytes.
:return: Assertion data, internal state."""
uv = USER_VERIFICATION(user_verification)
if challenge is None:
challenge = os.urandom(32)
else:
if not isinstance(challenge, bytes):
raise TypeError("Custom challenge must be bytes.")
challenge = _validata_challenge(challenge)
data = {
"publicKey": {

View File

@ -47,6 +47,23 @@ class TestFido2Server(unittest.TestCase):
}
self.assertEqual(request["publicKey"]["rp"], data)
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)
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)
def test_authenticate_complete_invalid_signature(self):
rp = RelyingParty("example.com", "Example")
server = Fido2Server(rp)