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

Metadata service support #208

Merged

Conversation

bdewater
Copy link
Collaborator

@bdewater bdewater commented May 20, 2019

Closes #175
Addresses #66 for packed and U2F attestation formats.

Some notes:

  • The CRL checking logic should probably extracted to the JWT gem
  • json_accessor helper to make working with JSON's camelCase and Ruby's snake_case less cumbersome, and dealing with data types JSON doesn't know about. Could easily be extended with an optional: true keyword arg to raise for specified mandatory elements but so far I'm not really convinced of the need
  • Policy (see processing rules) for acceptable authenticators is delegated to the application for now, see server.rb for how the conformance tools test for authenticators listed as compromised
  • remove the certificate_in_use? calls in the attestation response classes since the chain can then be verified using a X509::TrustStore using the root certificates from the metadata service, which includes this check

@grzuy
Copy link
Contributor

grzuy commented May 21, 2019

Wow, very nice!

I will try provide some feedback when I can.

@brauliomartinezlm
Copy link
Member

@bdewater I have finally had proper time to understand this more, go over Fido MDS docs and derived RFCs that I needed to refresh to give a more appropriate review.

I have not gone in depth on checking we're covering all the steps but have identified many of them in the processed described in 3.1.8 Metadata TOC object processing rules of the aforementioned doc. I figure it was not the goal of my first pass on this draft, but I'll cover it on my final stamp.

First of all, I can't tell you how much appreciation I have on you taking on such a complex and extensive part we're missing on our verification flows. This probably took you a lot of efforts and it's undoubtedly an excellent piece of work.

I don't have any concern or red flags on what I have reviewed. I didn't go to the details at this stage. The implementation and the architecture look great to me so far. I do have questions on which I would like to pick you brain a bit.

  • How is the gem user going to fire up the process of bootstrapping and populating the metadata store?

  • How is the gem user going to keep the metadata store up to date and perform necessary maintenance? Are you thinking of some side processing for this? I don't see this tied or fired by any webauthn existing flow, it will need to be something completely separate.

  • We need to minimize roundtrips for fetching metadata statements from authenticator vendors, so I guess we will be using a cache? What do you have in mind in such regard?

  • How are we going to persist the TOCs, dictionary of entries, etc? I guess this is coupled to the previous question.

  • Do you see this client living inside the gem and being extracted later? I keep seeing symptoms of this being an isolated component, with separate flows and contained complexity that lead me to think on this living outside the gem itself. I'm not suggesting we extract it now but there are certain flows (downloading and refreshing the store) that may grow into something worth considering it. My questions were already rumbling around store persistence and scheduled data refreshing (that might even mean running workers or other side processing).

Again, this is a tremendous effort and a great ton of high complexity, can't thank you enough!
Let me know if I can help on anything, or wanna chat any specifics in Gitter, I'd be glad to help keep this effort moving forward with all the capacity I can dedicate (sadly, sometimes is not much :( ).

Sorry this took me so long to jump on it and review 🙏

@grzuy
Copy link
Contributor

grzuy commented Jun 24, 2019

Some notes:

  • the metadata client does a lot and some of the CRL checking logic should probably extracted - I wasn't sure yet what that should look like, once a second use case shows to help guide forming the abstraction perhaps

Just chiming in to say agree with this.
Also like that you kept all the metadata client code namespaced with the Metadata constant.

In general, I would try to keep all FIDO MDS service logic as disconnected as possible to the WebAuthn core, which is the direction I see you are already taking. If we could connect them with a few method calls only, that would be ideal. Can even be converted into a separate library if we want in the future. Similar to https://www.npmjs.com/package/mds-client.

@bdewater
Copy link
Collaborator Author

Thanks for the review @brauliomartinezlm ! To answer your questions:

How is the gem user going to fire up the process of bootstrapping and populating the metadata store?

The gem will need to be configured with the MDS API key and a cache backend. I'm planning to add a simple in-memory backend for testing and and conformance testing, but RPs wishing to roll this out to production should really write a Redis/memcached/Active Record/etc adapter suitable for their application.

How is the gem user going to keep the metadata store up to date and perform necessary maintenance? Are you thinking of some side processing for this?

I was thinking the cache could work in a similar manner as Rails' implementation. It could fetch data on demand (TOC first, the detailed statement if needed), but it should also be possible to have a daily background job fetch all the MDS data to ensure there's always cached data to work with.

I don't see this tied or fired by any webauthn existing flow, it will need to be something completely separate.

It's implementing step 15 of verifying the credential registration. But like @grzuy already suggested the coupling should be fairly minimal, something like a valid_attestation_trustworthiness? method on AuthenticatorAttestationResponse that calls to the cache, gated behind the configuration.verify_attestation_statement value.

Do you see this client living inside the gem and being extracted later? I keep seeing symptoms of this being an isolated component, with separate flows and contained complexity that lead me to think on this living outside the gem itself.

Maybe, but given this being tied to registration where the RP wishes to request and verify attestation I feel there's also a case to be made for it staying a part of the gem, even if it's minimally coupled. I'm not seeing the value of a standalone client just yet but maybe you can convince me otherwise 🙂

After this is done I'll focus on upstreaming the x5c JWT verification and CRL handling from the Metadata::Client to https://github.com/jwt/ruby-jwt first.

@brauliomartinezlm
Copy link
Member

The gem will need to be configured with the MDS API key and a cache backend. I'm planning to add a simple in-memory backend for testing and and conformance testing, but RPs wishing to roll this out to production should really write a Redis/memcached/Active Record/etc adapter suitable for their application.

Yeah, that's reasonable 👍

I was thinking the cache could work in a similar manner as Rails' implementation. It could fetch data on demand (TOC first, the detailed statement if needed), but it should also be possible to have a daily background job fetch all the MDS data to ensure there's always cached data to work with.

Had a similar potential outcome in mind 👍

It's implementing step 15 of verifying the credential registration. But like @grzuy already suggested the coupling should be fairly minimal, something like a valid_attestation_trustworthiness? method on AuthenticatorAttestationResponse that calls to the cache, gated behind the configuration.verify_attestation_statement value.

Yeah, that's perfect. Keeping it as decoupled as that will give us the flexibility we need to take any decision in the future about this part of the gem's arch.

Maybe, but given this being tied to registration where the RP wishes to request and verify attestation I feel there's also a case to be made for it staying a part of the gem, even if it's minimally coupled. I'm not seeing the value of a standalone client just yet but maybe you can convince me otherwise 🙂

When I said symptoms on this being potentially extracted I was specifically thinking on the 2 first answers I quoted in this reply. Making the gem user plug backends and backgrounds jobs. I'm not in a point where I'd try to convince the rest on making it standalone client from scratch. I'm more than fine with keep building it inside the gem and come together to a conclusion with more cards on the table and having a shared decision in such move if that ever happens at all.

@bdewater
Copy link
Collaborator Author

bdewater commented Aug 8, 2019

This is ready for review! 🎉

I'll merge #234 soon and test with a Surface 4 to address the TODO in the TPM specs, but I think the overall direction is good.

@bdewater
Copy link
Collaborator Author

Decided to drop the TPM commit here - the Fido MDS is of no use at the moment here but we can use the TPM manufacturer root certificates to verify this instead. I'll open a separate PR for this.

Implements most of https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-metadata-service-v2.0-rd-20180702.html

Interestingly the MDS server currently does not correctly implement the
standard; TOC entry hashes for statements are padded base64url while they should
be unpadded. The conformance server however uses unpadded encoding correctly.
Copy link
Collaborator Author

@bdewater bdewater left a comment

Choose a reason for hiding this comment

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

Thanks for the review and kind words @brauliomartinezlm !

I'd like to ask what you would think about running the whole MDS topic as opt-in for a while for the gem's users.

Strictly speaking that already is the case, browsers don't ask for attestation by default 😉 #219 also provides a way to enforce that server side.

It's fair amount of heavy lifting that some gem users might not want or be ready to add, and for them, I'd like to give them the chance to keep doing the more basic stuff we were doing with the attestation statement and that didn't involve a cache backend, nor making requests to fido and authenticator manufacturers urls behind the curtains.

I see your point regarding easy of use, but I also feel asking for attestation is a 'heavy' feature in itself* (user must allow sharing "make and model of the security key" with RP) and the current implementation is security theatre for most formats. I'll think about this a bit more, but right now I'm leaning more towards a black/white solution where you either opt-out or -in completely into verifying attestation properly as described by the standard.

*: considering the user must consent to sharing "make and model" with the RP (unless pre-allowed by enterprise policies), and the use cases mentioned here are are not typical for consumers but rather for (large) companies, financial institutions and governments.

iii. Give us time to work on documentation and gem user adoption for all the theory and conceptual background behind this feature.

💯 I'll get started on a markdown file for here in the repo on and example implementation in https://github.com/cedarcode/webauthn-rails-demo-app


def get_with_token(uri)
if @token && [email protected]?
uri.path += "/" unless uri.path.end_with?("/")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Either the production or conformance MDS environment needed this. It was a bit wacky and resulted in HTTP 404s otherwise, I'll add a comment since I don't know exactly anymore either 😅

def get(uri)
get = Net::HTTP::Get.new(uri, DEFAULT_HEADERS)
response = http(uri).request(get)
response.value
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Raising an error, but seeing it again I agree it can be made a bit more explicit. Will change :)

module Coercer
class AssumedValue
def initialize(assume)
@assume = assume
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There's a few references to "assumed value" in that document :) e.g:

protocolFamily of type DOMString
The FIDO protocol family. The values "uaf", "u2f", and "fido2" are supported. If this field is missing, the assumed protocol family is "uaf".


it "has the expected attributes" do
expect(subject).to have_attributes(
authenticator_version: 2,
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 implemented the textual descriptions from section 5 quite literally and I didn't even notice the spec mentioned it, haha! Will add.

@grzuy grzuy requested review from grzuy and removed request for grzuy September 2, 2019 14:05
@grzuy
Copy link
Contributor

grzuy commented Sep 2, 2019

Hi @bdewater,

Thanks for all your hard work on this.

Heads up:

  • Planning to and want to review this myself :-)
  • Seeing it possibly included in a >= v2.1 release, not v2.0. I expect v2.0 to include new WebAuthn::PublicKeyCredential API and not a lot more. After some time with that released and stable we can continue pushing new features, like this one.

UPDATE: Just created a v2.0 milestone to provide more clarity.

@grzuy
Copy link
Contributor

grzuy commented Oct 31, 2019

I see your point regarding easy of use, but I also feel asking for attestation is a 'heavy' feature in itself* (user must allow sharing "make and model of the security key" with RP) and the current implementation is security theatre for most formats. I'll think about this a bit more, but right now I'm leaning more towards a black/white solution where you either opt-out or -in completely into verifying attestation properly as described by the standard.

For the record, there was an offline discussion a while ago with @bdewater and @brauliomartinezlm in which we decided we would only want to have an all-or-nothing config setting that turns ON/OFF attestation verification completely. No value in keeping the "partial" attestation verification we are providing as of v2.0.0 as an option.

We also agreed the best would be to have it turned OFF by default.

@grzuy
Copy link
Contributor

grzuy commented Oct 31, 2019

Hi @bdewater,

We decided to start merging all changes that are part of #66 into an integration branch (attestation_trustworthiness), instead of master.

Then, when feature complete, we merge into master.

@grzuy grzuy changed the base branch from master to attestation_trustworthiness October 31, 2019 17:19
Copy link
Contributor

@grzuy grzuy left a comment

Choose a reason for hiding this comment

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

Thank you @bdewater,
This is excellent work.

More than ready to be merged into attestation_trustworthiness epic branch and continue polishing all thing related to attestation there.

@bdewater
Copy link
Collaborator Author

Hey @grzuy! Working on a bigger feature branch sounds like a good idea. As mentioned on Gitter I ran into a few small issues that need fixing for successful integration in the Rails demo app. I'm aiming to push those fixes and the example PR up this weekend and we can continue from there :)

@grzuy grzuy merged commit 0f0947c into cedarcode:attestation_trustworthiness Oct 31, 2019
@@ -13,3 +13,5 @@
/Gemfile.lock
/gemfiles/*.gemfile.lock
.byebug_history

/spec/conformance/metadata.zip
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't having any effect because the file is also included in the diff here.

The intention was to ignore right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That must've gone wrong during a rebase, it was not meant to be included :( It's governed by the conformance tools terms of service and not open source.

@grzuy
Copy link
Contributor

grzuy commented Nov 8, 2019

Do you see this client living inside the gem and being extracted later? I keep seeing symptoms of this being an isolated component, with separate flows and contained complexity that lead me to think on this living outside the gem itself.

Maybe, but given this being tied to registration where the RP wishes to request and verify attestation I feel there's also a case to be made for it staying a part of the gem, even if it's minimally coupled. I'm not seeing the value of a standalone client just yet but maybe you can convince me otherwise slightly_smiling_face

Hi @bdewater, I've been thinking more and more about this and I think I am quite convinced that we want, at least, the metadata service client to be separate library/gem. I am not sure about the store yet, but the client for sure.

I think is also arguable that the WebAuthn::Metadata::Client should not be namespaced with WebAuthn. This is a client consuming the FIDO Metadata Service v2, which is more broad than WebAuthn (FIDO 2), it also contains metadata for FIDO UAF and FIDO U2F. Not only that, it was actually born as the FIDO UAF Authenticator Metadata Service v1.0, without support for FIDO2 at the time.

So I think this could be something like:

  • gem "fido_metadata_service" and FIDOMetadataService::Client; or
  • gem "fido_mds" and FIDOMDS::Client; or
  • gem "mds-client and MDS::Client or something similar

FWIW, there's a JS package similar to this: https://www.npmjs.com/package/mds-client.

@bdewater Thoughts?

@bdewater
Copy link
Collaborator Author

I agree with the namespacing but from a practical standpoint I didn't feel it was that big of a deal initially. I'd be happy to extract it and make a gem if you do, given our recent Gitter chat it seems to make sense to have the new gem support multiple publishers too (i.e. FIDO, SoloKeys).

The cache/store layer I do think is in the right place, for both practical reasons and this bit from the specification:

It is expected that most authenticators will support a small number of attestation types and attestation statement formats, while Relying Parties will decide what attestation types are acceptable to them by policy. Relying Parties will also need to understand the characteristics of the authenticators that they trust, based on information they have about these authenticators. For example, the FIDO Metadata Service provides one way to access such information.

@grzuy
Copy link
Contributor

grzuy commented Nov 11, 2019

I agree with the namespacing but from a practical standpoint I didn't feel it was that big of a deal initially. I'd be happy to extract it and make a gem if you do,

Thank you!
I am quite convinced we should have it separate.

given our recent Gitter chat it seems to make sense to have the new gem support multiple publishers too (i.e. FIDO, SoloKeys).

Nice.

So how would that work?
I was assumming the "FIDO Metadata Service" client would only know how to consume a web source that follows the https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-metadata-service-v2.0-id-20180227.html spec.

Is SoloKeys or any other manufacturer also providing a source implementing that spec?

The cache/store layer I do think is in the right place, for both practical reasons and this bit from the specification:

It is expected that most authenticators will support a small number of attestation types and attestation statement formats, while Relying Parties will decide what attestation types are acceptable to them by policy. Relying Parties will also need to understand the characteristics of the authenticators that they trust, based on information they have about these authenticators. For example, the FIDO Metadata Service provides one way to access such information.

Sounds good.

@bdewater
Copy link
Collaborator Author

So how would that work?
I was assumming the "FIDO Metadata Service" client would only know how to consume a web source that follows the https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-metadata-service-v2.0-id-20180227.html spec.
Is SoloKeys or any other manufacturer also providing a source implementing that spec?

Mostly but not entirely: https://github.com/solokeys/solo/tree/master/metadata - I'll think about this a little more, but in the mean time users could download these files by hand and add them to the cache if they wish to trust this vendor.

In the mean time, here's the gem extraction of this PR: https://github.com/bdewater/fido_metadata

@bdewater bdewater deleted the metadata-service branch November 13, 2019 16:44
@grzuy
Copy link
Contributor

grzuy commented Nov 13, 2019

Excellent.
Thank you very much!

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

Successfully merging this pull request may close these issues.

3 participants