Skip to content
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

Pre-flight and AppID improvements #297

Merged
merged 5 commits into from
Aug 24, 2023

Conversation

jschanck
Copy link
Collaborator

The first patch splits StateMachine::init_and_select into distinct init and select stages. It adds an auto_select hint to the select stage that can be used to skip device selection when the caller can determine that a particular device is suitable for the request.

The second patch uses the auto_select hint when we find acceptable credentials on a device through silent discovery. This resolves Bug 1451111

The third patch uses silent discovery to determine if there are acceptable (U2F) credentials on the device that require the use of the AppID extension. This resolves #269 and Bug 1846836.

I also added a -a flag to the ctap2 example which causes it to use an alternate RP ID. (This is added in the second patch but is used to test the third patch's behavior).

@jschanck jschanck requested a review from mozkeeler August 23, 2023 04:03
src/statemachine.rs Outdated Show resolved Hide resolved
if !allow_list.is_empty() {
// Try to silently discover U2F credentials that require the FIDO App ID extension. If
// any are found, we should use the alternate RP ID instead of the provided RP ID.
let silent_creds = silently_discover_credentials(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we do this here again? Didn't we already do this in StateMachine::sign()?
Couldn't we reuse the silent_creds-list we acquired in StateMachine, and simply overwrite the args.allow_list and args.relying_party_id there, before handing it in?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StateMachine is only used by USB HID devices, and I intend to write virtual device tests for this.

Comment on lines +371 to +375
if dev.get_protocol() != FidoProtocol::CTAP2 {
info!("Device does not support CTAP2");
let _ = selector.send(DeviceSelectorEvent::NotAToken(dev.id()));
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really important, but why remove that from init_device() and duplicate it here multiple times?
Then all NotAToken()-messages would be in the same place.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just seemed logically separate from device initialization. I could go either way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, from a 'functionality separation'-POV, this makes sense. I just slightly dislike the code duplication. But given that both solutions have their drawback, I'm fine with either, too. :)

Comment on lines 300 to 303
// Attempt to silently discover credentials. If we find useful credentials, then we
// can potentially skip a device selection tap. We may find acceptable credentials
// on several devices. In that case, the first device to ask for automatic
// selection will be selected.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand the auto_select feature, yet.
Why do want potentially racy device-selection?
I figured, we filter out devices that provide no suitable credentials here, by telling the DeviceSelector, they are NotAToken(). Then the DeviceSelector can decide if only a single device was discovered, and thus the selection tap can be skipped, or if multiple are discovered and a selection tap is necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't send NotAToken, because we need to blink at least one of the connected tokens. So we would need a new DeviceSelector message like NotAUsefulToken.

As for why we want this, suppose a user has multiple tokens connected and consider these cases:

  1. Only one token stores a valid credential, but they forget which.
  2. Several tokens contain valid credentials, and the relying party will accept any of them.
  3. Several tokens contain valid credentials, and the relying party will only accept some of them.
  4. Several tokens contain valid credentials, and the user really wants to use Token 1.

I believe 1 and 2 are common, and auto_select improves user experience in both.
As for 3, the current patch is not perfect, but with a few changes auto_select will improve UX in this case too. We just need to check that the device is suitable for the request, e.g. by checking the user verification requirement, after init and before wait_for_device_selector.

I don't think there's anything we can do about case 4. IMO it's not worth sacrificing the UX improvements for cases 1, 2, and 3 for the whim of a user who could just unplug their other tokens.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't send NotAToken, because we need to blink at least one of the connected tokens.

Right, I hadn't considered this.

My question was indeed mostly from the standpoint of 4.
I don't have a good overview right now, how this change works with resident keys / discoverable credentials. There, a user may have account A on token 1 and account B on token 2, and thus it's important which token is used.

I do like the idea of having NotAUsefulToken, where the DeviceSelector could basically keep track of them, instead of throwing them away immediately. If one or more useful tokens is found, it can cancel the not useful ones as usual. And if all devices report back as being not useful, it could just send a dummy blink or simply Continue to all of them.
Maybe in a follow-up PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to disable auto_select in sign for now. I want to get the AppID changes into Firefox 118, and I think you're right that this feature isn't ready to go.

Copy link
Collaborator

@mozkeeler mozkeeler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable.

@@ -1155,7 +1146,7 @@ pub mod test {

// Sending first GetAssertion with first allow_list-entry, that will return an error
let mut msg = cid.to_vec();
msg.extend(vec![HIDCmd::Cbor.into(), 0x00, 0x94]);
msg.extend(vec![HIDCmd::Cbor.into(), 0x00, 0x90]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a huge fan of "this is the magic length byte (I assume?) that you have to know to modify if you change anything below", but I think that's out of scope for this change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I filed #298.

@jschanck
Copy link
Collaborator Author

I opened #299 to revisit auto_select.

@jschanck jschanck merged commit c556ed4 into mozilla:ctap2-2021 Aug 24, 2023
@andreydanil
Copy link

@jschanck, Can this fix be backported to earlier versions of FF? It's impacting FF versions 115-118.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pre-flighting breaks AppID support
4 participants