solokeys / solo1

Solo 1 firmware in C
https://solokeys.com/
Other
2.3k stars 274 forks source link

Solo support in fwupd/LVFS #221

Open Forage opened 5 years ago

Forage commented 5 years ago

Hi,

I'm sure your todo list(s) are already quite long, but would it be an idea to add support for the solo key to fwupd? It would make firmware updating a bit more 'standard' for us linux users instead of using the 'hidden' (hint ;) ) https://update.solokeys.com/ page, also allowing updates to be provided automatically.

nickray commented 5 years ago

Speaking about hidden, you're aware of https://github.com/solokeys/solo-python, yes?

Regarding fwupd, I think it's about bigger devices (PC, phone), no?

Forage commented 5 years ago

I was not aware of that script no, thanks.

Fwupd is for any size device. From 'simple' open source USB devices like the colorhug (by @hughsie, the same person who also created fwupd) to motherboard bios updates.

nickray commented 5 years ago

Interesting! Will investigate and keep in mind if/when we revisit the update process. We do need to be cross-platform though, and our current method is in a sense probably simultaneously too much and too little of a protocol (custom extension of CTAP2) to integrate with fwupd as is.

hughsie commented 5 years ago

custom extension of CTAP2

Have you got any documentation on the protocol used? I'd be happy to help write a plugin for fwupd users.

nickray commented 5 years ago

Hey @hughsie, wow, thank you for your interest!

So what we do is reuse the main firmware's CTAPHID implementation in the bootloader. There's a concept of "vendor specific commands", of which (if memory serves me correctly) we use two: 0x40 ("write") to erase/write blocks (while calculating the signature, and 0x41 ("done") at the end to check the signature is valid. If it's not a "secure" key (i.e., a "non-verifying" bootloader build), then the signature check is a NOP. Else, if the signature is invalid, the key remains in bootloader mode until you flash a firmware with valid signature.

It should hopefully be relatively easy to follow what our Python tool does when you call solo key update.

For context (not needed for a potential fwupd integration), the web updater does an additional hack on top of this (encoding this process in WebAuthn navigator.credentials.get calls). This is to avoid having to use WebUSB (which Firefox won't support).

I think fwupd is in C? You need some kind of CTAP2 client library (actually I think just CTAP1, which is U2F under a new name, works too).

Kind of off-topic, but do you know of any efforts towards standardisation of a secure + small bootloader implementation that checks potential firmware update signatures?

nickray commented 5 years ago

With regards to acquiring the firmware, our policy is that https://github.com/solokeys/solo/blob/master/STABLE_VERSION contains the current stable version number, which you can then fetch programmatically from the release assets hosted under https://github.com/solokeys/solo/releases. I guess you'd mirror this in your own repo? In which case it might be useful to do your own Docker build, and check that it matches with ours (which gets our signature).

hughsie commented 5 years ago

I've done a bit of research today on the Solo. Would it be fair to say that the only device that makes sense to support using the "easy" fwupd GUI interface is the Secure, and not the Hacker? I think if you're the kind of person flashing over DFU for the "development" device then you're much better served with the existing solo CLI rather than the point-and-click notifications.

The other thing is that the stable firmware would need to be wrapped up with metadata into a cab file and uploaded into the LVFS. @nickray if you send me your contact details by email I can create you an account for the LVFS so you can play around with the service a bit (it's all free), but that's only if you would give the LVFS permission to mirror the firmware binaries themselves as that's a prerequisite of being on the LVFS.

As for the binary to ship inside the .cab archive, I notice there are json and hex file versions. Any preference to what is supported in fwupd? fwupd can already read IHEX files, and also already links to json-glib, so it's really whichever is easier to produce. I also notice there's no signature in the ihex file, which I can help include as an extra section if that's the route you want to take.

If all that sounds appealing, I can buy a few devices and build a fwupd plugin pretty quickly, I don't think it's much work at all.

nickray commented 5 years ago
hughsie commented 5 years ago

I think we just need to use the JSON as the "deliverable" in the cabinet archive. I have some PTO all of next week and then will start on a fwupd plugin on my return.

hughsie commented 5 years ago

I received a SoloKey today. Some initial comments:

Some questions:

nickray commented 5 years ago
* The device uses the STMicroelectronics USB VID of 0x0483 -- which was unexpected and suboptimal for a few reasons, e.g. you can't filter on the vendor+product ID as a security check and the vendor will need to be quirked in fwupd.

* The secure bootloader seems to use the same VID+PID as the runtime :(

Can you add some arguments why these two should be changed? We theoretically have http://pid.codes/org/SoloKeys/ available (and I'm personally in favour), but at some point there was an argument made that a proliferation of pairs would lead to udev rule complications. On the other hand, that was before https://github.com/Yubico/libu2f-host/blob/master/70-u2f.rules#L58-L59

* The BCD version of the device is hardcoded as `1.00` rather than the firmware version of `2.3.0` -- although I appreciate the BCD version format is somewhat restrictive. I think the only way around is for fwupd to `claim()` the HID interface and parsing the iProduct string, or issuing a u2f command to get the firmware version.

We put the firmware version in the product string, yes. So the argument is that using the BCD version would make it easier/more direct to detect the version? This would probably be favourable.

I think this would only give you major.minor though, whereas keeping track of patch updates should be something fwupd tracks, right?

Some questions:

* I need to be able to detect the hacker/secure devices without claiming the interface (so no way to issue a command). Can someone with a hacker please tell me if the USB PID is the same as the Secure version, and if the `iProduct` string is different in some way. I assume there's no U2F way of doing it.

I don't think this is reliably possible, then only thing you can do is check whether a genuine Solo Secure is connected, via the attestation method.. Keep in mind that the hacker device can have any kind of change. Seems fine to me if fwupd would only handle Solo Secure updates.

The product string is indeed different though, starting at around v1.0.0 (original Kickstarter supporters have even older versions): https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L36-L40

* Is the firmware version always going to be a 'triplet', e.g. `x.y.z`?

Yes, we're supposed to be following regular semver.

* Is downgrading versions supported? Will it work in all situations? If so, isn't that a downgrade attack?

Currently there's nothing preventing downgrades, although we have discussed preventing this. The two currently "advertised" methods (solo key update and https://update.solokeys.com) do nudge only towards updates, and downgrading as such is not a supported operation.

* What's the `\x11\x11\x11\x11\x11\x11\x11\x11` initialisation fix when in HID mode?

@conorpp can you chime in?

* What's `\x8C\x27\x90\xf6` meant to be?

This is just a (random) "magic" number, assumed not to occur in practice. It's the basis of supporting web updates via abusing WebAuthn signature requests. I don't think fwupd needs this.

* On the Solo Secure you have to enter the bootloader using the button-clicked-when-inserted trick, but the hacker seems to be able to reboot itself into bootloader mode. It would be convenient if fwupd could reboot a Secure device into bootloader mode automatically, rather than asking the user to do it. Given the Secure will only accept signed firmware and hopefully only accepts newer-or-the-same version firmware, listening on HID (only) shouldn't add any attack surface, right?

@conorpp?

* What's the minimum and maximum size of firmware fwupd should allow to be flashed? e.g. `0x2000<=sz<=0x8000` or is all firmware the same exact (padded) size?

(Current) flash layout is defined here: https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/memory_layout.h We intend to stick with it, although the upcoming OpenPGP extension may change things a little.

* Without parsing the `iProduct` how can I tell if a Solo Secure is in bootloader mode?

@conorpp?

hughsie commented 5 years ago

Can you add some arguments why these two should be changed?

It's a lot more robust than relying on firmware quirks or parsing indexed strings. This is only the second device I've come across (out of hundreds) that has the same VID/PID for bootloader and runtime modes.

I think this would only give you major.minor though, whereas keeping track of patch updates should be something fwupd tracks, right?

Yes, fwupd cares about the three digit semver, although I there are two schools of thought for the bcdVersion -- and a vocal one that thinks it represents the hardware version i.e. the PCB revision rather than the firmware version. I don't think it's super important to fix if we have to parse the semver anyway.

One further question which is important for fwupd is how to get the runtime version when in bootloader mode? The iProduct string is the unchanging bootloader version, as is the 0x44 command.

hughsie commented 5 years ago

Some progress:

[hughsie@localhost 2.4.1]$ fwupdmgr install SoloKeys-Secure-2.4.1.cab --allow-reinstall
Decompressing…           [***************************************]
Authenticating…          [***************************************]
Device Solo Secure needs to manually be put in update mode: Unplug the device, press and hold the button whilst re-inserting, and release when the light is flashing yellow.
[hughsie@localhost 2.4.1]$ fwupdmgr install SoloKeys-Secure-2.4.1.cab --allow-reinstall
Decompressing…           [***************************************]
Authenticating…          [***************************************]
Installing on Solo Secure…-                                      ]
Verifying…               [***************************************]

This is using the fwupd branch here: https://github.com/hughsie/fwupd/pull/1259 and the metadata here: https://github.com/hughsie/solokeys-lvfs

To clarify slightly, the solokeys-lvfs repo is designed to be populated by your guys at release time, with the versions changed and the metadata included in the XML. Doing "make" creates a .cab file that includes the JSON blob of firmware and the XML file and that can be uploaded to the LVFS. I've included the line art in that repo as it matches the style of all the other devices we support and is what's shown when the user does the update in the GUI. If you want to copy-and-paste the metainfo.xml, lineart or makefiles that's 100% fine, I'd even be happy to transfer the solokeys-lvfs to https://github.com/solokeys if that helps.

Getting that fwupd branch merged and uploading the firmware to the LVFS you get the "three green ticks" which unlocks a few of the corporate and government customers you are probably interested in. :)

nickray commented 5 years ago

One further question which is important for fwupd is how to get the runtime version when in bootloader mode? The iProduct string is the unchanging bootloader version, as is the 0x44 command.

I think the bootloader doesn't know the firmware version, or at least doesn't have a command to return it. The tricky thing to fix this is that the bootloader is thought of as immutable, at least there's no way to update it currently. So this means that a lot of people with Secure Solos have rather old bootloaders, with no way to update, unless we'd craft a particularly clever firmware update that overwrites the bootloader on first run (or similar). Do you know of safe/sane methods to update a bootloader?

All that said, I think improving this design is more likely to happen in a future hardware iteration, where we have separate (ported) firmware builds, than now.

Populating the solokeys-lvfs at release time though is something I'll try to make happen in the short term. Very cool you're helping us out here so much!

conorpp commented 5 years ago

Really awesome to get Solo supported in fwupd!

Answering some questions:

MEMORY
{
    flash (rx)      : ORIGIN = 0x08005000, LENGTH = 198K - 8
    ram (xrw)       : ORIGIN = 0x20000000, LENGTH = 48K
    sram2 (rw)      : ORIGIN = 0x10000000, LENGTH = 16K
}
hughsie commented 5 years ago

unless we'd craft a particularly clever firmware update that overwrites the bootloader on first run

On most hardware I've seen with the bootloader design the partition of flash with the bootloader is read-only, i.e. can't be programmed without an external programmer. If this isn't the case here then I guess you could update the bootloader from the flash loader although that does seem a bit risky. It's probably more sane to workaround old bootloaders in the fwupd plugin.

Given the fwupd plugin has been merged, I think this issue can be closed. When the firmware is uploaded to the LVFS we can do the normal PR announcement thing.

nickray commented 5 years ago

@hughsie I'm having an issue I can't resolve (not least since I know little about fwupd's internals). I believe I installed from fwupd/master properly (on Debian Buster).

What (I think) happens is that /usr/local/libexec/fwupd/fwupd somehow claims Solo. Symptoms are that /dev/hidraw0 turns up in dmesg, but no /dev/hidraw0 is created, and as a consequence, solo ls (from our solo-python) no longer lists any keys. Moreover, browsers can't access the key anymore either.

Can you confirm that the keys still work for you when fwupd is running? If so, any suggestions how I can go about debugging this issue on my side?

hughsie commented 5 years ago

Its plausible I forgot the interface release in my code, I can check Monday.

hughsie commented 5 years ago

My mistake entirely, my apologies. All fixed in master: https://github.com/hughsie/fwupd/commit/c6dba62cf04bc607fba7291ed79a87ba46848b09

nickray commented 5 years ago

@hughsie Thanks, but not quite :) If it's OK, I'll continue to report perceived issues here.

hughsie commented 5 years ago

case of Solo Hacker

failed to add USB device 0483:a2ca: failed to add device using on solokey: Only Solo Secure supported

This is entirely expected and correct, no?

and seem to not release

I can't reproduce that -- are you sure the new code is deployed? You can also run fwupd from the checkout using ninja && sudo ./src/fwupd --verbose if that helps.

case of Solo Secure in Bootloader mode, with older bootloader (identifies as Solo Keys Solo)

What does the old bootloader use for the string descriptor in the case of a Hacker variant? Solo Keys Hacker perhaps? It would be good to get a table of all the possible strings for both variants of the device including all the historical versions too.

I get "Authentication required" from PolicyKit. Why is this needed?

It's fwupd default site policy to require the admin password for any local or unsigned (as in, unsigned from the LVFS, rather than unsigned from the device PoV) firmware. If the firmware is downloaded from the LVFS and deployed using fwupd there is no auth dialog.

moreover, once I'm in bootloader mode (with a secure Solo)

Is this with a very old bootloader? Bear in mind fwupd has to "see" the device in runtime mode before it "sees" it in bootloader mode, otherwise it doesn't know the device firmware version (as the bootloader is unable to tell us that). Other devices have the same limitation, so that should work.

nickray commented 5 years ago

@all-contributors add @hughsie for ideas, code, infra, tools

allcontributors[bot] commented 5 years ago

@nickray

I've put up a pull request to add @hughsie! :tada:

returntrip commented 4 years ago

Can we now use fwupd to update our Solo Secure keys? I see the FW is stuck to 2.4.2: https://github.com/fwupd/solokeys-lvfs/tree/master/releases/SoloSecure/

I have raised a PR. Hopes it helps a bit with adopting fwupd