-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
U2F implementation #56
base: main
Are you sure you want to change the base?
Conversation
This implements enough of the CTAP1 protocol to complete transactions, albeit with hard-coded responses that don't pass cryptographic muster.
This changes the u2f implementation around somewhat, so it no longer hard-codes lengths of some variable length fields. It moves the crypto functions out to their own file, ahead of implementation.
Fix reading of config zone to get all 128 bytes, split initialisation into a helper function and initial (non-working) version of genkey.
This sets up slot configs, suspected bit packing errors causing this not to work. It also exposes the lock functions in Python. Be very careful.
This allows provisioning the 108A to the point that genkey can be called, and offers helper functions for some basic funtionality. These functions can easily brick your badge, so don't run them unless you know what you're doing. The crypto outputs haven't been verified yet.
At this stage, the critical cryptographic functions of the U2F implementation are linked in. Unfortunately, it's not yet working, currently because the signature parameters aren't parsing correctly. I'm somewhat concerned that the fido raw message formats document specifies that cryptographic signatures are over the input bytestring, rather than the SHA-256 of that bytestring. The 108A only allows signatures of 32-byte strings, so I've gone that way. I don't think that's the problem I'm seeing yet though, I think it's a more general problem parsing. In addition, the code is currently hard-coded to use handle 1 for attestation and handle 6 for authentication. This is because only handle 6 is set up correctly on my main test device. Handle 1 will need the keys from keys/* loaded into it - I'm aware that it's silly to put a key in git, but in this case it's not part of the trust path and we have no way of distributing the key without exposing it unless we do it in person in 2024. No work yet on setting up the UI.
drivers/tidal_usb/u2f_crypto.c
Outdated
uint8_t signature[64]; | ||
|
||
ESP_LOGI(TAG, "Calculating digest"); | ||
atcab_hw_sha2_256(signature_input, 69, digest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where does this 69 length come from? the data to be signed over is (1+32+32+L+65, L=1, 131) bytes long isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, this is wrong. 69 is the length of the thing to be signed during authenticate, 131 is the length to be signed during register. The register signature is there to do the attestation, which we expect to fail anyway (the attestation key isn't provisioned in slot 1), but this would make it doubly wrong.
This might have done it.
Co-authored-by: Skyler Mansfield <[email protected]>
The following is a minimal reproducer of the problem:
This works on working slots/devices and fails on non-working ones. Checking the revision matches the one that I have that works is |
We should arrange a fourth birthday party for this PR ❤️ |
@Jonty It's not THAT old just yet! Not even three! |
@Jonty Although, I might have to get some "EMF 2026? I'm still working on EMF 2022!" badges. |
I am bad at years, clearly. |
Add support for using the TiDAL as a secure 2nd factor authenticator.
Current status
The protocol for USB 2 factor auth support is complete and working on some devices. Users can register a TiDAL as an authenticator and authenticate requests, however this isn't working reliably across devices.
This branch contains a custom firmware that allows testing this functionality, however it requires that the crypto chip has been provisioned before it can be used. Once it's provisioned it cannot be reset, it is a destructive operation. We are not confident that the provisioning is correct. In particular, it may well require changes to support attestation. It will also prevent you being able to provision a known key onto the device, which will limit debugging.
This is done by calling
ecc108a.provision_slot()
followed byecc108a.lock_config_zone()
This is likely the problem, the following config (from
ecc108a_tools.run()
) works on slot 6 only:but this one, where all keys replicate slot 6's config above, no slots work:
Excessive detail
HID mode
The badge
FIDO USB HID / CTAPHID
Interactions with U2F devices are tunneled over the USB HID protocol. An explanation of how those messages are sent is in the USB HID section below.
These messages are detailed at https://fidoalliance.org/specs/fido-u2f-v1.0-ps-20141009/fido-u2f-hid-protocol-ps-20141009.html
The implementation in the badge is at https://github.com/emfcamp/TiDAL-Firmware/blob/feature/u2f-mode/drivers/tidal_usb/tidal_usb_u2f.c
There are three commands implemented, init, wink and msg. Wink is what makes a device flash for attention. Init begins a session, and 'msg' contains a message for the next protocol down in the stack.
FIDO / CTAP
That next protocol down is FIDO / CTAP. This is detailed in https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html and implemented in the
handle_u2f_msg
function of https://github.com/emfcamp/TiDAL-Firmware/blob/feature/u2f-mode/drivers/tidal_usb/tidal_usb_u2f.cThis message contains either a register or an authenticate command.
Register
In register mode, we need to allocate a 'handle' for the site. The handle is what the server sends to the device during authentication to identify itself. Many other devices use this to return an encrypted key, so the device then decrypts the handle, and uses that to sign the challenge. We don't have support for that, our handles are integers that reference which of the (few) key slots the crypto chip has is responsible for this site.
This is currently hard-coded to
6
, in https://github.com/emfcamp/TiDAL-Firmware/blob/feature/u2f-mode/drivers/tidal_usb/u2f_crypto.c#L122-L124We return from register with the handle, the pubkey to validate against, an attestation certificate and a signature. Attestation allows consumers to verify that an authenticator is itself authentic. For example, Yubikeys will be attested by Yubikey, to allow validation that the user is using one of an allowlist of known good authenticators
Authenticate
The authenticate message is relative simple, it contains a challenge, a counter and a signature. These are returned and checked by the host machine
Work remaining:
Hook up a GUI app that allows users to accept register requestsand pick a slot to store the key inUSB HID
In order to get this to work, we need to support more USB HID types than just mouse and keyboard. We have therefore switched from the TinyUSB implementation in
esp-iot-solution
to a vendored-in one with more options. We have moved some of the callbacks into the TiDAL USB driver and added support for the U2F HID types at https://github.com/emfcamp/TiDAL-Firmware/blob/feature/u2f-mode/components/tinyusb/additions/src/descriptors_control.c#L21-L58The HID implementation at https://github.com/emfcamp/TiDAL-Firmware/blob/feature/u2f-mode/drivers/tidal_usb/tidal_usb_hid.c always includes the standard HID code, but only includes the U2F hooks when enabled.
Work remaining:
Exposing multiple HID descriptors was causing issues. You need to choose betweenCONFIG_TINYUSB_HIDKEYBOARD_ENABLED
andCONFIG_TINYUSB_U2FHID_ENABLED
in sdkconfig.board. In future, having this switch at run-time rather than build-time would be preferable. This has not been a priority.ECC108a chip
The crypto chip is connected over i2c. The datasheet has many details. Some functions have been exposed in micropython, which may assist you in debugging. These are in the
ecc108a
module. There is also anec108a_tools
module which contains helper code for understanding the config zone.None of the cryptography functions will work until the config zone is locked, and none of the provisioning and config controls will work once it is locked.
Below is an example transaction, which may be useful for people attempting to validate the behavior. The verify method is not working..
Work remaining:
verify
methodDebugging end-to-end
I recommend using Wireshark in USB mode and
u2fcli
for debugging.