Skip to content

Commit

Permalink
Merge pull request #10 from santiagorodriguez96/sr--support-FIDO-meta…
Browse files Browse the repository at this point in the history
…data-msd3

Support FIDO MDS v3
  • Loading branch information
bdewater authored Oct 29, 2023
2 parents 1c6373a + 8280a2a commit 43feaa3
Show file tree
Hide file tree
Showing 26 changed files with 444 additions and 400 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ First, you need to [register for an access token](https://mds2.fidoalliance.org/
The cache interface is compatible with Rails' [`ActiveSupport::Cache::Store`](https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html), which means you can configure the gem to use your existing cache or a separate one:
```ruby
FidoMetadata.configure do |config|
config.metadata_token = "your token"
config.cache_backend = Rails.cache # or something like `ActiveSupport::Cache::FileStore.new(...)`
end
```
Expand Down
7 changes: 0 additions & 7 deletions bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,9 @@ require "fido_metadata"
# Configure in-memory cache
require "fido_metadata/test_cache_store"
FidoMetadata.configure do |config|
config.metadata_token = ENV["MDS_TOKEN"]
config.cache_backend = FidoMetadata::TestCacheStore.new
end

unless FidoMetadata.configuration.metadata_token
puts <<~TOKEN_HINT
No MDS token configured via the MDS_TOKEN environment variable.
Set one for this session: FidoMetadata.configuration.metadata_token = 'your token'
TOKEN_HINT
end
puts "Reset the cache via: FidoMetadata.configuration.cache_backend.clear"

# Start REPL
Expand Down
32 changes: 19 additions & 13 deletions lib/Root.cer
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
-----BEGIN CERTIFICATE-----
MIICQzCCAcigAwIBAgIORqmxkzowRM99NQZJurcwCgYIKoZIzj0EAwMwUzELMAkG
A1UEBhMCVVMxFjAUBgNVBAoTDUZJRE8gQWxsaWFuY2UxHTAbBgNVBAsTFE1ldGFk
YXRhIFRPQyBTaWduaW5nMQ0wCwYDVQQDEwRSb290MB4XDTE1MDYxNzAwMDAwMFoX
DTQ1MDYxNzAwMDAwMFowUzELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUZJRE8gQWxs
aWFuY2UxHTAbBgNVBAsTFE1ldGFkYXRhIFRPQyBTaWduaW5nMQ0wCwYDVQQDEwRS
b290MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEFEoo+6jdxg6oUuOloqPjK/nVGyY+
AXCFz1i5JR4OPeFJs+my143ai0p34EX4R1Xxm9xGi9n8F+RxLjLNPHtlkB3X4ims
rfIx7QcEImx1cMTgu5zUiwxLX1ookVhIRSoso2MwYTAOBgNVHQ8BAf8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU0qUfC6f2YshA1Ni9udeO0VS7vEYw
HwYDVR0jBBgwFoAU0qUfC6f2YshA1Ni9udeO0VS7vEYwCgYIKoZIzj0EAwMDaQAw
ZgIxAKulGbSFkDSZusGjbNkAhAkqTkLWo3GrN5nRBNNk2Q4BlG+AvM5q9wa5WciW
DcMdeQIxAMOEzOFsxX9Bo0h4LOFE5y5H8bdPFYW+l5gy1tQiJv+5NUyM2IBB55XU
YjdBz56jSA==
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
WD9f
-----END CERTIFICATE-----
1 change: 0 additions & 1 deletion lib/fido_metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ def self.configure
end

class Configuration
attr_accessor :metadata_token
attr_accessor :cache_backend
end
end
31 changes: 31 additions & 0 deletions lib/fido_metadata/authenticator_get_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require "fido_metadata/attributes"

module FidoMetadata
class AuthenticatorGetInfo
extend Attributes

json_accessor("versions")
json_accessor("extensions")
json_accessor("aaguid")
json_accessor("options")
json_accessor("maxMsgSize")
json_accessor("pinUvAuthProtocols")
json_accessor("maxCredentialCountInList")
json_accessor("maxCredentialIdLength")
json_accessor("transports")
json_accessor("algorithms")
json_accessor("maxSerializedLargeBlobArray")
json_accessor("forcePINChange")
json_accessor("minPINLength")
json_accessor("firmwareVersion")
json_accessor("maxCredBlobLength")
json_accessor("maxRPIDsForSetMinPINLength")
json_accessor("preferredPlatformUvAttempts")
json_accessor("uvModality")
json_accessor("certifications")
json_accessor("remainingDiscoverableCredentials")
json_accessor("vendorPrototypeConfigCommands")
end
end
41 changes: 8 additions & 33 deletions lib/fido_metadata/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,9 @@ class UnverifiedSigningKeyError < DataIntegrityError; end
File.read(File.join(__dir__, "..", "Root.cer"))
)].freeze

def initialize(token)
@token = token
end

def download_toc(uri, trusted_certs: FIDO_ROOT_CERTIFICATES)
response = get_with_token(uri)
payload, _ = JWT.decode(response, nil, true, algorithms: ["ES256"]) do |headers|
response = get(uri)
payload, _ = JWT.decode(response, nil, true, algorithms: ["RS256"]) do |headers|
jwt_certificates = headers["x5c"].map do |encoded|
OpenSSL::X509::Certificate.new(Base64.strict_decode64(encoded))
end
Expand All @@ -44,27 +40,8 @@ def download_toc(uri, trusted_certs: FIDO_ROOT_CERTIFICATES)
payload
end

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

private

def get_with_token(uri)
if @token && !@token.empty?
uri.path += "/" unless uri.path.end_with?("/")
uri.query = "token=#{@token}"
end
get(uri)
end

def get(uri)
get = Net::HTTP::Get.new(uri, DEFAULT_HEADERS)
response = http(uri).request(get)
Expand All @@ -73,14 +50,12 @@ def get(uri)
end

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
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

def download_crls(certificates)
Expand Down
15 changes: 15 additions & 0 deletions lib/fido_metadata/coercer/authenticator_get_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

require "fido_metadata/authenticator_get_info"

module FidoMetadata
module Coercer
module AuthenticatorGetInfo
def self.coerce(value)
return value if value.is_a?(FidoMetadata::AuthenticatorGetInfo)

FidoMetadata::AuthenticatorGetInfo.from_json(value) if value
end
end
end
end
15 changes: 15 additions & 0 deletions lib/fido_metadata/coercer/statement.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

require "fido_metadata/statement"

module FidoMetadata
module Coercer
module Statement
def self.coerce(value)
return value if value.is_a?(FidoMetadata::Statement)

FidoMetadata::Statement.from_json(value) if value
end
end
end
end
91 changes: 0 additions & 91 deletions lib/fido_metadata/constants.rb

This file was deleted.

2 changes: 2 additions & 0 deletions lib/fido_metadata/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require "fido_metadata/coercer/date"
require "fido_metadata/coercer/escaped_uri"
require "fido_metadata/coercer/objects"
require "fido_metadata/coercer/statement"

module FidoMetadata
class Entry
Expand All @@ -21,5 +22,6 @@ class Entry
json_accessor("timeOfLastStatusChange", Coercer::Date)
json_accessor("rogueListURL", Coercer::EscapedURI)
json_accessor("rogueListHash")
json_accessor("metadataStatement", Coercer::Statement)
end
end
26 changes: 10 additions & 16 deletions lib/fido_metadata/statement.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# frozen_string_literal: true

require "fido_metadata/attributes"
require "fido_metadata/constants"
require "fido_metadata/verification_method_descriptor"
require "fido_metadata/coercer/assumed_value"
require "fido_metadata/coercer/bit_field"
require "fido_metadata/coercer/certificates"
require "fido_metadata/coercer/magic_number"
require "fido_metadata/coercer/user_verification_details"
require "fido_metadata/coercer/authenticator_get_info"

module FidoMetadata
class Statement
Expand All @@ -22,31 +22,25 @@ class Statement
json_accessor("authenticatorVersion")
json_accessor("protocolFamily", Coercer::AssumedValue.new("uaf"))
json_accessor("upv")
json_accessor("assertionScheme")
json_accessor("authenticationAlgorithm", Coercer::MagicNumber.new(Constants::AUTHENTICATION_ALGORITHMS))
json_accessor("authenticationAlgorithms",
Coercer::MagicNumber.new(Constants::AUTHENTICATION_ALGORITHMS, array: true))
json_accessor("publicKeyAlgAndEncoding", Coercer::MagicNumber.new(Constants::PUBLIC_KEY_FORMATS))
json_accessor("publicKeyAlgAndEncodings",
Coercer::MagicNumber.new(Constants::PUBLIC_KEY_FORMATS, array: true))
json_accessor("attestationTypes", Coercer::MagicNumber.new(Constants::ATTESTATION_TYPES, array: true))
json_accessor("authenticationAlgorithms")
json_accessor("publicKeyAlgAndEncodings")
json_accessor("attestationTypes")
json_accessor("userVerificationDetails", Coercer::UserVerificationDetails)
json_accessor("keyProtection", Coercer::BitField.new(Constants::KEY_PROTECTION_TYPES))
json_accessor("keyProtection")
json_accessor("isKeyRestricted", Coercer::AssumedValue.new(true))
json_accessor("isFreshUserVerificationRequired", Coercer::AssumedValue.new(true))
json_accessor("matcherProtection",
Coercer::BitField.new(Constants::MATCHER_PROTECTION_TYPES, single_value: true))
json_accessor("matcherProtection")
json_accessor("cryptoStrength")
json_accessor("operatingEnv")
json_accessor("attachmentHint", Coercer::BitField.new(Constants::ATTACHMENT_HINTS))
json_accessor("isSecondFactorOnly")
json_accessor("tcDisplay", Coercer::BitField.new(Constants::TRANSACTION_CONFIRMATION_DISPLAY_TYPES))
json_accessor("attachmentHint")
json_accessor("tcDisplay")
json_accessor("tcDisplayContentType")
json_accessor("tcDisplayPNGCharacteristics")
json_accessor("attestationRootCertificates")
json_accessor("ecdaaTrustAnchors")
json_accessor("icon")
json_accessor("supportedExtensions")
json_accessor("schema")
json_accessor("authenticatorGetInfo", Coercer::AuthenticatorGetInfo)

# Lazy load certificates for compatibility ActiveSupport::Cache. Can be removed once we require a version of
# OpenSSL which includes https://github.com/ruby/openssl/pull/281
Expand Down
11 changes: 3 additions & 8 deletions lib/fido_metadata/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

module FidoMetadata
class Store
METADATA_ENDPOINT = URI("https://mds2.fidoalliance.org/")
METADATA_ENDPOINT = URI("https://mds.fidoalliance.org/")

def table_of_contents
@table_of_contents ||= begin
Expand Down Expand Up @@ -49,8 +49,7 @@ def fetch_statement(aaguid: nil, attestation_certificate_key_id: nil)
end
return unless entry

json = client.download_entry(entry.url, expected_hash: entry.hash)
statement = FidoMetadata::Statement.from_json(json)
statement = entry.metadata_statement
cache_backend.write(key, statement)
statement
end
Expand All @@ -71,12 +70,8 @@ def cache_backend
FidoMetadata.configuration.cache_backend || raise("no cache_backend configured")
end

def metadata_token
FidoMetadata.configuration.metadata_token || raise("no metadata_token configured")
end

def client
@client ||= FidoMetadata::Client.new(metadata_token)
@client ||= FidoMetadata::Client.new
end
end
end
Loading

0 comments on commit 43feaa3

Please sign in to comment.