cedarcode / webauthn-ruby

WebAuthn ruby server library ― Make your Ruby/Rails web server become a conformant WebAuthn Relying Party
https://rubygems.org/gems/webauthn
MIT License
644 stars 51 forks source link

Verification Issue with attestation: 'direct' and YubiKey 5C NFC #405

Closed maricavor closed 9 months ago

maricavor commented 10 months ago

I am currently working on implementing WebAuthn using the webauthn-ruby gem in Ruby and @github/webauthn-json package, specifically in conjunction with the YubiKey 5C NFC. During testing, I encountered an issue related to the attestation statement verification which seems unique to our implementation, as the official WebAuthn testing portals do not reproduce the same error and everything works ok. When setting up the Webauthn::Credential.options_for_create, I am specifying the attestation to be 'direct' as follows:

options = WebAuthn::Credential.options_for_create(
  user: { 
    id: user.webauthn_id, 
    name: user.username, 
    display_name: user.display_name,
  }, 
  attestation: 'direct',
  authenticator_selection: {
      authenticator_attachment: 'cross-platform',
      user_verification: 'preferred',
      require_resident_key: true,
      resident_key: 'preferred',
   }
)

Upon testing with the YubiKey 5C NFC, the verification fails with the following error message: Verification failed: WebAuthn::AttestationStatementVerificationError. However, when utilizing the same YubiKey 5C NFC on official WebAuthn testing portals like https://webauthn.me/ or https://webauthn.io/ with a 'direct' attestation, the process is successfully verified without issue.

santiagorodriguez96 commented 10 months ago

Hi @maricavor 👋 !

Thank you for the report!

I'll try to reproduce this tomorrow with my YubiKey 5C so that I can understand what's the issue. I'll let you know what I end up finding!

maricavor commented 10 months ago

Hi @santiagorodriguez96 ! Thanks for your help! After some time in troubleshooting the verification process I found out that it fails on validation of attestation statement:

 module AttestationStatement
    class Packed < Base
      # Follows "Verification procedure"
      def valid?(authenticator_data, client_data_hash)
        valid_format? &&
          valid_algorithm?(authenticator_data.credential) &&
          valid_ec_public_keys?(authenticator_data.credential) &&
          meet_certificate_requirement? &&
          matching_aaguid?(authenticator_data.attested_credential_data.raw_aaguid) &&
          valid_signature?(authenticator_data, client_data_hash) &&
          trustworthy?(aaguid: authenticator_data.aaguid) &&
          [attestation_type, attestation_trust_path]
      end
...

where trustworthy? check return false. The reason of this is the following verification:

def valid_certificate_chain?(aaguid: nil, attestation_certificate_key_id: nil)
        attestation_root_certificates_store(
          aaguid: aaguid,
          attestation_certificate_key_id: attestation_certificate_key_id
        ).verify(attestation_certificate, attestation_trust_path)
      end

I managed to retrive the error information and it returns error 20: unable to get local issuer certificate Does it mean that I have to add attestation_root_certificates_findersfor every authenticator I want to support and get authenticator data?

santiagorodriguez96 commented 10 months ago

Good catch @maricavor!

Does it mean that I have to add attestation_root_certificates_findersfor every authenticator I want to support and get authenticator data?

Yes, as stated in the README:

You can define what trust policy to enforce by setting acceptable_attestation_types config to a subset of ['None', 'Self', 'Basic', 'AttCA', 'Basic_or_AttCA'] and attestation_root_certificates_finders to an object that responds to #find and returns the corresponding root certificate for each registration. The #find method will be called passing keyword arguments attestation_format, aaguid and attestation_certificate_key_id.

The gem doesn't provide a way of obtaining the list of acceptable trust anchors right now. However, as an example that might be useful, we implemented a finder that – along with the fido_metadata gem – knows how to obtain the list of attestation root certificates as defined in the FIDO Metadata Service that we use for running the conformance specs – here you can see how we configure the relying party to use it. By following this protocol, you will be able to fetch the root certificates related to the FIDO authenticators. Alternatively, you could go ahead and collect all the root certificates yourself from manufacturers you want trust.

maricavor commented 10 months ago

Thanks a lot, @santiagorodriguez96 ! That makes sense. Actually I already modified fido_metadata gem for myself to support FIDO metadata Service v3 and download all the metadata and save it to database, which I use for validating the authenticators by their certificates statuses. Now as you suggested I just have to collect all the root certificates form metadata as well and implement a finder.

santiagorodriguez96 commented 10 months ago

That's great! 🙌

Now as you suggested I just have to collect all the root certificates form metadata as well and implement a finder.

If I'm not mistaken, you should be good with just the MDS Root Certificate.

https://fidoalliance.org/metadata/

santiagorodriguez96 commented 9 months ago

Closing this as it seems that the issue was addressed. Feel free to open if you have any other doubt or question!