From d57ce7a96e9802e8b92cdabbedbbaeda8e18702c Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Wed, 10 Jan 2024 17:09:23 -0800 Subject: [PATCH 1/3] Update docstrings with default challenge info --- webauthn/authentication/generate_authentication_options.py | 2 +- webauthn/registration/generate_registration_options.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webauthn/authentication/generate_authentication_options.py b/webauthn/authentication/generate_authentication_options.py index 8443a06..3ccc9a6 100644 --- a/webauthn/authentication/generate_authentication_options.py +++ b/webauthn/authentication/generate_authentication_options.py @@ -20,7 +20,7 @@ def generate_authentication_options( Args: `rp_id`: The Relying Party's unique identifier as specified in attestations. - (optional) `challenge`: A byte sequence for the authenticator to return back in its response. If no value is specified then a sequence of random bytes will be generated. + (optional) `challenge`: A byte sequence for the authenticator to return back in its response. Defaults to 64 random bytes. (optional) `timeout`: How long in milliseconds the browser should give the user to choose an authenticator. This value is a *hint* and may be ignored by the browser. (optional) `allow_credentials`: A list of credentials registered to the user. (optional) `user_verification`: The RP's preference for the authenticator's enforcement of the "user verified" flag. diff --git a/webauthn/registration/generate_registration_options.py b/webauthn/registration/generate_registration_options.py index 6142433..432e9a2 100644 --- a/webauthn/registration/generate_registration_options.py +++ b/webauthn/registration/generate_registration_options.py @@ -61,7 +61,7 @@ def generate_registration_options( `user_name`: A value that will help the user identify which account this credential is associated with. Can be an email address, etc... (optional) `user_id`: A collection of random bytes that identify a user account. For privacy reasons it should NOT be something like an email address. Defaults to 64 random bytes. (optional) `user_display_name`: A user-friendly representation of their account. Can be a full name ,etc... Defaults to the value of `user_name`. - (optional) `challenge`: A byte sequence for the authenticator to return back in its response. If no value is specified then a sequence of random bytes will be generated. + (optional) `challenge`: A byte sequence for the authenticator to return back in its response. Defaults to 64 random bytes. (optional) `timeout`: How long in milliseconds the browser should give the user to choose an authenticator. This value is a *hint* and may be ignored by the browser. (optional) `attestation`: The level of attestation to be provided by the authenticator. (optional) `authenticator_selection`: Require certain characteristics about an authenticator, like attachment, support for resident keys, user verification, etc... From f5c420cd17d2fdb0573cc1fa422c885c5eb3f799 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Wed, 10 Jan 2024 17:09:56 -0800 Subject: [PATCH 2/3] Hardcode generate_challenge() byte count --- tests/test_generate_challenge.py | 5 ----- webauthn/helpers/generate_challenge.py | 10 +++++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/test_generate_challenge.py b/tests/test_generate_challenge.py index c87211b..af7a684 100644 --- a/tests/test_generate_challenge.py +++ b/tests/test_generate_challenge.py @@ -15,8 +15,3 @@ def test_generates_unique_value_each_time(self) -> None: output2 = generate_challenge() assert output1 != output2 - - def test_supports_custom_lengths(self) -> None: - output = generate_challenge(32) - - assert len(output) == 32 diff --git a/webauthn/helpers/generate_challenge.py b/webauthn/helpers/generate_challenge.py index 2d679a9..dabc7f8 100644 --- a/webauthn/helpers/generate_challenge.py +++ b/webauthn/helpers/generate_challenge.py @@ -1,8 +1,12 @@ import secrets -def generate_challenge(length: int = 64) -> bytes: +def generate_challenge() -> bytes: """ - Generate a random authenticator challenge + Create a random value for the authenticator to sign, going above and beyond the recommended + number of random bytes as per https://www.w3.org/TR/webauthn-2/#sctn-cryptographic-challenges: + + "In order to prevent replay attacks, the challenges MUST contain enough entropy to make + guessing them infeasible. Challenges SHOULD therefore be at least 16 bytes long." """ - return secrets.token_bytes(length) + return secrets.token_bytes(64) From c471b0fdad69e83fbfa0510e1595b12861d5df30 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Wed, 10 Jan 2024 17:12:00 -0800 Subject: [PATCH 3/3] Refactor more use of `assert` in tests --- tests/test_generate_challenge.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_generate_challenge.py b/tests/test_generate_challenge.py index af7a684..efd2b98 100644 --- a/tests/test_generate_challenge.py +++ b/tests/test_generate_challenge.py @@ -7,11 +7,11 @@ class TestWebAuthnGenerateChallenge(TestCase): def test_generates_byte_sequence(self) -> None: output = generate_challenge() - assert type(output) == bytes - assert len(output) == 64 + self.assertEqual(type(output), bytes) + self.assertEqual(len(output), 64) def test_generates_unique_value_each_time(self) -> None: output1 = generate_challenge() output2 = generate_challenge() - assert output1 != output2 + self.assertNotEqual(output1, output2)