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

Rework CTAP2.0 GetPinToken-workflow #237

Merged
merged 3 commits into from
Mar 27, 2023
Merged

Conversation

msirringhaus
Copy link
Contributor

Trying to rework the GetPinToken-workflow, in order to work for tokens with and without UV.

Tested this with Yubikey BIO.
Would be great to if @andreydanil and @VincentVanlaer could also test this with their ThinC and Mooltipass respectively.

To test, run examples ctap2 and ctap2_discoverable_credentials with this patch and see if

  1. the good case works
  2. using the wrong finger / PIN will act as expected as well (either repeatedly ask you to touch it, or error out completely after 3 tries).

@msirringhaus
Copy link
Contributor Author

Once #205 lands, this needs of course some rebasing, and then it should be hopefully rather straight forward to implement the CTAP2.1 auth-workflows.

@andreydanil
Copy link

Nice! I can confirm that this code change does not break ThinC CTAP2.0 token.

I wasn't able to build this change into Firefox due to a reason with a dependency with error error: could not compile authrs_bridge due to previous error.

Compiling authrs_bridge v0.1.0 (/Users/andreydanil/development/github/mozilla-unified/dom/webauthn/authrs_bridge)
error[E0004]: non-exhaustive patterns: `Ok(StatusUpdate::PinAuthInvalid)` not covered
    --> dom/webauthn/authrs_bridge/src/lib.rs:338:15
     |
     |         match status_rx.recv() {
     |               ^^^^^^^^^^^^^^^^ pattern `Ok(StatusUpdate::PinAuthInvalid)` not covered

I would love to test UV = Discouraged, Preferred, Required once I can build this into Firefox.

src/statemachine.rs Outdated Show resolved Hide resolved
src/statemachine.rs Outdated Show resolved Hide resolved
callback.call(Err(e));
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, let's add a helper function for the error cases. There's a lot of duplication with sign, and right now they differ unnecessarily.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sadly, they do NOT differ unnecessarily. They actually are different, because the spec (or at least how vendors interpret it) are different for GetAssertion and MakeCredential, in what they return in case of errors.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't we just handle the difference in repackage_pin_errors? If not, I think we should avoid using repackage_pin_errors entirely. A flat list of cases would be easier to follow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we need repackage_pin_errors in other places as well, I've introduced a new function (which also uses it), that handles the differences, so we can move all of it out of statemachine.rs

Copy link
Collaborator

@jschanck jschanck left a comment

Choose a reason for hiding this comment

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

Looks good. Some minor comments below.

src/statemachine.rs Outdated Show resolved Hide resolved
src/statemachine.rs Outdated Show resolved Hide resolved
src/statemachine.rs Outdated Show resolved Hide resolved
callback.call(Err(e));
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't we just handle the difference in repackage_pin_errors? If not, I think we should avoid using repackage_pin_errors entirely. A flat list of cases would be easier to follow.

@VincentVanlaer
Copy link
Contributor

I tested this PR with my mooltipass:

@msirringhaus
Copy link
Contributor Author

Thank you @andreydanil and @VincentVanlaer for testing.

@VincentVanlaer: Could you open an issue for the failing ctap2_discoverable_creds-example?

Ok(PinUvAuthResult::SuccessGetPinToken)
}

fn handle_auth_command_errors<D: FidoDevice>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

This does not really "unify the cases" as we're now just pushing the dependence on Self::command() into this function. I'm sorry to go back and forth on this minor point, but I think we should do the flat list of cases instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So reverting this change and doing what we did before? Or something else entirely?
"Avoiding duplicate code, if possible" and "having a flat list" unfortunately are at odds in this case.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe it will be easier for me to just submit a follow up patch with what I'm imagining. This is not as clear as I'd like it to be, but it does appear to be correct.

}

let pin_auth = dev
.get_pin_token(&self.client_data_hash(), self.pin())
Copy link
Collaborator

Choose a reason for hiding this comment

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

This will ultimately call set_shared_secret on Device. Where are we doing that in the UsingInternalUv case?

Maybe we need to implement and integrate getPinUvAuthTokenUsingUvWithPermissions before we can land this patch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, you are right. I was under the impression, that we do not need the shared_secret, if we don't use a PIN. But we may need it for the hmac-extension (which I'm currently not sure, if it works correctly anyways).
I'll add a dev.establish_shared_secret()?; to the UsingInternalUv-case.

Waiting for getPinUvAuthTokenUsingUvWithPermissions will (probably) not change anything here, since the plan is to do that in a separate function (hence the name determine_pin_uv_auth_ctap_2_0()).
The InternalUv-case needs to work for CTAP2.0-only tokens as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK, sounds good. I didn't realize that Internal UV did not require the shared secret in 2.0, but we do need it for the hmac extension (even though it's not currently working).

@jschanck jschanck self-requested a review March 24, 2023 17:54
Copy link
Collaborator

@jschanck jschanck left a comment

Choose a reason for hiding this comment

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

Looks good!

}

let pin_auth = dev
.get_pin_token(&self.client_data_hash(), self.pin())
Copy link
Collaborator

Choose a reason for hiding this comment

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

OK, sounds good. I didn't realize that Internal UV did not require the shared secret in 2.0, but we do need it for the hmac extension (even though it's not currently working).

Ok(PinUvAuthResult::SuccessGetPinToken)
}

fn handle_auth_command_errors<D: FidoDevice>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe it will be easier for me to just submit a follow up patch with what I'm imagining. This is not as clear as I'd like it to be, but it does appear to be correct.

@jschanck jschanck merged commit 6700196 into mozilla:ctap2-2021 Mar 27, 2023
@VincentVanlaer
Copy link
Contributor

@VincentVanlaer: Could you open an issue for the failing ctap2_discoverable_creds-example?

I did some digging in the meantime and it seems that the mooltipass is not returning the name field, even though it is set when the credential is created (and is stored as I can see it on the screen). There does seem to be a possibility in the spec to not send the name field back when the user is not verified, but I consider that a discussion to be had on the mooltipass side whether this applies or not (I'm assuming it doesn't really apply and the mooltipass should send the name field)

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.

4 participants