class FidoMetadata::Client
Constants
- DEFAULT_HEADERS
- FIDO_ROOT_CERTIFICATES
Public Class Methods
new(token)
click to toggle source
# File lib/fido_metadata/client.rb, line 26 def initialize(token) @token = token end
Public Instance Methods
download_entry(uri, expected_hash:)
click to toggle source
# File lib/fido_metadata/client.rb, line 47 def download_entry(uri, expected_hash:) response = get_with_token(uri) decoded_hash = Base64.urlsafe_decode64(expected_hash) unless OpenSSL.fixed_length_secure_compare(OpenSSL::Digest::SHA256.digest(response), decoded_hash) raise(InvalidHashError) end decoded_body = Base64.urlsafe_decode64(response) JSON.parse(decoded_body) end
download_toc(uri, trusted_certs: FIDO_ROOT_CERTIFICATES)
click to toggle source
# File lib/fido_metadata/client.rb, line 30 def download_toc(uri, trusted_certs: FIDO_ROOT_CERTIFICATES) response = get_with_token(uri) payload, _ = JWT.decode(response, nil, true, algorithms: ["ES256"]) do |headers| jwt_certificates = headers["x5c"].map do |encoded| OpenSSL::X509::Certificate.new(Base64.strict_decode64(encoded)) end crls = download_crls(jwt_certificates) begin X5cKeyFinder.from(jwt_certificates, trusted_certs, crls) rescue JWT::VerificationError => e raise(UnverifiedSigningKeyError, e.message) end end payload end
Private Instance Methods
download_crls(certificates)
click to toggle source
# File lib/fido_metadata/client.rb, line 86 def download_crls(certificates) uris = extract_crl_distribution_points(certificates) crls = uris.compact.uniq.map do |uri| begin get(uri) rescue Net::ProtocolError # TODO: figure out why test endpoint specifies a missing and unused CRL in the cert chain, and see if this # rescue can be removed. If the CRL is used, OpenSSL error 3 (unable to get certificate CRL) will raise. nil end end crls.compact.map { |crl| OpenSSL::X509::CRL.new(crl) } end
extract_crl_distribution_points(certificates)
click to toggle source
# File lib/fido_metadata/client.rb, line 101 def extract_crl_distribution_points(certificates) certificates.map do |certificate| extension = certificate.extensions.detect { |ext| ext.oid == "crlDistributionPoints" } # TODO: replace this with proper parsing of deeply nested ASN1 structures match = extension&.value&.match(/URI:(?<uri>\S*)/) URI(match[:uri]) if match end end
get(uri)
click to toggle source
# File lib/fido_metadata/client.rb, line 68 def get(uri) get = Net::HTTP::Get.new(uri, DEFAULT_HEADERS) response = http(uri).request(get) response.value response.body end
get_with_token(uri)
click to toggle source
# File lib/fido_metadata/client.rb, line 60 def get_with_token(uri) if @token && !@token.empty? uri.path += "/" unless uri.path.end_with?("/") uri.query = "token=#{@token}" end get(uri) end
http(uri)
click to toggle source
# File lib/fido_metadata/client.rb, line 75 def http(uri) @http ||= begin http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.port == 443 http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.open_timeout = 5 http.read_timeout = 5 http end end