Skip to content

Commit e3f6864

Browse files
committed
winapi: add hmac-secret-mc
1 parent 301e73a commit e3f6864

File tree

3 files changed

+36
-5
lines changed

3 files changed

+36
-5
lines changed

examples/hmac_secret.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,19 @@
5858

5959
uv = "discouraged"
6060

61+
client_data_collector = DefaultClientDataCollector("https://example.com")
62+
6163
if use_winclient:
6264
# Use the Windows WebAuthn API if available, and we're not running as admin
6365
# By default only the PRF extension is allowed, we need to explicitly
6466
# configure the client to allow hmac-secret
65-
client = WindowsClient("https://example.com", allow_hmac_secret=True)
67+
client = WindowsClient(client_data_collector, allow_hmac_secret=True)
6668
else:
6769
# Locate a device
6870
for dev in enumerate_devices():
6971
client = Fido2Client(
7072
dev,
71-
client_data_collector=DefaultClientDataCollector("https://example.com"),
73+
client_data_collector=client_data_collector,
7274
user_interaction=CliInteraction(),
7375
# By default only the PRF extension is allowed, we need to explicitly
7476
# configure the client to allow hmac-secret

fido2/client/win_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ class WebAuthNCredentialAttestation(ctypes.Structure):
826826
("cbUnsignedExtensionOutputs", DWORD),
827827
("pbUnsignedExtensionOutputs", PBYTE),
828828
# Version 7 additions
829+
("pHmacSecret", ctypes.POINTER(WebAuthNHmacSecretSalt)),
829830
("bThirdPartyPayment", BOOL),
830831
# Version 8 additions
831832
("dwTransports", DWORD),

fido2/client/windows.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ def make_credential(self, options, event=None):
198198
win_extensions = []
199199
large_blob_support = WebAuthNLargeBlobSupport.NONE
200200
enable_prf = False
201+
hmac_salts = None
201202
if options.extensions:
202203
if "credentialProtectionPolicy" in options.extensions:
203204
win_extensions.append(
@@ -226,11 +227,23 @@ def make_credential(self, options, event=None):
226227
)
227228
if options.extensions.get("minPinLength", True):
228229
win_extensions.append(WebAuthNExtension("minPinLength", BOOL(True)))
229-
if "prf" in options.extensions:
230+
prf = AuthenticatorExtensionsPRFInputs.from_dict(
231+
cast(Mapping | None, options.extensions.get("prf"))
232+
)
233+
if prf:
230234
enable_prf = True
231235
win_extensions.append(WebAuthNExtension("hmac-secret", BOOL(True)))
236+
if prf.eval:
237+
hmac_salts = WebAuthNHmacSecretSalt(prf.eval.first, prf.eval.second)
232238
elif "hmacCreateSecret" in options.extensions and self._allow_hmac_secret:
233239
win_extensions.append(WebAuthNExtension("hmac-secret", BOOL(True)))
240+
hmac_get_secret = HMACGetSecretInput.from_dict(
241+
cast(Mapping | None, options.extensions.get("hmacGetSecret"))
242+
)
243+
if hmac_get_secret:
244+
hmac_salts = WebAuthNHmacSecretSalt(
245+
hmac_get_secret.salt1, hmac_get_secret.salt2
246+
)
234247

235248
if event:
236249
timer = CancelThread(event)
@@ -268,7 +281,7 @@ def make_credential(self, options, event=None):
268281
resident_key == ResidentKeyRequirement.PREFERRED,
269282
enable_prf,
270283
win_extensions,
271-
None, # TODO: hmac_secret_salts
284+
hmac_salts,
272285
options.hints,
273286
)
274287
),
@@ -284,20 +297,35 @@ def make_credential(self, options, event=None):
284297
obj = attestation_pointer.contents
285298
att_obj = AttestationObject(obj.attestation_object)
286299

287-
extension_outputs = {}
300+
extension_outputs: dict[str, Any] = {}
288301
if options.extensions:
289302
extensions_out = att_obj.auth_data.extensions or {}
290303
if options.extensions.get("credProps"):
291304
extension_outputs["credProps"] = {"rk": bool(obj.bResidentKey)}
292305
if "hmac-secret" in extensions_out:
306+
if obj.dwVersion >= 7:
307+
secret = obj.pHmacSecret.contents
308+
secrets = (secret.first, secret.second)
309+
else:
310+
secrets = None
293311
if enable_prf:
294312
extension_outputs["prf"] = {
295313
"enabled": extensions_out["hmac-secret"]
296314
}
315+
if secrets:
316+
results = {"first": secrets[0]}
317+
if secrets[1]:
318+
results["second"] = secrets[1]
319+
extension_outputs["prf"]["results"] = results
297320
else:
298321
extension_outputs["hmacCreateSecret"] = extensions_out[
299322
"hmac-secret"
300323
]
324+
if secrets:
325+
results = {"output1": secrets[0]}
326+
if secrets[1]:
327+
results["output2"] = secrets[1]
328+
extension_outputs["hmacGetSecret"] = results
301329
if "largeBlob" in options.extensions:
302330
extension_outputs["largeBlob"] = {
303331
"supported": bool(obj.bLargeBlobSupported)

0 commit comments

Comments
 (0)