Apple 後來建議申請Entitlement : com.apple.developer.hid.virtual.device 並使用 IOHIDUserDeviceCreateWithProperties 建立 Virtual HID. 這樣事情就簡單多了😮💨
The FIDO Authenticator is developed using DriverKit Frameworks, in order to port SoftU2F to macOS Big Sur.
- Handle the Virtual HID part
- Handle CTAP Command
⚠️ When receiving HID Frame, receive unexpected data usually.⚠️
- Entitilements - Need to apply to Apple
<key>com.apple.developer.driverkit</key> <true/>
<key>com.apple.developer.driverkit.family.hid.device</key> <true/>
<key>com.apple.developer.driverkit.family.hid.eventservice</key> <true/>
<key>com.apple.developer.driverkit.family.hid.virtual.device</key> <true/>
<key>com.apple.developer.driverkit.transport.hid</key> <true/>
<key>com.apple.developer.driverkit.transport.usb</key>
:
- Non-public entitlement
<!-- Can solve the problem of not being able to apply for 'com.apple.developer.driverkit.userclient-access' -->
<key>com.apple.developer.driverkit.allow-any-userclient-access</key> <true/>
- Setup XCode > Build Phases > Embed System Extensions :
com.gotrustid.SoftFIDO2.dext
- Entitilements
<!-- Permission to activate or deactivate system extensions. -->
<key>com.apple.developer.system-extension.install</key> <true/>
- Entitilements - Need to apply to Apple
- After Big Sur 11.2, If you do not have this entitlement, cannot communicate with the Driver
- When Open Driver Return Error = kIOReturnNotPermitted (0x2e2)
- After Big Sur 11.2, If you do not have this entitlement, cannot communicate with the Driver
<!-- After macOS Big Sur 11.2 "mandatory requirement" -->
<key>com.apple.developer.driverkit.userclient-access</key>
<array>
<string>com.gotrustid.SoftFIDO2</string>
</array>
- ℹ️ xnu-7195.50.7.100.1. IOKitKeys.h Found an undisclosed entitlement for driver,the above entitlement
com.apple.developer.driverkit.userclient-access
may not be neededcom.apple.developer.driverkit.allow-any-userclient-access
=> Add to SoftFIDO2
- Disable SIP (System Integrity Protection):Bypass Code-Signing, Notarization Check
- Enable Activation from Any Directory:
$ systemextensionsctl developer on
- If not set, it will be restricted to App under /Applications to activate Driver
- 觀看LOG
$ log show --predicate 'sender == "sysextd" or sender CONTAINS[c] "Fido" or sender CONTAINS[c] "HID"' --info --debug --last 2m
$ systemextensionsctl reset
:Reset Old SoftFIDO2 driver- (Sometimes need) Reboot your macOS
- Rebuild SoftFIDO2
- Run FidoDriverManager - Activate Driver
- Run UserClient, If success will show...
FidoDriverUserClient Start OK ✅
To view the Log, enter the command in the console:
> log show --predicate 'sender == "sysextd" or sender CONTAINS[c] "Fido" or sender CONTAINS[c] "HID"' --info --debug --last 2m
- You can click the [Try SetReport] Button, it will send 64bytes same data ( For loop from 0x01 to 0x09) HID Frame to Driver .
- You can go to https://webauthndemo.appspot.com/ to try whether Webauthn has sent data to Driver.
- Show Log
- Run
FidoDriverManager
to install / activate SoftFIDO2 Driver - Run
UserClient
to test SoftFIDO2 Driver - Show Log
- Class comparison
SoftU2F (Kext) - IOKit | SoftFIDO2 (Dext) - DriverKit | |
---|---|---|
class SoftU2FDriver : IOService |
class com_gotrustid_SoftFIDO2_SoftFido2Driver : IOService |
|
class SoftU2FUserClient : IOUserClient |
class SoftFido2UserClient : IOUserClient |
|
class SoftU2FDevice : IOHIDDevice |
class SoftFido2Device : IOUserHIDDevice |
- Function不是 1vs1的關係,Source Code也不多,就不列表了。只擷取資料接收的部份程式碼,在下方比較。
In the frameReceived of UserClient, read the HID Frame data from the received
IOMemoryDescriptor *report
- SoftU2F (Kext):After mapping, you can directly access the IOMemoryDescriptor content
report->prepare()
IOMemoryMap *reportMap = report->map();
// Can directly access
reportMap->getAddress()
report->complete();
- SoftFIDO2 (Dext):Requires access
IOMemoryDescriptor
viaIODMACommand
- Note: DMACommand is supported after macOS BigSur Beta9 (DriverKit 20) and Xcode 12
uint64_t flags = 0;
uint32_t dmaSegmentCount = 1;
IOAddressSegment segments[32];
ret = dmaCmd->PrepareForDMA(kIODMACommandPrepareForDMANoOptions, report, 0, 0, &flags, &dmaSegmentCount, segments);
if (ret == kIOReturnSuccess) {
uint64_t offset;
uint64_t length;
IOMemoryDescriptor* out = nullptr;
ret = dmaCmd->GetPreparation(&offset, &length, &out);
// 得到 offset = 0, length = 64
if (out != nullptr) {
IOMemoryMap* outMemMap = nullptr;
ret = out->CreateMapping(kIOMemoryMapCacheModeDefault, 0, 0, 0, 0, &outMemMap);
if (outMemMap != nullptr) {
memcpy((void*) notifyArgs, (void*) outMemMap->GetAddress(), outMemMap->GetLength());
OSSafeReleaseNULL(outMemMap);
}
}
ret = dmaCmd->CompleteDMA(kIODMACommandCompleteDMANoOptions);
}