class WebAuthn::AttestationStatement::Base

Constants

AAGUID_EXTENSION_OID

Attributes

statement[R]

Public Class Methods

new(statement) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 31
def initialize(statement)
  @statement = statement
end

Public Instance Methods

attestation_certificate() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 43
def attestation_certificate
  certificates&.first
end
attestation_certificate_key_id() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 47
def attestation_certificate_key_id
  raw_subject_key_identifier&.unpack("H*")&.[](0)
end
format() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 39
def format
  WebAuthn::AttestationStatement::FORMAT_TO_CLASS.key(self.class)
end
valid?(_authenticator_data, _client_data_hash) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 35
def valid?(_authenticator_data, _client_data_hash)
  raise NotImplementedError
end

Private Instance Methods

algorithm() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 78
def algorithm
  statement["alg"]
end
attestation_root_certificates_store(aaguid: nil, attestation_certificate_key_id: nil) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 112
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
attestation_trust_path() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 90
def attestation_trust_path
  if certificates&.any?
    certificates
  end
end
certificates() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 71
def certificates
  @certificates ||=
    raw_certificates&.map do |raw_certificate|
      OpenSSL::X509::Certificate.new(raw_certificate)
    end
end
configuration() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 177
def configuration
  WebAuthn.configuration
end
cose_algorithm() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 169
def cose_algorithm
  @cose_algorithm ||=
    COSE::Algorithm.find(algorithm).tap do |alg|
      alg && configuration.algorithms.include?(alg.name) ||
        raise(UnsupportedAlgorithm, "Unsupported algorithm #{algorithm}")
    end
end
matching_aaguid?(attested_credential_data_aaguid) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 55
def matching_aaguid?(attested_credential_data_aaguid)
  extension = attestation_certificate&.extensions&.detect { |ext| ext.oid == AAGUID_EXTENSION_OID }
  if extension
    # `extension.value` mangles data into ASCII, so we must manually compare bytes
    # see https://github.com/ruby/openssl/pull/234
    extension.to_der[-WebAuthn::AuthenticatorData::AttestedCredentialData::AAGUID_LENGTH..-1] ==
      attested_credential_data_aaguid
  else
    true
  end
end
matching_public_key?(authenticator_data) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 67
def matching_public_key?(authenticator_data)
  attestation_certificate.public_key.to_der == authenticator_data.credential.public_key_object.to_der
end
raw_certificates() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 82
def raw_certificates
  statement["x5c"]
end
raw_subject_key_identifier() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 144
def raw_subject_key_identifier
  extension = attestation_certificate.extensions.detect { |ext| ext.oid == "subjectKeyIdentifier" }
  return unless extension

  ext_asn1 = OpenSSL::ASN1.decode(extension.to_der)
  ext_value = ext_asn1.value.last
  OpenSSL::ASN1.decode(ext_value.value).value
end
root_certificates(aaguid: nil, attestation_certificate_key_id: nil) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 123
def root_certificates(aaguid: nil, attestation_certificate_key_id: nil)
  root_certificates =
    configuration.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
signature() click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 86
def signature
  statement["sig"]
end
trustworthy?(aaguid: nil, attestation_certificate_key_id: nil) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 96
def trustworthy?(aaguid: nil, attestation_certificate_key_id: nil)
  if ATTESTATION_TYPES_WITH_ROOT.include?(attestation_type)
    configuration.acceptable_attestation_types.include?(attestation_type) &&
      valid_certificate_chain?(aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id)
  else
    configuration.acceptable_attestation_types.include?(attestation_type)
  end
end
valid_certificate_chain?(aaguid: nil, attestation_certificate_key_id: nil) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 105
def valid_certificate_chain?(aaguid: nil, attestation_certificate_key_id: nil)
  attestation_root_certificates_store(
    aaguid: aaguid,
    attestation_certificate_key_id: attestation_certificate_key_id
  ).verify(attestation_certificate, attestation_trust_path)
end
valid_signature?(authenticator_data, client_data_hash, public_key = attestation_certificate.public_key) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 153
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
verification_data(authenticator_data, client_data_hash) click to toggle source
# File lib/webauthn/attestation_statement/base.rb, line 165
def verification_data(authenticator_data, client_data_hash)
  authenticator_data.data + client_data_hash
end