webauthn 2.1.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +113 -13
- data/.travis.yml +21 -18
- data/Appraisals +4 -0
- data/CHANGELOG.md +41 -0
- data/CONTRIBUTING.md +0 -5
- data/README.md +70 -8
- data/SECURITY.md +6 -4
- data/gemfiles/openssl_2_2.gemfile +7 -0
- data/lib/cose/rsapkcs1_algorithm.rb +50 -0
- data/lib/webauthn/attestation_object.rb +43 -0
- data/lib/webauthn/attestation_statement.rb +20 -20
- data/lib/webauthn/attestation_statement/android_key.rb +28 -30
- data/lib/webauthn/attestation_statement/android_safetynet.rb +27 -7
- data/lib/webauthn/attestation_statement/base.rb +108 -10
- data/lib/webauthn/attestation_statement/fido_u2f.rb +8 -6
- data/lib/webauthn/attestation_statement/none.rb +7 -1
- data/lib/webauthn/attestation_statement/packed.rb +13 -41
- data/lib/webauthn/attestation_statement/tpm.rb +38 -75
- data/lib/webauthn/authenticator_assertion_response.rb +3 -7
- data/lib/webauthn/authenticator_attestation_response.rb +19 -84
- data/lib/webauthn/authenticator_data.rb +51 -51
- data/lib/webauthn/authenticator_data/attested_credential_data.rb +29 -50
- data/lib/webauthn/authenticator_response.rb +3 -0
- data/lib/webauthn/credential_creation_options.rb +2 -0
- data/lib/webauthn/credential_request_options.rb +2 -0
- data/lib/webauthn/fake_authenticator.rb +7 -3
- data/lib/webauthn/fake_authenticator/attestation_object.rb +7 -3
- data/lib/webauthn/fake_authenticator/authenticator_data.rb +2 -4
- data/lib/webauthn/fake_client.rb +19 -5
- data/lib/webauthn/public_key.rb +21 -2
- data/lib/webauthn/public_key_credential.rb +13 -3
- data/lib/webauthn/u2f_migrator.rb +5 -4
- data/lib/webauthn/version.rb +1 -1
- data/script/ci/install-openssl +7 -0
- data/script/ci/install-ruby +13 -0
- data/webauthn.gemspec +13 -9
- metadata +54 -41
- data/lib/android_safetynet/attestation_response.rb +0 -116
- data/lib/cose/rsassa_algorithm.rb +0 -10
- data/lib/tpm/constants.rb +0 -44
- data/lib/tpm/s_attest.rb +0 -26
- data/lib/tpm/s_attest/s_certify_info.rb +0 -14
- data/lib/tpm/sized_buffer.rb +0 -13
- data/lib/tpm/t_public.rb +0 -32
- data/lib/tpm/t_public/s_ecc_parms.rb +0 -17
- data/lib/tpm/t_public/s_rsa_parms.rb +0 -17
- data/lib/webauthn/attestation_statement/android_key/authorization_list.rb +0 -39
- data/lib/webauthn/attestation_statement/android_key/key_description.rb +0 -37
- data/lib/webauthn/attestation_statement/tpm/cert_info.rb +0 -44
- data/lib/webauthn/attestation_statement/tpm/pub_area.rb +0 -85
- data/lib/webauthn/signature_verifier.rb +0 -77
| @@ -1,32 +1,49 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "bindata"
         | 
| 3 4 | 
             
            require "webauthn/authenticator_data/attested_credential_data"
         | 
| 5 | 
            +
            require "webauthn/error"
         | 
| 4 6 |  | 
| 5 7 | 
             
            module WebAuthn
         | 
| 6 | 
            -
              class  | 
| 7 | 
            -
                RP_ID_HASH_POSITION = 0
         | 
| 8 | 
            +
              class AuthenticatorDataFormatError < WebAuthn::Error; end
         | 
| 8 9 |  | 
| 10 | 
            +
              class AuthenticatorData < BinData::Record
         | 
| 9 11 | 
             
                RP_ID_HASH_LENGTH = 32
         | 
| 10 12 | 
             
                FLAGS_LENGTH = 1
         | 
| 11 13 | 
             
                SIGN_COUNT_LENGTH = 4
         | 
| 12 14 |  | 
| 13 | 
            -
                 | 
| 15 | 
            +
                endian :big
         | 
| 14 16 |  | 
| 15 | 
            -
                 | 
| 16 | 
            -
                 | 
| 17 | 
            -
                 | 
| 18 | 
            -
             | 
| 17 | 
            +
                count_bytes_remaining :data_length
         | 
| 18 | 
            +
                string :rp_id_hash, length: RP_ID_HASH_LENGTH
         | 
| 19 | 
            +
                struct :flags do
         | 
| 20 | 
            +
                  bit1 :extension_data_included
         | 
| 21 | 
            +
                  bit1 :attested_credential_data_included
         | 
| 22 | 
            +
                  bit1 :reserved_for_future_use_4
         | 
| 23 | 
            +
                  bit1 :reserved_for_future_use_3
         | 
| 24 | 
            +
                  bit1 :reserved_for_future_use_2
         | 
| 25 | 
            +
                  bit1 :user_verified
         | 
| 26 | 
            +
                  bit1 :reserved_for_future_use_1
         | 
| 27 | 
            +
                  bit1 :user_present
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
                bit32 :sign_count
         | 
| 30 | 
            +
                count_bytes_remaining :trailing_bytes_length
         | 
| 31 | 
            +
                string :trailing_bytes, length: :trailing_bytes_length
         | 
| 19 32 |  | 
| 20 | 
            -
                def  | 
| 21 | 
            -
                   | 
| 33 | 
            +
                def self.deserialize(data)
         | 
| 34 | 
            +
                  read(data)
         | 
| 35 | 
            +
                rescue EOFError
         | 
| 36 | 
            +
                  raise AuthenticatorDataFormatError
         | 
| 22 37 | 
             
                end
         | 
| 23 38 |  | 
| 24 | 
            -
                 | 
| 39 | 
            +
                def data
         | 
| 40 | 
            +
                  to_binary_s
         | 
| 41 | 
            +
                end
         | 
| 25 42 |  | 
| 26 43 | 
             
                def valid?
         | 
| 27 | 
            -
                   | 
| 28 | 
            -
                    (! | 
| 29 | 
            -
                     | 
| 44 | 
            +
                  (!attested_credential_data_included? || attested_credential_data.valid?) &&
         | 
| 45 | 
            +
                    (!extension_data_included? || extension_data) &&
         | 
| 46 | 
            +
                    valid_length?
         | 
| 30 47 | 
             
                end
         | 
| 31 48 |  | 
| 32 49 | 
             
                def user_flagged?
         | 
| @@ -34,26 +51,19 @@ module WebAuthn | |
| 34 51 | 
             
                end
         | 
| 35 52 |  | 
| 36 53 | 
             
                def user_present?
         | 
| 37 | 
            -
                  flags | 
| 54 | 
            +
                  flags.user_present == 1
         | 
| 38 55 | 
             
                end
         | 
| 39 56 |  | 
| 40 57 | 
             
                def user_verified?
         | 
| 41 | 
            -
                  flags | 
| 58 | 
            +
                  flags.user_verified == 1
         | 
| 42 59 | 
             
                end
         | 
| 43 60 |  | 
| 44 61 | 
             
                def attested_credential_data_included?
         | 
| 45 | 
            -
                  flags | 
| 62 | 
            +
                  flags.attested_credential_data_included == 1
         | 
| 46 63 | 
             
                end
         | 
| 47 64 |  | 
| 48 65 | 
             
                def extension_data_included?
         | 
| 49 | 
            -
                  flags | 
| 50 | 
            -
                end
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                def rp_id_hash
         | 
| 53 | 
            -
                  @rp_id_hash ||=
         | 
| 54 | 
            -
                    if valid?
         | 
| 55 | 
            -
                      data_at(RP_ID_HASH_POSITION, RP_ID_HASH_LENGTH)
         | 
| 56 | 
            -
                    end
         | 
| 66 | 
            +
                  flags.extension_data_included == 1
         | 
| 57 67 | 
             
                end
         | 
| 58 68 |  | 
| 59 69 | 
             
                def credential
         | 
| @@ -62,35 +72,39 @@ module WebAuthn | |
| 62 72 | 
             
                  end
         | 
| 63 73 | 
             
                end
         | 
| 64 74 |  | 
| 65 | 
            -
                def sign_count
         | 
| 66 | 
            -
                  @sign_count ||= data_at(SIGN_COUNT_POSITION, SIGN_COUNT_LENGTH).unpack('L>')[0]
         | 
| 67 | 
            -
                end
         | 
| 68 | 
            -
             | 
| 69 75 | 
             
                def attested_credential_data
         | 
| 70 76 | 
             
                  @attested_credential_data ||=
         | 
| 71 | 
            -
                    AttestedCredentialData. | 
| 77 | 
            +
                    AttestedCredentialData.deserialize(trailing_bytes)
         | 
| 78 | 
            +
                rescue AttestedCredentialDataFormatError
         | 
| 79 | 
            +
                  raise AuthenticatorDataFormatError
         | 
| 72 80 | 
             
                end
         | 
| 73 81 |  | 
| 74 82 | 
             
                def extension_data
         | 
| 75 83 | 
             
                  @extension_data ||= CBOR.decode(raw_extension_data)
         | 
| 76 84 | 
             
                end
         | 
| 77 85 |  | 
| 78 | 
            -
                def  | 
| 79 | 
            -
                   | 
| 86 | 
            +
                def aaguid
         | 
| 87 | 
            +
                  raw_aaguid = attested_credential_data.raw_aaguid
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  unless raw_aaguid == WebAuthn::AuthenticatorData::AttestedCredentialData::ZEROED_AAGUID
         | 
| 90 | 
            +
                    attested_credential_data.aaguid
         | 
| 91 | 
            +
                  end
         | 
| 80 92 | 
             
                end
         | 
| 81 93 |  | 
| 82 94 | 
             
                private
         | 
| 83 95 |  | 
| 84 96 | 
             
                def valid_length?
         | 
| 85 | 
            -
                   | 
| 97 | 
            +
                  data_length == base_length + attested_credential_data_length + extension_data_length
         | 
| 86 98 | 
             
                end
         | 
| 87 99 |  | 
| 88 100 | 
             
                def raw_extension_data
         | 
| 89 | 
            -
                   | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 101 | 
            +
                  if extension_data_included?
         | 
| 102 | 
            +
                    if attested_credential_data_included?
         | 
| 103 | 
            +
                      trailing_bytes[attested_credential_data.length..-1]
         | 
| 104 | 
            +
                    else
         | 
| 105 | 
            +
                      trailing_bytes.snapshot
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
                  end
         | 
| 94 108 | 
             
                end
         | 
| 95 109 |  | 
| 96 110 | 
             
                def attested_credential_data_length
         | 
| @@ -109,22 +123,8 @@ module WebAuthn | |
| 109 123 | 
             
                  end
         | 
| 110 124 | 
             
                end
         | 
| 111 125 |  | 
| 112 | 
            -
                def extension_data_position
         | 
| 113 | 
            -
                  base_length + attested_credential_data_length
         | 
| 114 | 
            -
                end
         | 
| 115 | 
            -
             | 
| 116 126 | 
             
                def base_length
         | 
| 117 127 | 
             
                  RP_ID_HASH_LENGTH + FLAGS_LENGTH + SIGN_COUNT_LENGTH
         | 
| 118 128 | 
             
                end
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                def flags_position
         | 
| 121 | 
            -
                  RP_ID_HASH_LENGTH
         | 
| 122 | 
            -
                end
         | 
| 123 | 
            -
             | 
| 124 | 
            -
                def data_at(position, length = nil)
         | 
| 125 | 
            -
                  length ||= data.size - position
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                  data[position..(position + length - 1)]
         | 
| 128 | 
            -
                end
         | 
| 129 129 | 
             
              end
         | 
| 130 130 | 
             
            end
         | 
| @@ -1,34 +1,43 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "bindata"
         | 
| 3 4 | 
             
            require "cose/key"
         | 
| 5 | 
            +
            require "webauthn/error"
         | 
| 4 6 |  | 
| 5 7 | 
             
            module WebAuthn
         | 
| 6 | 
            -
              class  | 
| 7 | 
            -
             | 
| 8 | 
            +
              class AttestedCredentialDataFormatError < WebAuthn::Error; end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              class AuthenticatorData < BinData::Record
         | 
| 11 | 
            +
                class AttestedCredentialData < BinData::Record
         | 
| 8 12 | 
             
                  AAGUID_LENGTH = 16
         | 
| 9 13 | 
             
                  ZEROED_AAGUID = 0.chr * AAGUID_LENGTH
         | 
| 10 14 |  | 
| 11 15 | 
             
                  ID_LENGTH_LENGTH = 2
         | 
| 12 16 |  | 
| 13 | 
            -
                   | 
| 17 | 
            +
                  endian :big
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  string :raw_aaguid, length: AAGUID_LENGTH
         | 
| 20 | 
            +
                  bit16 :id_length
         | 
| 21 | 
            +
                  string :id, read_length: :id_length
         | 
| 22 | 
            +
                  count_bytes_remaining :trailing_bytes_length
         | 
| 23 | 
            +
                  string :trailing_bytes, length: :trailing_bytes_length
         | 
| 14 24 |  | 
| 15 | 
            -
                  #  | 
| 16 | 
            -
                  Credential = | 
| 17 | 
            -
                     | 
| 18 | 
            -
                       | 
| 25 | 
            +
                  # TODO: use keyword_init when we dropped Ruby 2.4 support
         | 
| 26 | 
            +
                  Credential =
         | 
| 27 | 
            +
                    Struct.new(:id, :public_key) do
         | 
| 28 | 
            +
                      def public_key_object
         | 
| 29 | 
            +
                        COSE::Key.deserialize(public_key).to_pkey
         | 
| 30 | 
            +
                      end
         | 
| 19 31 | 
             
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 32 |  | 
| 22 | 
            -
                  def  | 
| 23 | 
            -
                     | 
| 33 | 
            +
                  def self.deserialize(data)
         | 
| 34 | 
            +
                    read(data)
         | 
| 35 | 
            +
                  rescue EOFError
         | 
| 36 | 
            +
                    raise AttestedCredentialDataFormatError
         | 
| 24 37 | 
             
                  end
         | 
| 25 38 |  | 
| 26 39 | 
             
                  def valid?
         | 
| 27 | 
            -
                     | 
| 28 | 
            -
                  end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                  def raw_aaguid
         | 
| 31 | 
            -
                    data_at(0, AAGUID_LENGTH)
         | 
| 40 | 
            +
                    valid_credential_public_key?
         | 
| 32 41 | 
             
                  end
         | 
| 33 42 |  | 
| 34 43 | 
             
                  def aaguid
         | 
| @@ -37,62 +46,32 @@ module WebAuthn | |
| 37 46 |  | 
| 38 47 | 
             
                  def credential
         | 
| 39 48 | 
             
                    @credential ||=
         | 
| 40 | 
            -
                      if  | 
| 49 | 
            +
                      if valid?
         | 
| 41 50 | 
             
                        Credential.new(id, public_key)
         | 
| 42 51 | 
             
                      end
         | 
| 43 52 | 
             
                  end
         | 
| 44 53 |  | 
| 45 54 | 
             
                  def length
         | 
| 46 55 | 
             
                    if valid?
         | 
| 47 | 
            -
                       | 
| 56 | 
            +
                      AAGUID_LENGTH + ID_LENGTH_LENGTH + id_length + public_key_length
         | 
| 48 57 | 
             
                    end
         | 
| 49 58 | 
             
                  end
         | 
| 50 59 |  | 
| 51 60 | 
             
                  private
         | 
| 52 61 |  | 
| 53 | 
            -
                  attr_reader :data
         | 
| 54 | 
            -
             | 
| 55 62 | 
             
                  def valid_credential_public_key?
         | 
| 56 63 | 
             
                    cose_key = COSE::Key.deserialize(public_key)
         | 
| 57 64 |  | 
| 58 | 
            -
                    !!cose_key.alg
         | 
| 59 | 
            -
                  end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                  def id
         | 
| 62 | 
            -
                    if valid?
         | 
| 63 | 
            -
                      data_at(id_position, id_length)
         | 
| 64 | 
            -
                    end
         | 
| 65 | 
            +
                    !!cose_key.alg && WebAuthn.configuration.algorithms.include?(COSE::Algorithm.find(cose_key.alg).name)
         | 
| 65 66 | 
             
                  end
         | 
| 66 67 |  | 
| 67 68 | 
             
                  def public_key
         | 
| 68 | 
            -
                     | 
| 69 | 
            -
                  end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                  def id_position
         | 
| 72 | 
            -
                    id_length_position + ID_LENGTH_LENGTH
         | 
| 73 | 
            -
                  end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                  def id_length
         | 
| 76 | 
            -
                    @id_length ||= data_at(id_length_position, ID_LENGTH_LENGTH).unpack(UINT16_BIG_ENDIAN_FORMAT)[0]
         | 
| 77 | 
            -
                  end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                  def id_length_position
         | 
| 80 | 
            -
                    AAGUID_LENGTH
         | 
| 81 | 
            -
                  end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                  def public_key_position
         | 
| 84 | 
            -
                    id_position + id_length
         | 
| 69 | 
            +
                    trailing_bytes[0..public_key_length - 1]
         | 
| 85 70 | 
             
                  end
         | 
| 86 71 |  | 
| 87 72 | 
             
                  def public_key_length
         | 
| 88 73 | 
             
                    @public_key_length ||=
         | 
| 89 | 
            -
                      CBOR.encode(CBOR::Unpacker.new(StringIO.new( | 
| 90 | 
            -
                  end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                  def data_at(position, length = nil)
         | 
| 93 | 
            -
                    length ||= data.size - position
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                    data[position..(position + length - 1)]
         | 
| 74 | 
            +
                      CBOR.encode(CBOR::Unpacker.new(StringIO.new(trailing_bytes)).each.first).length
         | 
| 96 75 | 
             
                  end
         | 
| 97 76 | 
             
                end
         | 
| 98 77 | 
             
              end
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "webauthn/authenticator_data"
         | 
| 3 4 | 
             
            require "webauthn/client_data"
         | 
| 4 5 | 
             
            require "webauthn/error"
         | 
| 5 6 | 
             
            require "webauthn/security_utils"
         | 
| @@ -91,6 +92,8 @@ module WebAuthn | |
| 91 92 |  | 
| 92 93 | 
             
                def valid_authenticator_data?
         | 
| 93 94 | 
             
                  authenticator_data.valid?
         | 
| 95 | 
            +
                rescue WebAuthn::AuthenticatorDataFormatError
         | 
| 96 | 
            +
                  false
         | 
| 94 97 | 
             
                end
         | 
| 95 98 |  | 
| 96 99 | 
             
                def valid_user_presence?
         | 
| @@ -16,6 +16,8 @@ module WebAuthn | |
| 16 16 | 
             
                attr_accessor :allow_credentials, :extensions, :user_verification
         | 
| 17 17 |  | 
| 18 18 | 
             
                def initialize(allow_credentials: [], extensions: nil, user_verification: nil)
         | 
| 19 | 
            +
                  super()
         | 
| 20 | 
            +
             | 
| 19 21 | 
             
                  @allow_credentials = allow_credentials
         | 
| 20 22 | 
             
                  @extensions = extensions
         | 
| 21 23 | 
             
                  @user_verification = user_verification
         | 
| @@ -18,7 +18,8 @@ module WebAuthn | |
| 18 18 | 
             
                  user_present: true,
         | 
| 19 19 | 
             
                  user_verified: false,
         | 
| 20 20 | 
             
                  attested_credential_data: true,
         | 
| 21 | 
            -
                  sign_count: nil
         | 
| 21 | 
            +
                  sign_count: nil,
         | 
| 22 | 
            +
                  extensions: nil
         | 
| 22 23 | 
             
                )
         | 
| 23 24 | 
             
                  credential_id, credential_key, credential_sign_count = new_credential
         | 
| 24 25 | 
             
                  sign_count ||= credential_sign_count
         | 
| @@ -37,7 +38,8 @@ module WebAuthn | |
| 37 38 | 
             
                    user_present: user_present,
         | 
| 38 39 | 
             
                    user_verified: user_verified,
         | 
| 39 40 | 
             
                    attested_credential_data: attested_credential_data,
         | 
| 40 | 
            -
                    sign_count: sign_count
         | 
| 41 | 
            +
                    sign_count: sign_count,
         | 
| 42 | 
            +
                    extensions: extensions
         | 
| 41 43 | 
             
                  ).serialize
         | 
| 42 44 | 
             
                end
         | 
| 43 45 |  | 
| @@ -47,7 +49,8 @@ module WebAuthn | |
| 47 49 | 
             
                  user_present: true,
         | 
| 48 50 | 
             
                  user_verified: false,
         | 
| 49 51 | 
             
                  aaguid: AuthenticatorData::AAGUID,
         | 
| 50 | 
            -
                  sign_count: nil
         | 
| 52 | 
            +
                  sign_count: nil,
         | 
| 53 | 
            +
                  extensions: nil
         | 
| 51 54 | 
             
                )
         | 
| 52 55 | 
             
                  credential_options = credentials[rp_id]
         | 
| 53 56 |  | 
| @@ -63,6 +66,7 @@ module WebAuthn | |
| 63 66 | 
             
                      aaguid: aaguid,
         | 
| 64 67 | 
             
                      credential: nil,
         | 
| 65 68 | 
             
                      sign_count: sign_count || credential_sign_count,
         | 
| 69 | 
            +
                      extensions: extensions
         | 
| 66 70 | 
             
                    ).serialize
         | 
| 67 71 |  | 
| 68 72 | 
             
                    signature = credential_key.sign("SHA256", authenticator_data + client_data_hash)
         | 
| @@ -14,7 +14,8 @@ module WebAuthn | |
| 14 14 | 
             
                    user_present: true,
         | 
| 15 15 | 
             
                    user_verified: false,
         | 
| 16 16 | 
             
                    attested_credential_data: true,
         | 
| 17 | 
            -
                    sign_count: 0
         | 
| 17 | 
            +
                    sign_count: 0,
         | 
| 18 | 
            +
                    extensions: nil
         | 
| 18 19 | 
             
                  )
         | 
| 19 20 | 
             
                    @client_data_hash = client_data_hash
         | 
| 20 21 | 
             
                    @rp_id_hash = rp_id_hash
         | 
| @@ -24,6 +25,7 @@ module WebAuthn | |
| 24 25 | 
             
                    @user_verified = user_verified
         | 
| 25 26 | 
             
                    @attested_credential_data = attested_credential_data
         | 
| 26 27 | 
             
                    @sign_count = sign_count
         | 
| 28 | 
            +
                    @extensions = extensions
         | 
| 27 29 | 
             
                  end
         | 
| 28 30 |  | 
| 29 31 | 
             
                  def serialize
         | 
| @@ -44,7 +46,8 @@ module WebAuthn | |
| 44 46 | 
             
                    :user_present,
         | 
| 45 47 | 
             
                    :user_verified,
         | 
| 46 48 | 
             
                    :attested_credential_data,
         | 
| 47 | 
            -
                    :sign_count
         | 
| 49 | 
            +
                    :sign_count,
         | 
| 50 | 
            +
                    :extensions
         | 
| 48 51 | 
             
                  )
         | 
| 49 52 |  | 
| 50 53 | 
             
                  def authenticator_data
         | 
| @@ -60,7 +63,8 @@ module WebAuthn | |
| 60 63 | 
             
                          credential: credential_data,
         | 
| 61 64 | 
             
                          user_present: user_present,
         | 
| 62 65 | 
             
                          user_verified: user_verified,
         | 
| 63 | 
            -
                          sign_count: 0
         | 
| 66 | 
            +
                          sign_count: 0,
         | 
| 67 | 
            +
                          extensions: extensions
         | 
| 64 68 | 
             
                        )
         | 
| 65 69 | 
             
                      end
         | 
| 66 70 | 
             
                  end
         | 
| @@ -115,8 +115,7 @@ module WebAuthn | |
| 115 115 | 
             
                    case credential[:public_key]
         | 
| 116 116 | 
             
                    when OpenSSL::PKey::RSA
         | 
| 117 117 | 
             
                      key = COSE::Key::RSA.from_pkey(credential[:public_key])
         | 
| 118 | 
            -
                       | 
| 119 | 
            -
                      key.instance_variable_set(:@alg, -257)
         | 
| 118 | 
            +
                      key.alg = -257
         | 
| 120 119 | 
             
                    when OpenSSL::PKey::EC::Point
         | 
| 121 120 | 
             
                      alg = {
         | 
| 122 121 | 
             
                        COSE::Key::Curve.by_name("P-256").id => -7,
         | 
| @@ -125,8 +124,7 @@ module WebAuthn | |
| 125 124 | 
             
                      }
         | 
| 126 125 |  | 
| 127 126 | 
             
                      key = COSE::Key::EC2.from_pkey(credential[:public_key])
         | 
| 128 | 
            -
                       | 
| 129 | 
            -
                      key.instance_variable_set(:@alg, alg[key.crv])
         | 
| 127 | 
            +
                      key.alg = alg[key.crv]
         | 
| 130 128 |  | 
| 131 129 | 
             
                    end
         | 
| 132 130 |  | 
    
        data/lib/webauthn/fake_client.rb
    CHANGED
    
    | @@ -29,7 +29,8 @@ module WebAuthn | |
| 29 29 | 
             
                  rp_id: nil,
         | 
| 30 30 | 
             
                  user_present: true,
         | 
| 31 31 | 
             
                  user_verified: false,
         | 
| 32 | 
            -
                  attested_credential_data: true
         | 
| 32 | 
            +
                  attested_credential_data: true,
         | 
| 33 | 
            +
                  extensions: nil
         | 
| 33 34 | 
             
                )
         | 
| 34 35 | 
             
                  rp_id ||= URI.parse(origin).host
         | 
| 35 36 |  | 
| @@ -41,12 +42,16 @@ module WebAuthn | |
| 41 42 | 
             
                    client_data_hash: client_data_hash,
         | 
| 42 43 | 
             
                    user_present: user_present,
         | 
| 43 44 | 
             
                    user_verified: user_verified,
         | 
| 44 | 
            -
                    attested_credential_data: attested_credential_data
         | 
| 45 | 
            +
                    attested_credential_data: attested_credential_data,
         | 
| 46 | 
            +
                    extensions: extensions
         | 
| 45 47 | 
             
                  )
         | 
| 46 48 |  | 
| 47 49 | 
             
                  id =
         | 
| 48 50 | 
             
                    if attested_credential_data
         | 
| 49 | 
            -
                      WebAuthn::AuthenticatorData | 
| 51 | 
            +
                      WebAuthn::AuthenticatorData
         | 
| 52 | 
            +
                        .deserialize(CBOR.decode(attestation_object)["authData"])
         | 
| 53 | 
            +
                        .attested_credential_data
         | 
| 54 | 
            +
                        .id
         | 
| 50 55 | 
             
                    else
         | 
| 51 56 | 
             
                      "id-for-pk-without-attested-credential-data"
         | 
| 52 57 | 
             
                    end
         | 
| @@ -55,6 +60,7 @@ module WebAuthn | |
| 55 60 | 
             
                    "type" => "public-key",
         | 
| 56 61 | 
             
                    "id" => internal_encoder.encode(id),
         | 
| 57 62 | 
             
                    "rawId" => encoder.encode(id),
         | 
| 63 | 
            +
                    "clientExtensionResults" => extensions,
         | 
| 58 64 | 
             
                    "response" => {
         | 
| 59 65 | 
             
                      "attestationObject" => encoder.encode(attestation_object),
         | 
| 60 66 | 
             
                      "clientDataJSON" => encoder.encode(client_data_json)
         | 
| @@ -62,7 +68,13 @@ module WebAuthn | |
| 62 68 | 
             
                  }
         | 
| 63 69 | 
             
                end
         | 
| 64 70 |  | 
| 65 | 
            -
                def get(challenge: fake_challenge, | 
| 71 | 
            +
                def get(challenge: fake_challenge,
         | 
| 72 | 
            +
                        rp_id: nil,
         | 
| 73 | 
            +
                        user_present: true,
         | 
| 74 | 
            +
                        user_verified: false,
         | 
| 75 | 
            +
                        sign_count: nil,
         | 
| 76 | 
            +
                        extensions: nil,
         | 
| 77 | 
            +
                        user_handle: nil)
         | 
| 66 78 | 
             
                  rp_id ||= URI.parse(origin).host
         | 
| 67 79 |  | 
| 68 80 | 
             
                  client_data_json = data_json_for(:get, encoder.decode(challenge))
         | 
| @@ -74,17 +86,19 @@ module WebAuthn | |
| 74 86 | 
             
                    user_present: user_present,
         | 
| 75 87 | 
             
                    user_verified: user_verified,
         | 
| 76 88 | 
             
                    sign_count: sign_count,
         | 
| 89 | 
            +
                    extensions: extensions
         | 
| 77 90 | 
             
                  )
         | 
| 78 91 |  | 
| 79 92 | 
             
                  {
         | 
| 80 93 | 
             
                    "type" => "public-key",
         | 
| 81 94 | 
             
                    "id" => internal_encoder.encode(assertion[:credential_id]),
         | 
| 82 95 | 
             
                    "rawId" => encoder.encode(assertion[:credential_id]),
         | 
| 96 | 
            +
                    "clientExtensionResults" => extensions,
         | 
| 83 97 | 
             
                    "response" => {
         | 
| 84 98 | 
             
                      "clientDataJSON" => encoder.encode(client_data_json),
         | 
| 85 99 | 
             
                      "authenticatorData" => encoder.encode(assertion[:authenticator_data]),
         | 
| 86 100 | 
             
                      "signature" => encoder.encode(assertion[:signature]),
         | 
| 87 | 
            -
                      "userHandle" => nil
         | 
| 101 | 
            +
                      "userHandle" => user_handle ? encoder.encode(user_handle) : nil
         | 
| 88 102 | 
             
                    }
         | 
| 89 103 | 
             
                  }
         | 
| 90 104 | 
             
                end
         |