-
Notifications
You must be signed in to change notification settings - Fork 72
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
Implement all commands of Ctap2 #154
Conversation
…COSE-crate is added and able to actually parse it
…g it (which would need a DER-parser)
…ich has to be calculated before hand
I don't really know much about the spec, so I was wondering whether the crypto-usage stuff, which requires #142 to be finalised and implemented, is a strict requirement or a "nice to have". I assume this PR can't be merged before the manager and statemachine are done and there are no more WIP commits then? Mostly just trying to understand whether webauthn support in firefox is in the near future or not at all. |
The crypto-stuff is a strict requirement for hardware tokens that support a PIN (which is pretty much most of the FIDO2 compatible ones). In my branch, I already have an openssl-backend going (probably highly insecure) which works, and am currently working on the NSS-backend (need to check for existing bindings or write my own). The statemachine and manager are mostly functional (although some CTAP1-backwards compatibility is still missing). Once all of that is done (assuming it passes review at some point), we still need to integrate it into Firefox (Rust<->C glue, new User dialogs, etc.). So yes, its coming, but not tomorrow. I still work more or less full time on this. Just look at my fork on the In case you (or @theSuess) want to help: More testcases would be nice. Esp. for the GetAssertion and GetNextAssertion commands. |
I'm coming back to this after a busy summer (sorry for the delay!), and was wondering what you exactly mean with testcases? Do you mean use our devices to test things, or more literally unit tests. The first one I could probably help with with my Mooltipass miniBLE, but the latter might be a bit out of my league since I don't know any Rust. I see @theSuess wrote msirringhaus#1 but I'm not too sure whether or not that's related. |
Now its my turn to apologize for the delay :-) And thank you for pointing out @theSuess 's PR to me, as I completely missed that one (github somehow didn't notify me about it). As a quick general update: This project has been on hold for the past two months or so, because of unrelated FF91esr-work (plus some more weeks of vacation), which is still ongoing. But hopefully, this will be done soon, so I can pick this up again. |
No worries, I presumed some vacation was for sure involved. Important to recharge your batteries. If you at some stage would like em to test with my Mooltipass miniBLE, then feel free to let me know! |
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.
Ignoring all the TODO stuff, this looks good so far, I just left a few suggestions here and there.
pub challenge: Challenge, | ||
pub origin: Origin, | ||
// Should not be skipped but default to False according to https://www.w3.org/TR/webauthn/#collectedclientdata-hash-of-the-serialized-client-data | ||
#[serde(rename = "crossOrigin", skip_serializing_if = "Option::is_none")] |
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.
We should make this #[serde(rename = "crossOrigin", serialize_with ="some_fn_that_serializes_none_to_false")]
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.
This is already fixed in my follow-up branch. It is not an Option anymore, and serde is using bool::default now.
Do you want me to backport that, or just look at it with the next PR?
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.
That's ok, we can look at it in the next one, then.
src/ctap2/client_data.rs
Outdated
{ | ||
let mut out = [0u8; 32]; | ||
if out.len() != v.len() { | ||
return Err(E::custom("unexpected byte len")); |
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.
I think we should use E::invalid_length
here instead of a custom error.
base64 = { version = "^0.10", optional = true } | ||
base64 = "^0.10" | ||
nom = "^5.1" | ||
sha2 = "^0.8.0" |
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.
if we use sha2 0.9, it uses digest 0.9, which in turn uses generic-array 0.13, which supports has impl Into<[T; N]>
for GenericArrays.
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.
I have tried to use the versions that are already vendored in Firefox, to avoid update procedures there that might cause problems. Currently, Firefox has sha2 in version 0.8.2.
If you think it's not a problem to bump that dependency in Firefox as well, I can go with 0.9.
hasher.input(&data); | ||
|
||
let mut output = [0u8; 32]; | ||
output.copy_from_slice(hasher.result().as_slice()); |
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.
It's nicer to just use hasher.result().into()
; see comment on Cargo.toml.
token_binding: Some(TokenBinding::Present(vec![0x00, 0x01, 0x02, 0x03])), | ||
}; | ||
|
||
assert_eq!( |
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.
Is this expected hash correct? AFAICS the implementation will not serialize properly when cross_origin is None
, so the test shouldn't pass right now.
src/ctap2/commands/mod.rs
Outdated
@@ -314,7 +324,14 @@ impl Into<u8> for StatusCode { | |||
pub enum CommandError { | |||
InputTooSmall, | |||
MissingRequiredField(&'static str), | |||
Deserializing(SerdeError), | |||
Deserializing(CborError), | |||
Serialization(CborError), |
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.
Can we name these two consistently? Either Deerializing/Serializing, or Deserialization/Serialization
src/ctap2/commands/get_assertion.rs
Outdated
Dev: FidoDevice + io::Read + io::Write + fmt::Debug, | ||
{ | ||
if input.is_empty() { | ||
return Err(CommandError::InputTooSmall).map_err(HIDError::Command); |
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.
I guess this is a matter of style, and I see it in many places, but it looks weird to me to just call map_err
like this when we could just use HIDError::Command(...)
; or alternatively we can just implement From
as appropriate and rely on ?
…'Parsing'-variant
…ls, by using the shorter .into()
That would be it from my side for now. Remaining questions are |
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.
This looks fine as is. The sha2 version bump we can handle later, and there's no need to spend time backporting stuff onto this if they're coming in the follow-up PR anyway.
These are now all the commands defined in the spec.
Not all functionality is there yet, and a few testcases are missing, as the spec doesn't give examples for those.
Most notably: All crypto-usage is still missing. For this, we need something like #142 with more functionality and then implement that for different crypto backends (NSS, openssl, ring, ..).
Next step would be to create a new Manager and StateMachine, which uses the new commands.