Skip to content

Commit

Permalink
Merge PR #232
Browse files Browse the repository at this point in the history
  • Loading branch information
dainnilsson committed Sep 23, 2024
2 parents 35c2d92 + 8803011 commit f932065
Show file tree
Hide file tree
Showing 6 changed files with 626 additions and 154 deletions.
55 changes: 35 additions & 20 deletions examples/cred_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
creates a new credential for it with the extension enabled, and stores some data.
"""
from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client, UserInteraction
from fido2.client import Fido2Client, WindowsClient, UserInteraction
from fido2.server import Fido2Server
from getpass import getpass
import ctypes
import sys
import os

Expand Down Expand Up @@ -63,24 +64,32 @@ def request_uv(self, permissions, rd_id):
return True


# Locate a device
for dev in enumerate_devices():
client = Fido2Client(dev, "https://example.com", user_interaction=CliInteraction())
if "credBlob" in client.info.extensions:
break
else:
print("No Authenticator with the CredBlob extension found!")
sys.exit(1)

# Prefer UV token if supported
uv = "discouraged"
if client.info.options.get("pinUvAuthToken") or client.info.options.get("uv"):
uv = "preferred"
print("Authenticator supports UV token")

if WindowsClient.is_available() and not ctypes.windll.shell32.IsUserAnAdmin():
# Use the Windows WebAuthn API if available, and we're not running as admin
client = WindowsClient("https://example.com")
else:
# Locate a device
for dev in enumerate_devices():
client = Fido2Client(
dev, "https://example.com", user_interaction=CliInteraction()
)
if "credBlob" in client.info.extensions:
break
else:
print("No Authenticator with the CredBlob extension found!")
sys.exit(1)

# Prefer UV token if supported
if client.info.options.get("pinUvAuthToken") or client.info.options.get("uv"):
uv = "preferred"
print("Authenticator supports UV token")


server = Fido2Server({"id": "example.com", "name": "Example RP"})
user = {"id": b"user_id", "name": "A. User"}

# Prepare parameters for makeCredential
create_options, state = server.register_begin(
user,
Expand All @@ -91,11 +100,14 @@ def request_uv(self, permissions, rd_id):

# Add CredBlob extension, attach data
blob = os.urandom(32) # 32 random bytes
options = dict(create_options["publicKey"])
options["extensions"] = {"credBlob": blob}

# Create a credential
result = client.make_credential(options)
result = client.make_credential(
{
**create_options["publicKey"],
"extensions": {"credBlob": blob},
}
)

# Complete registration
auth_data = server.register_complete(
Expand All @@ -113,12 +125,15 @@ def request_uv(self, permissions, rd_id):

# Prepare parameters for getAssertion
request_options, state = server.authenticate_begin()
options = dict(request_options["publicKey"])
options["extensions"] = {"getCredBlob": True}

# Authenticate the credential
# Only one cred in allowCredentials, only one response.
result = client.get_assertion(options).get_response(0)
result = client.get_assertion(
{
**request_options["publicKey"],
"extensions": {"getCredBlob": True},
}
).get_response(0)

blob_res = result.authenticator_data.extensions.get("credBlob")

Expand Down
87 changes: 54 additions & 33 deletions examples/hmac_secret.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
derive two separate secrets.
"""
from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client, UserInteraction
from fido2.server import Fido2Server
from fido2.client import Fido2Client, WindowsClient, UserInteraction
from getpass import getpass
import ctypes
import sys
import os

Expand Down Expand Up @@ -63,30 +65,49 @@ def request_uv(self, permissions, rd_id):
return True


# Locate a device
for dev in enumerate_devices():
client = Fido2Client(dev, "https://example.com", user_interaction=CliInteraction())
if "hmac-secret" in client.info.extensions:
break
uv = "discouraged"
rk = "discouraged"

if WindowsClient.is_available() and not ctypes.windll.shell32.IsUserAnAdmin():
# Use the Windows WebAuthn API if available, and we're not running as admin
client = WindowsClient("https://example.com")
rk = "required" # Windows requires resident key for hmac-secret
else:
print("No Authenticator with the HmacSecret extension found!")
sys.exit(1)
# Locate a device
for dev in enumerate_devices():
client = Fido2Client(
dev, "https://example.com", user_interaction=CliInteraction()
)
if "hmac-secret" in client.info.extensions:
break
else:
print("No Authenticator with the HmacSecret extension found!")
sys.exit(1)

server = Fido2Server({"id": "example.com", "name": "Example RP"}, attestation="none")
user = {"id": b"user_id", "name": "A. User"}

# Prepare parameters for makeCredential
rp = {"id": "example.com", "name": "Example RP"}
user = {"id": b"user_id", "name": "A. User"}
challenge = b"Y2hhbGxlbmdl"
create_options, state = server.register_begin(
user,
resident_key_requirement=rk,
user_verification=uv,
authenticator_attachment="cross-platform",
)

# Create a credential with a HmacSecret
# Create a credential
result = client.make_credential(
{
"rp": rp,
"user": user,
"challenge": challenge,
"pubKeyCredParams": [{"type": "public-key", "alg": -7}],
**create_options["publicKey"],
"extensions": {"hmacCreateSecret": True},
},
}
)

# Complete registration
auth_data = server.register_complete(
state, result.client_data, result.attestation_object
)
credentials = [auth_data.credential_data]

# HmacSecret result:
if not result.extension_results.get("hmacCreateSecret"):
Expand All @@ -104,17 +125,17 @@ def request_uv(self, permissions, rd_id):
salt = os.urandom(32)
print("Authenticate with salt:", salt.hex())


# Prepare parameters for getAssertion
request_options, state = server.authenticate_begin(credentials, user_verification=uv)

# Authenticate the credential
result = client.get_assertion(
{
"rpId": rp["id"],
"challenge": challenge,
"allowCredentials": allow_list,
"extensions": {"hmacGetSecret": {"salt1": salt}},
},
).get_response(
0
) # Only one cred in allowList, only one response.
{**request_options["publicKey"], "extensions": {"hmacGetSecret": {"salt1": salt}}}
)

# Only one cred in allowCredentials, only one response.
result = result.get_response(0)

output1 = result.extension_results["hmacGetSecret"]["output1"]
print("Authenticated, secret:", output1.hex())
Expand All @@ -126,16 +147,16 @@ 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,
**request_options["publicKey"],
"extensions": {"hmacGetSecret": {"salt1": salt, "salt2": salt2}},
},
).get_response(
0
) # One cred in allowCredentials, single response.
}
)

# Only one cred in allowCredentials, only one response.
result = result.get_response(0)

output = result.extension_results["hmacGetSecret"]
print("Old secret:", output["output1"].hex())
Expand Down
90 changes: 53 additions & 37 deletions examples/large_blobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
On Windows, the native WebAuthn API will be used.
"""
from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client, UserInteraction
from fido2.client import Fido2Client, WindowsClient, UserInteraction
from fido2.server import Fido2Server
from getpass import getpass
import ctypes
import sys


Expand Down Expand Up @@ -65,26 +66,31 @@ def request_uv(self, permissions, rd_id):
return True


uv = "discouraged"

# Locate a device
for dev in enumerate_devices():
client = Fido2Client(dev, "https://example.com", user_interaction=CliInteraction())
if "largeBlobKey" in client.info.extensions:
break
if WindowsClient.is_available() and not ctypes.windll.shell32.IsUserAnAdmin():
# Use the Windows WebAuthn API if available, and we're not running as admin
client = WindowsClient("https://example.com")
else:
print("No Authenticator with the largeBlobKey extension found!")
sys.exit(1)


if not client.info.options.get("largeBlobs"):
print("Authenticator does not support large blobs!")
sys.exit(1)


# Prefer UV token if supported
uv = "discouraged"
if client.info.options.get("pinUvAuthToken") or client.info.options.get("uv"):
uv = "preferred"
print("Authenticator supports UV token")
for dev in enumerate_devices():
client = Fido2Client(
dev, "https://example.com", user_interaction=CliInteraction()
)
if "largeBlobKey" in client.info.extensions:
break
else:
print("No Authenticator with the largeBlobKey extension found!")
sys.exit(1)

if not client.info.options.get("largeBlobs"):
print("Authenticator does not support large blobs!")
sys.exit(1)

# Prefer UV token if supported
if client.info.options.get("pinUvAuthToken") or client.info.options.get("uv"):
uv = "preferred"
print("Authenticator supports UV token")


server = Fido2Server({"id": "example.com", "name": "Example RP"})
Expand All @@ -100,50 +106,60 @@ def request_uv(self, permissions, rd_id):

print("Creating a credential with LargeBlob support...")

# Enable largeBlob
options = dict(create_options["publicKey"])
options["extensions"] = {"largeBlob": {"support": "required"}}

# Create a credential
result = client.make_credential(options)
result = client.make_credential(
{
**create_options["publicKey"],
# Enable largeBlob
"extensions": {"largeBlob": {"support": "required"}},
}
)

# Complete registration
auth_data = server.register_complete(
state, result.client_data, result.attestation_object
)
credentials = [auth_data.credential_data]

if not result.extension_results.get("supported"):
if not result.extension_results.get("largeBlob", {}).get("supported"):
print("Credential does not support largeBlob, failure!")
sys.exit(1)

print("Credential created! Writing a blob...")

# If UV is configured, it is required
if auth_data.is_user_verified():
uv = "required"

# Prepare parameters for getAssertion
request_options, state = server.authenticate_begin(user_verification=uv)

# Write a large blob
options = dict(request_options["publicKey"])
options["extensions"] = {"largeBlob": {"write": b"Here is some data to store!"}}

# Authenticate the credential
selection = client.get_assertion(options)
selection = client.get_assertion(
{
**request_options["publicKey"],
# Write a large blob
"extensions": {"largeBlob": {"write": b"Here is some data to store!"}},
}
)

# Only one cred in allowCredentials, only one response.
result = selection.get_response(0)
if not result.extension_results.get("written"):
if not result.extension_results.get("largeBlob", {}).get("written"):
print("Failed to write blob!")
sys.exit(1)

print("Blob written! Reading back the blob...")

# Read the blob
options = dict(request_options["publicKey"])
options["extensions"] = {"largeBlob": {"read": True}}

# Authenticate the credential
selection = client.get_assertion(options)
selection = client.get_assertion(
{
**request_options["publicKey"],
# Read the blob
"extensions": {"largeBlob": {"read": True}},
}
)

# Only one cred in allowCredentials, only one response.
result = selection.get_response(0)
print("Read blob:", result.extension_results.get("blob"))
print("Read blob:", result.extension_results.get("largeBlob", {}).get("blob"))
Loading

0 comments on commit f932065

Please sign in to comment.