Yubico / Yubico.NET.SDK

A YubiKey SDK for .NET developers
Apache License 2.0
96 stars 48 forks source link

Queue macOS input reports so that large responses aren't dropped #84

Closed GregDomzalski closed 1 month ago

GregDomzalski commented 1 month ago

Description

This has been a longstanding bug in the SDK that has been haunting me since the FIDO device code was originally written. Apple's documentation on how async input reports are intended to work are about as clear as mud. But with some extra (temporary) debug logging I was able to finally reproduce the issue with enough visibility into what was actually happening. The implementation used by our own libfido2 library now makes a lot more sense as well.

Fundamentally, we were just using the IOHIDDeviceRegisterInputReportCallback and its corresponding callback incorrectly. The read buffer that we supply during the registration is just meant to be a place for macOS to cache the report data... I guess? The intended use still is not super clear to me.

But what was clear from the logs is that the report callback was getting called faster than we were calling GetReport to read said reports. And since we previously did not queue up any of the reports until they were read, it resulted in reports either getting dropped, or arriving before we ran the IO runloop. That would usually result in the CFRunLoopRunInMode call inside of GetReport to timeout. Not because the YubiKey was taking a long time, but because the report already came and went and we missed it.

So now, we pass a ConcurrentQueue into the native callback via a GCHandle. We queue the report that we see in the callback so that when GetReport is eventually called, we will have the report. We first attempt to read straight from the queue, and if there's nothing there, we run the runloop which should then cause macOS to read from the YubiKey. This behavior now much more closely matches libfido2.

Fixes (internal issue)

Type of change

Please delete options that are not relevant.

How has this been tested?

Ran some local FIDO2 tests as well as real-world use cases using our internally dependent software.

Test configuration:

Checklist:

github-actions[bot] commented 1 month ago

Test Results: Windows

    2 files      2 suites   2s :stopwatch: 3 615 tests 3 615 :white_check_mark: 0 :zzz: 0 :x: 3 617 runs  3 617 :white_check_mark: 0 :zzz: 0 :x:

Results for commit 5c10887f.

:recycle: This comment has been updated with latest results.

github-actions[bot] commented 1 month ago

Test Results: Ubuntu

    2 files      2 suites   5s :stopwatch: 3 607 tests 3 607 :white_check_mark: 0 :zzz: 0 :x: 3 609 runs  3 609 :white_check_mark: 0 :zzz: 0 :x:

Results for commit 5c10887f.

:recycle: This comment has been updated with latest results.

github-actions[bot] commented 1 month ago

Test Results: MacOS

    2 files      2 suites   1s :stopwatch: 3 607 tests 3 607 :white_check_mark: 0 :zzz: 0 :x: 3 609 runs  3 609 :white_check_mark: 0 :zzz: 0 :x:

Results for commit 5c10887f.

:recycle: This comment has been updated with latest results.

github-actions[bot] commented 1 month ago

Code Coverage

Package Line Rate Branch Rate Complexity Health
Yubico.Core 42% 31% 4257
Yubico.YubiKey 51% 47% 18417
Summary 49% (31675 / 64317) 45% (8017 / 17979) 22674

Minimum allowed line rate is 40%