diff --git a/examples/hmac_secret.py b/examples/hmac_secret.py index 6fc9ab7..93602c3 100644 --- a/examples/hmac_secret.py +++ b/examples/hmac_secret.py @@ -30,24 +30,38 @@ creates a new credential for it with the extension enabled, and uses it to derive two separate secrets. """ -from fido2.hid import CtapHidDevice -from fido2.client import Fido2Client, UserInteraction -from getpass import getpass -import sys +from __future__ import annotations import os +import sys +from getpass import getpass +from typing import Iterable + +from fido2.client import Fido2Client, UserInteraction +from fido2.cose import ES256 +from fido2.hid import CtapHidDevice +from fido2.webauthn import ( + PublicKeyCredentialCreationOptions, + PublicKeyCredentialDescriptor, + PublicKeyCredentialParameters, + PublicKeyCredentialRequestOptions, + PublicKeyCredentialRpEntity, + PublicKeyCredentialType, + PublicKeyCredentialUserEntity, +) + try: from fido2.pcsc import CtapPcscDevice + + have_pcsc = True except ImportError: - CtapPcscDevice = None + have_pcsc = False -def enumerate_devices(): - for dev in CtapHidDevice.list_devices(): - yield dev - if CtapPcscDevice: - for dev in CtapPcscDevice.list_devices(): - yield dev +def enumerate_devices() -> Iterable[CtapHidDevice | CtapPcscDevice]: + yield from CtapHidDevice.list_devices() + if have_pcsc: + yield from CtapPcscDevice.list_devices() # Handle user interaction @@ -73,50 +87,62 @@ def request_uv(self, permissions, rd_id): sys.exit(1) # Prepare parameters for makeCredential -rp = {"id": "example.com", "name": "Example RP"} -user = {"id": b"user_id", "name": "A. User"} +rp = PublicKeyCredentialRpEntity(id="example.com", name="Example RP") +user = PublicKeyCredentialUserEntity(id=b"user_id", name="A. User") challenge = b"Y2hhbGxlbmdl" # Create a credential with a HmacSecret result = client.make_credential( - { - "rp": rp, - "user": user, - "challenge": challenge, - "pubKeyCredParams": [{"type": "public-key", "alg": -7}], - "extensions": {"hmacCreateSecret": True}, - }, + PublicKeyCredentialCreationOptions( + rp=rp, + user=user, + challenge=challenge, + pub_key_cred_params=[ + PublicKeyCredentialParameters( + type=PublicKeyCredentialType.PUBLIC_KEY, alg=ES256.ALGORITHM + ) + ], + extensions={"hmacCreateSecret": True}, + ), ) # HmacSecret result: -if not result.extension_results.get("hmacCreateSecret"): +if result.extension_results is None or not result.extension_results.get( + "hmacCreateSecret" +): print("Failed to create credential with HmacSecret") sys.exit(1) credential = result.attestation_object.auth_data.credential_data +assert credential is not None print("New credential created, with the HmacSecret extension.") # Prepare parameters for getAssertion challenge = b"Q0hBTExFTkdF" # Use a new challenge for each call. -allow_list = [{"type": "public-key", "id": credential.credential_id}] +allow_list = [ + PublicKeyCredentialDescriptor( + type=PublicKeyCredentialType.PUBLIC_KEY, id=credential.credential_id + ) +] # Generate a salt for HmacSecret: salt = os.urandom(32) print("Authenticate with salt:", salt.hex()) # Authenticate the credential -result = client.get_assertion( - { - "rpId": rp["id"], - "challenge": challenge, - "allowCredentials": allow_list, - "extensions": {"hmacGetSecret": {"salt1": salt}}, - }, +assertion_result = client.get_assertion( + options=PublicKeyCredentialRequestOptions( + rp_id=rp.id, + challenge=challenge, + allow_credentials=allow_list, + extensions={"hmacGetSecret": {"salt1": salt}}, + ), ).get_response( 0 ) # Only one cred in allowList, only one response. -output1 = result.extension_results["hmacGetSecret"]["output1"] +assert assertion_result.extension_results is not None +output1 = assertion_result.extension_results["hmacGetSecret"]["output1"] print("Authenticated, secret:", output1.hex()) # Authenticate again, using two salts to generate two secrets: @@ -126,17 +152,17 @@ def request_uv(self, permissions, rd_id): print("Authenticate with second salt:", salt2.hex()) # The first salt is reused, which should result in the same secret. -result = client.get_assertion( - { - "rpId": rp["id"], - "challenge": challenge, - "allowCredentials": allow_list, - "extensions": {"hmacGetSecret": {"salt1": salt, "salt2": salt2}}, - }, +assertion_result = client.get_assertion( + options=PublicKeyCredentialRequestOptions( + challenge=challenge, + rp_id=rp.id, + allow_credentials=allow_list, + extensions={"hmacGetSecret": {"salt1": salt, "salt2": salt2}}, + ), ).get_response( 0 ) # One cred in allowCredentials, single response. - -output = result.extension_results["hmacGetSecret"] +assert assertion_result.extension_results is not None +output = assertion_result.extension_results["hmacGetSecret"] print("Old secret:", output["output1"].hex()) print("New secret:", output["output2"].hex())