fido_metadata 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +196 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +31 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +75 -0
- data/LICENSE.txt +21 -0
- data/README.md +86 -0
- data/Rakefile +12 -0
- data/bin/console +24 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +8 -0
- data/fido_metadata.gemspec +41 -0
- data/lib/Root.cer +15 -0
- data/lib/fido_metadata.rb +19 -0
- data/lib/fido_metadata/attributes.rb +37 -0
- data/lib/fido_metadata/biometric_accuracy_descriptor.rb +15 -0
- data/lib/fido_metadata/biometric_status_report.rb +18 -0
- data/lib/fido_metadata/client.rb +110 -0
- data/lib/fido_metadata/code_accuracy_descriptor.rb +14 -0
- data/lib/fido_metadata/coercer/assumed_value.rb +19 -0
- data/lib/fido_metadata/coercer/bit_field.rb +22 -0
- data/lib/fido_metadata/coercer/certificates.rb +16 -0
- data/lib/fido_metadata/coercer/date.rb +15 -0
- data/lib/fido_metadata/coercer/escaped_uri.rb +17 -0
- data/lib/fido_metadata/coercer/magic_number.rb +24 -0
- data/lib/fido_metadata/coercer/objects.rb +18 -0
- data/lib/fido_metadata/coercer/user_verification_details.rb +36 -0
- data/lib/fido_metadata/constants.rb +91 -0
- data/lib/fido_metadata/entry.rb +25 -0
- data/lib/fido_metadata/pattern_accuracy_descriptor.rb +13 -0
- data/lib/fido_metadata/refinement/fixed_length_secure_compare.rb +23 -0
- data/lib/fido_metadata/statement.rb +65 -0
- data/lib/fido_metadata/status_report.rb +20 -0
- data/lib/fido_metadata/store.rb +82 -0
- data/lib/fido_metadata/table_of_contents.rb +17 -0
- data/lib/fido_metadata/test_cache_store.rb +26 -0
- data/lib/fido_metadata/verification_method_descriptor.rb +20 -0
- data/lib/fido_metadata/version.rb +5 -0
- data/lib/fido_metadata/x5c_key_finder.rb +50 -0
- metadata +186 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module FidoMetadata
|
|
6
|
+
module Coercer
|
|
7
|
+
module EscapedURI
|
|
8
|
+
# The character # is a reserved character and not allowed in URLs, it is replaced by its hex value %x23.
|
|
9
|
+
# https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-metadata-service-v2.0-rd-20180702.html#idl-def-MetadataTOCPayloadEntry
|
|
10
|
+
def self.coerce(value)
|
|
11
|
+
return value if value.is_a?(URI)
|
|
12
|
+
|
|
13
|
+
URI(value.gsub(/%x23/, "#")) if value
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FidoMetadata
|
|
4
|
+
module Coercer
|
|
5
|
+
class MagicNumber
|
|
6
|
+
def initialize(mapping, array: false)
|
|
7
|
+
@mapping = mapping
|
|
8
|
+
@array = array
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def coerce(values)
|
|
12
|
+
if @array
|
|
13
|
+
return values unless values.all? { |value| value.is_a?(Integer) }
|
|
14
|
+
|
|
15
|
+
values.map { |value| @mapping[value] }.compact
|
|
16
|
+
else
|
|
17
|
+
return values unless values.is_a?(Integer)
|
|
18
|
+
|
|
19
|
+
@mapping[values]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FidoMetadata
|
|
4
|
+
module Coercer
|
|
5
|
+
class Objects
|
|
6
|
+
def initialize(klass)
|
|
7
|
+
@klass = klass
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def coerce(values)
|
|
11
|
+
return unless values.is_a?(Array)
|
|
12
|
+
return values if values.all? { |value| value.is_a?(@klass) }
|
|
13
|
+
|
|
14
|
+
values.map { |value| @klass.from_json(value) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fido_metadata/verification_method_descriptor"
|
|
4
|
+
|
|
5
|
+
module FidoMetadata
|
|
6
|
+
module Coercer
|
|
7
|
+
class UserVerificationDetails
|
|
8
|
+
def self.coerce(values)
|
|
9
|
+
return unless values.is_a?(Array)
|
|
10
|
+
return values if values.all? do |array|
|
|
11
|
+
array.all? do |object|
|
|
12
|
+
object.is_a?(VerificationMethodDescriptor)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
values.map do |array|
|
|
17
|
+
array.map do |hash|
|
|
18
|
+
object = FidoMetadata::VerificationMethodDescriptor.from_json(hash)
|
|
19
|
+
|
|
20
|
+
if hash["baDesc"]
|
|
21
|
+
object.ba_desc = FidoMetadata::BiometricAccuracyDescriptor.from_json(hash["baDesc"])
|
|
22
|
+
end
|
|
23
|
+
if hash["caDesc"]
|
|
24
|
+
object.ca_desc = FidoMetadata::CodeAccuracyDescriptor.from_json(hash["caDesc"])
|
|
25
|
+
end
|
|
26
|
+
if hash["paDesc"]
|
|
27
|
+
object.pa_desc = FidoMetadata::PatternAccuracyDescriptor.from_json(hash["paDesc"])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
object
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FidoMetadata
|
|
4
|
+
module Constants
|
|
5
|
+
# https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-registry-v2.0-rd-20180702.html
|
|
6
|
+
|
|
7
|
+
ATTACHMENT_HINTS = {
|
|
8
|
+
0x0001 => "INTERNAL",
|
|
9
|
+
0x0002 => "EXTERNAL",
|
|
10
|
+
0x0004 => "WIRED",
|
|
11
|
+
0x0008 => "WIRELESS",
|
|
12
|
+
0x0010 => "NFC",
|
|
13
|
+
0x0020 => "BLUETOOTH",
|
|
14
|
+
0x0040 => "NETWORK",
|
|
15
|
+
0x0080 => "READY",
|
|
16
|
+
0x0100 => "WIFI_DIRECT",
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
ATTESTATION_TYPES = {
|
|
20
|
+
0x3E07 => "BASIC_FULL", # 'Basic' in WebAuthn
|
|
21
|
+
0x3E08 => "BASIC_SURROGATE", # 'Self' in WebAuthn
|
|
22
|
+
0x3E09 => "ECDAA",
|
|
23
|
+
0x3E0A => "ATTCA",
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
AUTHENTICATION_ALGORITHMS = {
|
|
27
|
+
0x0001 => "SECP256R1_ECDSA_SHA256_RAW",
|
|
28
|
+
0x0002 => "SECP256R1_ECDSA_SHA256_DER",
|
|
29
|
+
0x0003 => "RSASSA_PSS_SHA256_RAW",
|
|
30
|
+
0x0004 => "RSASSA_PSS_SHA256_DER",
|
|
31
|
+
0x0005 => "SECP256K1_ECDSA_SHA256_RAW",
|
|
32
|
+
0x0006 => "SECP256K1_ECDSA_SHA256_DER",
|
|
33
|
+
0x0007 => "SM2_SM3_RAW",
|
|
34
|
+
0x0008 => "RSA_EMSA_PKCS1_SHA256_RAW",
|
|
35
|
+
0x0009 => "RSA_EMSA_PKCS1_SHA256_DER",
|
|
36
|
+
0x000A => "RSASSA_PSS_SHA384_RAW",
|
|
37
|
+
0x000B => "RSASSA_PSS_SHA512_RAW",
|
|
38
|
+
0x000C => "RSASSA_PKCSV15_SHA256_RAW",
|
|
39
|
+
0x000D => "RSASSA_PKCSV15_SHA384_RAW",
|
|
40
|
+
0x000E => "RSASSA_PKCSV15_SHA512_RAW",
|
|
41
|
+
0x000F => "RSASSA_PKCSV15_SHA1_RAW",
|
|
42
|
+
0x0010 => "SECP384R1_ECDSA_SHA384_RAW",
|
|
43
|
+
0x0011 => "SECP521R1_ECDSA_SHA512_RAW",
|
|
44
|
+
0x0012 => "ED25519_EDDSA_SHA256_RAW",
|
|
45
|
+
}.freeze
|
|
46
|
+
|
|
47
|
+
KEY_PROTECTION_TYPES = {
|
|
48
|
+
0x0001 => "SOFTWARE",
|
|
49
|
+
0x0002 => "HARDWARE",
|
|
50
|
+
0x0004 => "TEE",
|
|
51
|
+
0x0008 => "SECURE_ELEMENT",
|
|
52
|
+
0x0010 => "REMOTE_HANDLE",
|
|
53
|
+
}.freeze
|
|
54
|
+
|
|
55
|
+
MATCHER_PROTECTION_TYPES = {
|
|
56
|
+
0x0001 => "SOFTWARE",
|
|
57
|
+
0x0002 => "TEE",
|
|
58
|
+
0x0004 => "ON_CHIP",
|
|
59
|
+
}.freeze
|
|
60
|
+
|
|
61
|
+
PUBLIC_KEY_FORMATS = {
|
|
62
|
+
0x0100 => "ECC_X962_RAW",
|
|
63
|
+
0x0101 => "ECC_X962_DER",
|
|
64
|
+
0x0102 => "RSA_2048_RAW",
|
|
65
|
+
0x0103 => "RSA_2048_DER",
|
|
66
|
+
0x0104 => "COSE",
|
|
67
|
+
}.freeze
|
|
68
|
+
|
|
69
|
+
TRANSACTION_CONFIRMATION_DISPLAY_TYPES = {
|
|
70
|
+
0x0001 => "ANY",
|
|
71
|
+
0x0002 => "PRIVILEGED_SOFTWARE",
|
|
72
|
+
0x0004 => "TEE",
|
|
73
|
+
0x0008 => "HARDWARE",
|
|
74
|
+
0x0010 => "REMOTE",
|
|
75
|
+
}.freeze
|
|
76
|
+
|
|
77
|
+
USER_VERIFICATION_METHODS = {
|
|
78
|
+
0x00000001 => "PRESENCE",
|
|
79
|
+
0x00000002 => "FINGERPRINT",
|
|
80
|
+
0x00000004 => "PASSCODE",
|
|
81
|
+
0x00000008 => "VOICEPRINT",
|
|
82
|
+
0x00000010 => "FACEPRINT",
|
|
83
|
+
0x00000020 => "LOCATION",
|
|
84
|
+
0x00000040 => "EYEPRINT",
|
|
85
|
+
0x00000080 => "PATTERN",
|
|
86
|
+
0x00000100 => "HANDPRINT",
|
|
87
|
+
0x00000200 => "NONE",
|
|
88
|
+
0x00000400 => "ALL",
|
|
89
|
+
}.freeze
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fido_metadata/attributes"
|
|
4
|
+
require "fido_metadata/biometric_status_report"
|
|
5
|
+
require "fido_metadata/status_report"
|
|
6
|
+
require "fido_metadata/coercer/date"
|
|
7
|
+
require "fido_metadata/coercer/escaped_uri"
|
|
8
|
+
require "fido_metadata/coercer/objects"
|
|
9
|
+
|
|
10
|
+
module FidoMetadata
|
|
11
|
+
class Entry
|
|
12
|
+
extend Attributes
|
|
13
|
+
|
|
14
|
+
json_accessor("aaid")
|
|
15
|
+
json_accessor("aaguid")
|
|
16
|
+
json_accessor("attestationCertificateKeyIdentifiers")
|
|
17
|
+
json_accessor("hash")
|
|
18
|
+
json_accessor("url", Coercer::EscapedURI)
|
|
19
|
+
json_accessor("biometricStatusReports", Coercer::Objects.new(BiometricStatusReport))
|
|
20
|
+
json_accessor("statusReports", Coercer::Objects.new(StatusReport))
|
|
21
|
+
json_accessor("timeOfLastStatusChange", Coercer::Date)
|
|
22
|
+
json_accessor("rogueListURL", Coercer::EscapedURI)
|
|
23
|
+
json_accessor("rogueListHash")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fido_metadata/attributes"
|
|
4
|
+
|
|
5
|
+
module FidoMetadata
|
|
6
|
+
class PatternAccuracyDescriptor
|
|
7
|
+
extend Attributes
|
|
8
|
+
|
|
9
|
+
json_accessor("minComplexity")
|
|
10
|
+
json_accessor("maxRetries")
|
|
11
|
+
json_accessor("blockSlowdown")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
|
|
5
|
+
module FidoMetadata
|
|
6
|
+
module Refinement
|
|
7
|
+
module FixedLengthSecureCompare
|
|
8
|
+
unless OpenSSL.singleton_class.method_defined?(:fixed_length_secure_compare)
|
|
9
|
+
refine OpenSSL.singleton_class do
|
|
10
|
+
def fixed_length_secure_compare(a, b) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
11
|
+
raise ArgumentError, "inputs must be of equal length" unless a.bytesize == b.bytesize
|
|
12
|
+
|
|
13
|
+
# borrowed from Rack::Utils
|
|
14
|
+
l = a.unpack("C*")
|
|
15
|
+
r, i = 0, -1
|
|
16
|
+
b.each_byte { |v| r |= v ^ l[i += 1] }
|
|
17
|
+
r == 0
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fido_metadata/attributes"
|
|
4
|
+
require "fido_metadata/constants"
|
|
5
|
+
require "fido_metadata/verification_method_descriptor"
|
|
6
|
+
require "fido_metadata/coercer/assumed_value"
|
|
7
|
+
require "fido_metadata/coercer/bit_field"
|
|
8
|
+
require "fido_metadata/coercer/certificates"
|
|
9
|
+
require "fido_metadata/coercer/magic_number"
|
|
10
|
+
require "fido_metadata/coercer/user_verification_details"
|
|
11
|
+
|
|
12
|
+
module FidoMetadata
|
|
13
|
+
class Statement
|
|
14
|
+
extend Attributes
|
|
15
|
+
|
|
16
|
+
json_accessor("legalHeader")
|
|
17
|
+
json_accessor("aaid")
|
|
18
|
+
json_accessor("aaguid")
|
|
19
|
+
json_accessor("attestationCertificateKeyIdentifiers")
|
|
20
|
+
json_accessor("description")
|
|
21
|
+
json_accessor("alternativeDescriptions")
|
|
22
|
+
json_accessor("authenticatorVersion")
|
|
23
|
+
json_accessor("protocolFamily", Coercer::AssumedValue.new("uaf"))
|
|
24
|
+
json_accessor("upv")
|
|
25
|
+
json_accessor("assertionScheme")
|
|
26
|
+
json_accessor("authenticationAlgorithm", Coercer::MagicNumber.new(Constants::AUTHENTICATION_ALGORITHMS))
|
|
27
|
+
json_accessor("authenticationAlgorithms",
|
|
28
|
+
Coercer::MagicNumber.new(Constants::AUTHENTICATION_ALGORITHMS, array: true))
|
|
29
|
+
json_accessor("publicKeyAlgAndEncoding", Coercer::MagicNumber.new(Constants::PUBLIC_KEY_FORMATS))
|
|
30
|
+
json_accessor("publicKeyAlgAndEncodings",
|
|
31
|
+
Coercer::MagicNumber.new(Constants::PUBLIC_KEY_FORMATS, array: true))
|
|
32
|
+
json_accessor("attestationTypes", Coercer::MagicNumber.new(Constants::ATTESTATION_TYPES, array: true))
|
|
33
|
+
json_accessor("userVerificationDetails", Coercer::UserVerificationDetails)
|
|
34
|
+
json_accessor("keyProtection", Coercer::BitField.new(Constants::KEY_PROTECTION_TYPES))
|
|
35
|
+
json_accessor("isKeyRestricted", Coercer::AssumedValue.new(true))
|
|
36
|
+
json_accessor("isFreshUserVerificationRequired", Coercer::AssumedValue.new(true))
|
|
37
|
+
json_accessor("matcherProtection",
|
|
38
|
+
Coercer::BitField.new(Constants::MATCHER_PROTECTION_TYPES, single_value: true))
|
|
39
|
+
json_accessor("cryptoStrength")
|
|
40
|
+
json_accessor("operatingEnv")
|
|
41
|
+
json_accessor("attachmentHint", Coercer::BitField.new(Constants::ATTACHMENT_HINTS))
|
|
42
|
+
json_accessor("isSecondFactorOnly")
|
|
43
|
+
json_accessor("tcDisplay", Coercer::BitField.new(Constants::TRANSACTION_CONFIRMATION_DISPLAY_TYPES))
|
|
44
|
+
json_accessor("tcDisplayContentType")
|
|
45
|
+
json_accessor("tcDisplayPNGCharacteristics")
|
|
46
|
+
json_accessor("attestationRootCertificates")
|
|
47
|
+
json_accessor("ecdaaTrustAnchors")
|
|
48
|
+
json_accessor("icon")
|
|
49
|
+
json_accessor("supportedExtensions")
|
|
50
|
+
|
|
51
|
+
# Lazy load certificates for compatibility ActiveSupport::Cache. Can be removed once we require a version of
|
|
52
|
+
# OpenSSL which includes https://github.com/ruby/openssl/pull/281
|
|
53
|
+
def attestation_root_certificates
|
|
54
|
+
Coercer::Certificates.coerce(@attestation_root_certificates)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def trust_store
|
|
58
|
+
trust_store = OpenSSL::X509::Store.new
|
|
59
|
+
attestation_root_certificates.each do |certificate|
|
|
60
|
+
trust_store.add_cert(certificate)
|
|
61
|
+
end
|
|
62
|
+
trust_store
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fido_metadata/attributes"
|
|
4
|
+
require "fido_metadata/coercer/date"
|
|
5
|
+
require "fido_metadata/coercer/escaped_uri"
|
|
6
|
+
|
|
7
|
+
module FidoMetadata
|
|
8
|
+
class StatusReport
|
|
9
|
+
extend Attributes
|
|
10
|
+
|
|
11
|
+
json_accessor("status")
|
|
12
|
+
json_accessor("effectiveDate", Coercer::Date)
|
|
13
|
+
json_accessor("certificate")
|
|
14
|
+
json_accessor("url", Coercer::EscapedURI)
|
|
15
|
+
json_accessor("certificationDescriptor")
|
|
16
|
+
json_accessor("certificateNumber")
|
|
17
|
+
json_accessor("certificationPolicyVersion")
|
|
18
|
+
json_accessor("certificationRequirementsVersion")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fido_metadata/client"
|
|
4
|
+
require "fido_metadata/table_of_contents"
|
|
5
|
+
require "fido_metadata/statement"
|
|
6
|
+
|
|
7
|
+
module FidoMetadata
|
|
8
|
+
class Store
|
|
9
|
+
METADATA_ENDPOINT = URI("https://mds2.fidoalliance.org/")
|
|
10
|
+
|
|
11
|
+
def table_of_contents
|
|
12
|
+
@table_of_contents ||= begin
|
|
13
|
+
key = "metadata_toc"
|
|
14
|
+
toc = cache_backend.read(key)
|
|
15
|
+
return toc if toc
|
|
16
|
+
|
|
17
|
+
json = client.download_toc(METADATA_ENDPOINT)
|
|
18
|
+
toc = FidoMetadata::TableOfContents.from_json(json)
|
|
19
|
+
cache_backend.write(key, toc)
|
|
20
|
+
toc
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def fetch_entry(aaguid: nil, attestation_certificate_key_id: nil)
|
|
25
|
+
verify_arguments(aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id)
|
|
26
|
+
|
|
27
|
+
if aaguid
|
|
28
|
+
table_of_contents.entries.detect { |entry| entry.aaguid == aaguid }
|
|
29
|
+
elsif attestation_certificate_key_id
|
|
30
|
+
table_of_contents.entries.detect do |entry|
|
|
31
|
+
entry.attestation_certificate_key_identifiers&.detect do |id|
|
|
32
|
+
id == attestation_certificate_key_id
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def fetch_statement(aaguid: nil, attestation_certificate_key_id: nil)
|
|
39
|
+
verify_arguments(aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id)
|
|
40
|
+
|
|
41
|
+
key = "statement_#{aaguid || attestation_certificate_key_id}"
|
|
42
|
+
statement = cache_backend.read(key)
|
|
43
|
+
return statement if statement
|
|
44
|
+
|
|
45
|
+
entry = if aaguid
|
|
46
|
+
fetch_entry(aaguid: aaguid)
|
|
47
|
+
elsif attestation_certificate_key_id
|
|
48
|
+
fetch_entry(attestation_certificate_key_id: attestation_certificate_key_id)
|
|
49
|
+
end
|
|
50
|
+
return unless entry
|
|
51
|
+
|
|
52
|
+
json = client.download_entry(entry.url, expected_hash: entry.hash)
|
|
53
|
+
statement = FidoMetadata::Statement.from_json(json)
|
|
54
|
+
cache_backend.write(key, statement)
|
|
55
|
+
statement
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def verify_arguments(aaguid: nil, attestation_certificate_key_id: nil)
|
|
61
|
+
unless aaguid || attestation_certificate_key_id
|
|
62
|
+
raise ArgumentError, "must pass either aaguid or attestation_certificate_key"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if aaguid && attestation_certificate_key_id
|
|
66
|
+
raise ArgumentError, "cannot pass both aaguid and attestation_certificate_key"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def cache_backend
|
|
71
|
+
FidoMetadata.configuration.cache_backend || raise("no cache_backend configured")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def metadata_token
|
|
75
|
+
FidoMetadata.configuration.metadata_token || raise("no metadata_token configured")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def client
|
|
79
|
+
@client ||= FidoMetadata::Client.new(metadata_token)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fido_metadata/attributes"
|
|
4
|
+
require "fido_metadata/entry"
|
|
5
|
+
require "fido_metadata/coercer/date"
|
|
6
|
+
require "fido_metadata/coercer/objects"
|
|
7
|
+
|
|
8
|
+
module FidoMetadata
|
|
9
|
+
class TableOfContents
|
|
10
|
+
extend Attributes
|
|
11
|
+
|
|
12
|
+
json_accessor("legalHeader")
|
|
13
|
+
json_accessor("nextUpdate", Coercer::Date)
|
|
14
|
+
json_accessor("entries", Coercer::Objects.new(Entry))
|
|
15
|
+
json_accessor("no")
|
|
16
|
+
end
|
|
17
|
+
end
|