Crystalix007 / U2FDevice

An implementation of the U2F device protocol for Raspberry Pi 0
GNU General Public License v3.0
64 stars 6 forks source link

Doesn't seem to work with Raspbian Buster #6

Closed Piflyer closed 5 years ago

Piflyer commented 5 years ago

Hello, I've tried to run in multiple times with sudo systemctl start U2FDevice.service. It's not recognized by any sites at all. I haven't tried Stretch yet, but any help will be appreciated.

Piflyer commented 5 years ago

When I run /usr/bin/U2FDevice it gives "Descriptor is unavailable Closing".

Crystalix007 commented 5 years ago

This program requires that a kernel device is available in /dev/ as /dev/hidg0 which is effectively a pipe device (meaning that it is a virtual file) for communicating with the host computer.

In order for this specific device to operate, the kernel must be instructed to create the device. I guess that I could have made this part of the program itself, but it was simpler to implement it with a bash script.

Assuming you got through the README, to the part where you set up USB for multiple drivers simultaneously, then the next step is to run the setup script (Scripts/Kernel_HID_Config.sh), as root.

sudo Scripts/Kernel_HID_Config.sh

Then, try running the program again. So long as this works (i.e. without the "Descriptor is unavailable" message), you should just need to install the setup script and service (as this setup script must be run on every reboot). For this, make install is provided. After this is successfully run, sudo systemctl start U2FDevice.service should work.

TL;DR:

If still nothing works, just tell me.

Piflyer commented 5 years ago

I accidentally plugged in the wrong USB port on the Pi Zero, however, it seems like Github does have authentication issues with my device.

Piflyer commented 5 years ago

It does also seem like this key could only be used to authenticate one account for some reason. I tried adding my Gmail and Github account, and both had errors saying it was the wrong security key, even though it worked the first time with Gmail.

Crystalix007 commented 5 years ago

If you powered off the device between registering Gmail the first time, and failing to authenticate the second time, hopefully you read the warnings section.

Basically, this device needs a software power-off. The SD card itself requires it in order to not lose data, and the program requires it, in order to receive the SIGINT signal to save before closing. Otherwise, you may have registered, but the registration may have never saved on the sd-card.

Potentially, I could make it so that any keyfile-modifying instructions saved the keyfile themselves, however, the device should still be powered off (via software) technically for the SD card anyway.

To shut down the Pi safely, just ssh in and run sudo systemctl poweroff.

Piflyer commented 5 years ago

Hi, I tried doing a software shutdown, but for some reason, the key isn't saved at all. I believe every time the device turns on, the key changes.

Crystalix007 commented 5 years ago

Don't update to HEAD yet. I want to fix the underlying issue first, before making it too rare to debug.

Step 0

First, the private keys should be stored at /usr/share/U2F_Priv_Keys.txt. Assuming that you aren't currently using or are able to use any of the logins located here, just delete the file. Otherwise, move this keyfile somewhere else (i.e. back it up somewhere else and delete).

Stop the already running service with sudo systemctl stop U2FDevice.service. If not already running, run sudo Scripts/Kernel_HID_Config.sh.

Step 1

Then, try running the program from the git directory with sudo ./U2FDevice, register with Gmail, or any other problem service, and then close the program with Ctrl-C in the terminal window.

This should stop the program correctly, and leave the public and private keys in /usr/share/U2F_Priv_Keys.txt.

Double check (open) the file. It should have a format like:

<key handle> <app parameters> <private key> <public key> <key auth count>

The key handle should be a number (as far as I remember); app param, priv key, pub key should all be base64 sequences; and key auth count should be another number.

You should have one line per key you test.

If there are no values in the file, tell me, and we can continue to debug that. Otherwise, continue to step 2.

Step 2

To test whether the keys remain the same, copy this keyfile somewhere else. I shall call this copy A. And then re-run the program with sudo ./U2FDevice.

Go to the same sites as you registered previously, in step 1, and try to re-authenticate. If this fails, the keys are changing somehow. As before, tell me and we can debug this in further depth, otherwise continue below.

Again, stop the program with Ctrl-C, and open up the keyfile (/usr/share/U2F_Priv_Keys.txt). Compare it manually to the previous copy (copy A) which you stored.

Assuming you didn't register for any new services, the file should look identical, except for the key auth count (the number at the end of each line). If you did register for any new services, they should just appear as new lines below the existing ones.

If this still looks fine, then the issue was likely a corrupted keyfile being loaded. If for any reason the program did fail mid-save (or the filesystem for that matter), the program currently has no way of checking when it loads the keyfile the next time.

If the corruption is bad enough, the program should simply crash on start with a message like Invalid keyhandle format on line..., or if the corruption is really bad, then simply crash with a non-zero return code. However, I haven't actually manually verified that arbitrary corruption would not be able to corrupt future keys (i.e. new keys you were attempting to register after the corruption). Theoretically, it should simply refuse to run, but, this is not assured.

If you got this far, and everything is still working, double check the original file compared to the format, and see if you can spot anything immediately wrong. If it still looks fine, then try and upload a syntactically similar keyfile (i.e. one that looks the same, but with different key values).

Don't actually post your real keyfile however, as this would compromise any recoverable keys.

A PCRE regex you can use to check each line is:

^\d+\s+[\dA-Za-z+\/=]+\s+[\dA-Za-z+\/=]+\s+[\dA-Za-z+\/=]+\s+\d+$

This doesn't guarantee that the keyfile is correct, but if it doesn't match a line, it's probably corrupted.

Addendum

I recently pushed to master a commit which should automatically save the keyfile as soon as a modifying command comes in. Filesystem corruption and the fact that it saves when the program stops still means that you should always use a software shutdown, but if the issue was simply the program never saving, then this should provide a good safety-net for that kind of issue.

Piflyer commented 5 years ago

It seems like the new update has successfully solved the problem. Thanks!

Piflyer commented 5 years ago

However, Gmail does work, but it seems like Github specifically has an issue with this protocol.

Crystalix007 commented 5 years ago

I'm going to assume that you're using Firefox, or some derivative. I tested Firefox version 68.0.2, on Manjaro Linux (tested on 7/9/19).

Testing with my own Raspberry Pi, it seems that Firefox will entirely crash when attempting to use it on GitHub. I'm unsure whether this is to do with having multiple keys on the device, or an existing U2F key assigned on GitHub.

Digging a bit deeper, the underlying U2F library being used by Firefox for this authentication seems to really dislike errors. When trying to register / authenticate with the Raspberry Pi, it seems like GitHub attempts to use a key-handle of incorrect length (if you run systemctl status U2FDevice.service' it will say something likeInvalid key handle... U2F_Msg_CMD::error 27264`).

What's occurring here is (I think) an error in the underlying library. As per the U2F specification, when the browser attempts to request a key with an invalid key-handle (as in, the key-handle is not possibly valid for the device), the device should return an error code SW_WRONG_DATA = 0x6A80 = 27364, as seen in the debug messages earlier.

Firefox seems to be unable to cope with this (similarly, it cannot cope if the device times out, but that's a different issue). If you try Chromium, or a derivative, such as Chrome, it handles this error successfully, and the authentication / registration proceeds as normal.

I'm unsure about how to solve this issue, as it seems to be a lack of compliance with the standard on the part of Firefox. Theoretically, I could just ignore the standard, but then that would be non-standard behaviour. Additionally, if I do ignore the standard, I don't know how to tell Firefox that it needs a different key.

The only solution I have right now is to use Chromium or a derivative. That's not really a solution, because free software should always be preferred, but in this instance, I don't know if there is an alternative.

Potentially, it may be that some other error code will not break Firefox, in which case I could add a patch file to fix it for those people who accept Firefox's non-standardness.


Where does this occur

See U2F_Authenticate_APDU.cpp line 51.

Where is the error in Firefox

I believe the error is occurring in Firefox at dom/webauthn/U2FHIDTokenManager.cpp, line 270, void U2FHIDTokenManager::HandleRegisterResult(). Or, alternatively, wherever the calls from dom/webauthn/U2FTokenManager.cpp, line 349, U2FTokenManager::DoRegister() go to.

In U2FHIDTokenManager, if the result is an error, it performs mRegisterPromise.Reject() call. I don't know the source code of Firefox, so this is the best I can give you, but probably this handling is failing somehow.

Piflyer commented 5 years ago

From my point of view, it seems like the problem is GitHub itself. I'm currently using Chrome on Macos Catalina, and other websites do work. It seems like GitHub is authenticate the key, but struggles to verify it when logging in.

Crystalix007 commented 5 years ago

I may have actually misspoken. The tests I did before were unfortunately on an old commit, which may have influenced where I saw the error.

I did some deeper investigation today, and still I'm not sure I found the root cause, but I did fix quite a few issues, so HEAD version should be less buggy.

Since I typically used to test without the service running, instead running the program directly, I didn't realise that systemd actually sends a SIGTERM on stopping, rather than a SIGINT. I already implemented a similar fix for the android branch, but I forgot to backport. Now, I have added SIGTERM to the list of signals that the custom signal-handler handles.

Overall, this may actually fix an issue of corruption.

On a side-note, I am now getting reliable log-in (with Firefox) on GitHub. I additionally have two different U2F devices assigned, so GitHub does actually check multiple keys, and it's still working.

Crystalix007 commented 5 years ago

If you update (you will need to re-add the key to GitHub) and it still doesn't work, I might want to see the debug files. Just add the key to GitHub, then run a sudo systemctl stop U2FDevice.service, and send the resulting files:

/tmp/comdev.txt
/tmp/comhost.txt
/tmp/devAPDU.html
/tmp/devpackets.html
/tmp/hostAPDU.html
/tmp/hostpackets.html

to me at michaelkuc6 <at> gmail <dot> com. Just replace the words with the correct symbols.

Since this does reveal quite a bit, you should then delete the key from GitHub.

Crystalix007 commented 5 years ago

It turns out the real issue is actually due to GitHub using multiple "app parameters". This basically just means that they check each key with multiple different names.

So, they first check whether you can sign a key for one (GitHub) app, and then, if you cannot, check if you can sign for another app.

Before the commit which fixes this, I was not erring correctly when it sent the wrong app parameter. So effectively, they requested a signature for GitHub (A?), and got a signature for GitHub (B?), which was not compliant with the specification.

Thanks for finding this. There was potentially a leak of information before this change, as anybody could have requested a signature for any app so long as they guessed a correct key handle. I don't think they could really do much with the signature, but it may have been an attack service, so much better this was closed.