File: packed.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 (72 lines) | stat: -rw-r--r-- 2,381 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
# frozen_string_literal: true

require "openssl"
require "webauthn/attestation_statement/base"

module WebAuthn
  # Implements https://www.w3.org/TR/2018/CR-webauthn-20180807/#packed-attestation
  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

      private

      def valid_algorithm?(credential)
        !self_attestation? || algorithm == COSE::Key.deserialize(credential.public_key).alg
      end

      def self_attestation?
        !raw_certificates
      end

      def valid_format?
        algorithm && signature
      end

      def valid_ec_public_keys?(credential)
        (certificates&.map(&:public_key) || [credential.public_key_object])
          .select { |pkey| pkey.is_a?(OpenSSL::PKey::EC) }
          .all? { |pkey| pkey.check_key }
      end

      # Check https://www.w3.org/TR/2018/CR-webauthn-20180807/#packed-attestation-cert-requirements
      def meet_certificate_requirement?
        if attestation_certificate
          subject = attestation_certificate.subject.to_a

          attestation_certificate.version == 2 &&
            subject.assoc('OU')&.at(1) == "Authenticator Attestation" &&
            attestation_certificate.find_extension('basicConstraints')&.value == 'CA:FALSE'
        else
          true
        end
      end

      def attestation_type
        if attestation_trust_path
          WebAuthn::AttestationStatement::ATTESTATION_TYPE_BASIC_OR_ATTCA # FIXME: use metadata if available
        else
          WebAuthn::AttestationStatement::ATTESTATION_TYPE_SELF
        end
      end

      def valid_signature?(authenticator_data, client_data_hash)
        super(
          authenticator_data,
          client_data_hash,
          attestation_certificate&.public_key || authenticator_data.credential.public_key_object
        )
      end
    end
  end
end