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 (139 lines) | stat: -rw-r--r-- 3,232 bytes parent folder | download
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
131
132
133
134
135
136
137
138
139
# frozen_string_literal: true

require "cose/key"
require "cbor"
require "securerandom"

module WebAuthn
  class FakeAuthenticator
    class AuthenticatorData
      AAGUID = SecureRandom.random_bytes(16)

      attr_reader :sign_count

      def initialize(
        rp_id_hash:,
        credential: {
          id: SecureRandom.random_bytes(16),
          public_key: OpenSSL::PKey::EC.generate("prime256v1").public_key
        },
        sign_count: 0,
        user_present: true,
        user_verified: !user_present,
        aaguid: AAGUID,
        extensions: { "fakeExtension" => "fakeExtensionValue" }
      )
        @rp_id_hash = rp_id_hash
        @credential = credential
        @sign_count = sign_count
        @user_present = user_present
        @user_verified = user_verified
        @aaguid = aaguid
        @extensions = extensions
      end

      def serialize
        rp_id_hash + flags + serialized_sign_count + attested_credential_data + extension_data
      end

      private

      attr_reader :rp_id_hash, :credential, :user_present, :user_verified, :extensions

      def flags
        [
          [
            bit(:user_present),
            reserved_for_future_use_bit,
            bit(:user_verified),
            reserved_for_future_use_bit,
            reserved_for_future_use_bit,
            reserved_for_future_use_bit,
            attested_credential_data_included_bit,
            extension_data_included_bit
          ].join
        ].pack("b*")
      end

      def serialized_sign_count
        [sign_count].pack('L>')
      end

      def attested_credential_data
        @attested_credential_data ||=
          if credential
            @aaguid +
              [credential[:id].length].pack("n*") +
              credential[:id] +
              cose_credential_public_key
          else
            ""
          end
      end

      def extension_data
        if extensions
          CBOR.encode(extensions)
        else
          ""
        end
      end

      def bit(flag)
        if context[flag]
          "1"
        else
          "0"
        end
      end

      def attested_credential_data_included_bit
        if attested_credential_data.empty?
          "0"
        else
          "1"
        end
      end

      def extension_data_included_bit
        if extension_data.empty?
          "0"
        else
          "1"
        end
      end

      def reserved_for_future_use_bit
        "0"
      end

      def context
        { user_present: user_present, user_verified: user_verified }
      end

      def cose_credential_public_key
        case credential[:public_key]
        when OpenSSL::PKey::RSA
          key = COSE::Key::RSA.from_pkey(credential[:public_key])
          key.alg = -257
        when OpenSSL::PKey::EC::Point
          alg = {
            COSE::Key::Curve.by_name("P-256").id => -7,
            COSE::Key::Curve.by_name("P-384").id => -35,
            COSE::Key::Curve.by_name("P-521").id => -36
          }

          key = COSE::Key::EC2.from_pkey(credential[:public_key])
          key.alg = alg[key.crv]

        end

        key.serialize
      end

      def key_bytes(public_key)
        public_key.to_bn.to_s(2)
      end
    end
  end
end