File: authenticator_data.rb

package info (click to toggle)
ruby-webauthn 2.5.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 384 kB
  • sloc: ruby: 2,138; sh: 4; makefile: 4
file content (130 lines) | stat: -rw-r--r-- 3,051 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# frozen_string_literal: true

require "bindata"
require "webauthn/authenticator_data/attested_credential_data"
require "webauthn/error"

module WebAuthn
  class AuthenticatorDataFormatError < WebAuthn::Error; end

  class AuthenticatorData < BinData::Record
    RP_ID_HASH_LENGTH = 32
    FLAGS_LENGTH = 1
    SIGN_COUNT_LENGTH = 4

    endian :big

    count_bytes_remaining :data_length
    string :rp_id_hash, length: RP_ID_HASH_LENGTH
    struct :flags do
      bit1 :extension_data_included
      bit1 :attested_credential_data_included
      bit1 :reserved_for_future_use_4
      bit1 :reserved_for_future_use_3
      bit1 :reserved_for_future_use_2
      bit1 :user_verified
      bit1 :reserved_for_future_use_1
      bit1 :user_present
    end
    bit32 :sign_count
    count_bytes_remaining :trailing_bytes_length
    string :trailing_bytes, length: :trailing_bytes_length

    def self.deserialize(data)
      read(data)
    rescue EOFError
      raise AuthenticatorDataFormatError
    end

    def data
      to_binary_s
    end

    def valid?
      (!attested_credential_data_included? || attested_credential_data.valid?) &&
        (!extension_data_included? || extension_data) &&
        valid_length?
    end

    def user_flagged?
      user_present? || user_verified?
    end

    def user_present?
      flags.user_present == 1
    end

    def user_verified?
      flags.user_verified == 1
    end

    def attested_credential_data_included?
      flags.attested_credential_data_included == 1
    end

    def extension_data_included?
      flags.extension_data_included == 1
    end

    def credential
      if attested_credential_data_included?
        attested_credential_data.credential
      end
    end

    def attested_credential_data
      @attested_credential_data ||=
        AttestedCredentialData.deserialize(trailing_bytes)
    rescue AttestedCredentialDataFormatError
      raise AuthenticatorDataFormatError
    end

    def extension_data
      @extension_data ||= CBOR.decode(raw_extension_data)
    end

    def aaguid
      raw_aaguid = attested_credential_data.raw_aaguid

      unless raw_aaguid == WebAuthn::AuthenticatorData::AttestedCredentialData::ZEROED_AAGUID
        attested_credential_data.aaguid
      end
    end

    private

    def valid_length?
      data_length == base_length + attested_credential_data_length + extension_data_length
    end

    def raw_extension_data
      if extension_data_included?
        if attested_credential_data_included?
          trailing_bytes[attested_credential_data.length..-1]
        else
          trailing_bytes.snapshot
        end
      end
    end

    def attested_credential_data_length
      if attested_credential_data_included?
        attested_credential_data.length
      else
        0
      end
    end

    def extension_data_length
      if extension_data_included?
        raw_extension_data.length
      else
        0
      end
    end

    def base_length
      RP_ID_HASH_LENGTH + FLAGS_LENGTH + SIGN_COUNT_LENGTH
    end
  end
end