class WebAuthn::AttestationStatement::Base
def algorithm
def algorithm statement["alg"] end
def attestation_certificate
def attestation_certificate certificates&.first end
def attestation_certificate_key_id
def attestation_certificate_key_id attestation_certificate.subject_key_identifier&.unpack("H*")&.[](0) end
def attestation_root_certificates_store(aaguid: nil, attestation_certificate_key_id: nil)
def attestation_root_certificates_store(aaguid: nil, attestation_certificate_key_id: nil) OpenSSL::X509::Store.new.tap do |store| root_certificates( aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id ).each do |cert| store.add_cert(cert) end end end
def attestation_trust_path
def attestation_trust_path if certificates&.any? certificates end end
def certificates
def certificates @certificates ||= raw_certificates&.map do |raw_certificate| OpenSSL::X509::Certificate.new(raw_certificate) end end
def cose_algorithm
def cose_algorithm @cose_algorithm ||= COSE::Algorithm.find(algorithm).tap do |alg| alg && relying_party.algorithms.include?(alg.name) || raise(UnsupportedAlgorithm, "Unsupported algorithm #{algorithm}") end end
def format
def format WebAuthn::AttestationStatement::FORMAT_TO_CLASS.key(self.class) end
def initialize(statement, relying_party = WebAuthn.configuration.relying_party)
def initialize(statement, relying_party = WebAuthn.configuration.relying_party) @statement = statement @relying_party = relying_party end
def matching_aaguid?(attested_credential_data_aaguid)
def matching_aaguid?(attested_credential_data_aaguid) extension = attestation_certificate&.find_extension(AAGUID_EXTENSION_OID) if extension aaguid_value = OpenSSL::ASN1.decode(extension.value_der).value aaguid_value == attested_credential_data_aaguid else true end end
def matching_public_key?(authenticator_data)
def matching_public_key?(authenticator_data) attestation_certificate.public_key.to_der == authenticator_data.credential.public_key_object.to_der end
def raw_certificates
def raw_certificates statement["x5c"] end
def root_certificates(aaguid: nil, attestation_certificate_key_id: nil)
def root_certificates(aaguid: nil, attestation_certificate_key_id: nil) root_certificates = relying_party.attestation_root_certificates_finders.reduce([]) do |certs, finder| if certs.empty? finder.find( attestation_format: format, aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id ) || [] else certs end end if root_certificates.empty? && respond_to?(:default_root_certificates, true) default_root_certificates else root_certificates end end
def signature
def signature statement["sig"] end
def trustworthy?(aaguid: nil, attestation_certificate_key_id: nil)
def trustworthy?(aaguid: nil, attestation_certificate_key_id: nil) if ATTESTATION_TYPES_WITH_ROOT.include?(attestation_type) relying_party.acceptable_attestation_types.include?(attestation_type) && valid_certificate_chain?(aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id) else relying_party.acceptable_attestation_types.include?(attestation_type) end end
def valid?(_authenticator_data, _client_data_hash)
def valid?(_authenticator_data, _client_data_hash) raise NotImplementedError end
def valid_certificate_chain?(aaguid: nil, attestation_certificate_key_id: nil)
def valid_certificate_chain?(aaguid: nil, attestation_certificate_key_id: nil) root_certificates = root_certificates( aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id ) if certificates&.one? && root_certificates.include?(attestation_certificate) return true end attestation_root_certificates_store( aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id ).verify(attestation_certificate, attestation_trust_path) end
def valid_signature?(authenticator_data, client_data_hash, public_key = attestation_certificate.public_key)
def valid_signature?(authenticator_data, client_data_hash, public_key = attestation_certificate.public_key) raise("Incompatible algorithm and key") unless cose_algorithm.compatible_key?(public_key) cose_algorithm.verify( public_key, signature, verification_data(authenticator_data, client_data_hash) ) rescue COSE::Error false end
def verification_data(authenticator_data, client_data_hash)
def verification_data(authenticator_data, client_data_hash) authenticator_data.data + client_data_hash end