webauthn 2.1.0 → 2.4.1
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 +4 -4
- data/.rubocop.yml +113 -13
- data/.travis.yml +21 -18
- data/Appraisals +4 -0
- data/CHANGELOG.md +41 -0
- data/CONTRIBUTING.md +0 -5
- data/README.md +70 -8
- data/SECURITY.md +6 -4
- data/gemfiles/openssl_2_2.gemfile +7 -0
- data/lib/cose/rsapkcs1_algorithm.rb +50 -0
- data/lib/webauthn/attestation_object.rb +43 -0
- data/lib/webauthn/attestation_statement.rb +20 -20
- data/lib/webauthn/attestation_statement/android_key.rb +28 -30
- data/lib/webauthn/attestation_statement/android_safetynet.rb +27 -7
- data/lib/webauthn/attestation_statement/base.rb +108 -10
- data/lib/webauthn/attestation_statement/fido_u2f.rb +8 -6
- data/lib/webauthn/attestation_statement/none.rb +7 -1
- data/lib/webauthn/attestation_statement/packed.rb +13 -41
- data/lib/webauthn/attestation_statement/tpm.rb +38 -75
- data/lib/webauthn/authenticator_assertion_response.rb +3 -7
- data/lib/webauthn/authenticator_attestation_response.rb +19 -84
- data/lib/webauthn/authenticator_data.rb +51 -51
- data/lib/webauthn/authenticator_data/attested_credential_data.rb +29 -50
- data/lib/webauthn/authenticator_response.rb +3 -0
- data/lib/webauthn/credential_creation_options.rb +2 -0
- data/lib/webauthn/credential_request_options.rb +2 -0
- data/lib/webauthn/fake_authenticator.rb +7 -3
- data/lib/webauthn/fake_authenticator/attestation_object.rb +7 -3
- data/lib/webauthn/fake_authenticator/authenticator_data.rb +2 -4
- data/lib/webauthn/fake_client.rb +19 -5
- data/lib/webauthn/public_key.rb +21 -2
- data/lib/webauthn/public_key_credential.rb +13 -3
- data/lib/webauthn/u2f_migrator.rb +5 -4
- data/lib/webauthn/version.rb +1 -1
- data/script/ci/install-openssl +7 -0
- data/script/ci/install-ruby +13 -0
- data/webauthn.gemspec +13 -9
- metadata +54 -41
- data/lib/android_safetynet/attestation_response.rb +0 -116
- data/lib/cose/rsassa_algorithm.rb +0 -10
- data/lib/tpm/constants.rb +0 -44
- data/lib/tpm/s_attest.rb +0 -26
- data/lib/tpm/s_attest/s_certify_info.rb +0 -14
- data/lib/tpm/sized_buffer.rb +0 -13
- data/lib/tpm/t_public.rb +0 -32
- data/lib/tpm/t_public/s_ecc_parms.rb +0 -17
- data/lib/tpm/t_public/s_rsa_parms.rb +0 -17
- data/lib/webauthn/attestation_statement/android_key/authorization_list.rb +0 -39
- data/lib/webauthn/attestation_statement/android_key/key_description.rb +0 -37
- data/lib/webauthn/attestation_statement/tpm/cert_info.rb +0 -44
- data/lib/webauthn/attestation_statement/tpm/pub_area.rb +0 -85
- data/lib/webauthn/signature_verifier.rb +0 -77
@@ -4,7 +4,6 @@ require "cose"
|
|
4
4
|
require "openssl"
|
5
5
|
require "webauthn/attestation_statement/base"
|
6
6
|
require "webauthn/attestation_statement/fido_u2f/public_key"
|
7
|
-
require "webauthn/signature_verifier"
|
8
7
|
|
9
8
|
module WebAuthn
|
10
9
|
module AttestationStatement
|
@@ -19,7 +18,8 @@ module WebAuthn
|
|
19
18
|
valid_credential_public_key?(authenticator_data.credential.public_key) &&
|
20
19
|
valid_aaguid?(authenticator_data.attested_credential_data.raw_aaguid) &&
|
21
20
|
valid_signature?(authenticator_data, client_data_hash) &&
|
22
|
-
|
21
|
+
trustworthy?(attestation_certificate_key_id: attestation_certificate_key_id) &&
|
22
|
+
[attestation_type, attestation_trust_path]
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
@@ -47,10 +47,8 @@ module WebAuthn
|
|
47
47
|
attested_credential_data_aaguid == WebAuthn::AuthenticatorData::AttestedCredentialData::ZEROED_AAGUID
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
51
|
-
|
52
|
-
.new(VALID_ATTESTATION_CERTIFICATE_ALGORITHM, certificate_public_key)
|
53
|
-
.verify(signature, verification_data(authenticator_data, client_data_hash))
|
50
|
+
def algorithm
|
51
|
+
VALID_ATTESTATION_CERTIFICATE_ALGORITHM.id
|
54
52
|
end
|
55
53
|
|
56
54
|
def verification_data(authenticator_data, client_data_hash)
|
@@ -64,6 +62,10 @@ module WebAuthn
|
|
64
62
|
def public_key_u2f(cose_key_data)
|
65
63
|
PublicKey.new(cose_key_data)
|
66
64
|
end
|
65
|
+
|
66
|
+
def attestation_type
|
67
|
+
WebAuthn::AttestationStatement::ATTESTATION_TYPE_BASIC_OR_ATTCA
|
68
|
+
end
|
67
69
|
end
|
68
70
|
end
|
69
71
|
end
|
@@ -6,12 +6,18 @@ module WebAuthn
|
|
6
6
|
module AttestationStatement
|
7
7
|
class None < Base
|
8
8
|
def valid?(*_args)
|
9
|
-
if statement == {}
|
9
|
+
if statement == {} && trustworthy?
|
10
10
|
[WebAuthn::AttestationStatement::ATTESTATION_TYPE_NONE, nil]
|
11
11
|
else
|
12
12
|
false
|
13
13
|
end
|
14
14
|
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def attestation_type
|
19
|
+
WebAuthn::AttestationStatement::ATTESTATION_TYPE_NONE
|
20
|
+
end
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
@@ -2,25 +2,21 @@
|
|
2
2
|
|
3
3
|
require "openssl"
|
4
4
|
require "webauthn/attestation_statement/base"
|
5
|
-
require "webauthn/signature_verifier"
|
6
5
|
|
7
6
|
module WebAuthn
|
8
7
|
# Implements https://www.w3.org/TR/2018/CR-webauthn-20180807/#packed-attestation
|
9
|
-
# ECDAA attestation is unsupported.
|
10
8
|
module AttestationStatement
|
11
9
|
class Packed < Base
|
12
10
|
# Follows "Verification procedure"
|
13
11
|
def valid?(authenticator_data, client_data_hash)
|
14
|
-
check_unsupported_feature
|
15
|
-
|
16
12
|
valid_format? &&
|
17
13
|
valid_algorithm?(authenticator_data.credential) &&
|
18
|
-
valid_certificate_chain? &&
|
19
14
|
valid_ec_public_keys?(authenticator_data.credential) &&
|
20
15
|
meet_certificate_requirement? &&
|
21
16
|
matching_aaguid?(authenticator_data.attested_credential_data.raw_aaguid) &&
|
22
17
|
valid_signature?(authenticator_data, client_data_hash) &&
|
23
|
-
|
18
|
+
trustworthy?(aaguid: authenticator_data.aaguid) &&
|
19
|
+
[attestation_type, attestation_trust_path]
|
24
20
|
end
|
25
21
|
|
26
22
|
private
|
@@ -30,27 +26,11 @@ module WebAuthn
|
|
30
26
|
end
|
31
27
|
|
32
28
|
def self_attestation?
|
33
|
-
!raw_certificates
|
29
|
+
!raw_certificates
|
34
30
|
end
|
35
31
|
|
36
32
|
def valid_format?
|
37
|
-
algorithm && signature
|
38
|
-
[raw_certificates, raw_ecdaa_key_id].compact.size < 2
|
39
|
-
)
|
40
|
-
end
|
41
|
-
|
42
|
-
def check_unsupported_feature
|
43
|
-
if raw_ecdaa_key_id
|
44
|
-
raise NotSupportedError, "ecdaaKeyId of the packed attestation format is not implemented yet"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def valid_certificate_chain?
|
49
|
-
if certificate_chain
|
50
|
-
certificate_chain.all? { |c| certificate_in_use?(c) }
|
51
|
-
else
|
52
|
-
true
|
53
|
-
end
|
33
|
+
algorithm && signature
|
54
34
|
end
|
55
35
|
|
56
36
|
def valid_ec_public_keys?(credential)
|
@@ -65,7 +45,6 @@ module WebAuthn
|
|
65
45
|
subject = attestation_certificate.subject.to_a
|
66
46
|
|
67
47
|
attestation_certificate.version == 2 &&
|
68
|
-
certificate_in_use?(attestation_certificate) &&
|
69
48
|
subject.assoc('OU')&.at(1) == "Authenticator Attestation" &&
|
70
49
|
attestation_certificate.extensions.find { |ext| ext.oid == 'basicConstraints' }&.value == 'CA:FALSE'
|
71
50
|
else
|
@@ -73,27 +52,20 @@ module WebAuthn
|
|
73
52
|
end
|
74
53
|
end
|
75
54
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
55
|
+
def attestation_type
|
56
|
+
if attestation_trust_path
|
57
|
+
WebAuthn::AttestationStatement::ATTESTATION_TYPE_BASIC_OR_ATTCA # FIXME: use metadata if available
|
58
|
+
else
|
59
|
+
WebAuthn::AttestationStatement::ATTESTATION_TYPE_SELF
|
60
|
+
end
|
80
61
|
end
|
81
62
|
|
82
63
|
def valid_signature?(authenticator_data, client_data_hash)
|
83
|
-
|
84
|
-
|
64
|
+
super(
|
65
|
+
authenticator_data,
|
66
|
+
client_data_hash,
|
85
67
|
attestation_certificate&.public_key || authenticator_data.credential.public_key_object
|
86
68
|
)
|
87
|
-
|
88
|
-
signature_verifier.verify(signature, authenticator_data.data + client_data_hash)
|
89
|
-
end
|
90
|
-
|
91
|
-
def attestation_type_and_trust_path
|
92
|
-
if attestation_trust_path
|
93
|
-
[WebAuthn::AttestationStatement::ATTESTATION_TYPE_BASIC_OR_ATTCA, attestation_trust_path]
|
94
|
-
else
|
95
|
-
[WebAuthn::AttestationStatement::ATTESTATION_TYPE_SELF, nil]
|
96
|
-
end
|
97
69
|
end
|
98
70
|
end
|
99
71
|
end
|
@@ -2,98 +2,63 @@
|
|
2
2
|
|
3
3
|
require "cose/algorithm"
|
4
4
|
require "openssl"
|
5
|
-
require "tpm/
|
5
|
+
require "tpm/key_attestation"
|
6
6
|
require "webauthn/attestation_statement/base"
|
7
|
-
require "webauthn/attestation_statement/tpm/cert_info"
|
8
|
-
require "webauthn/attestation_statement/tpm/pub_area"
|
9
|
-
require "webauthn/signature_verifier"
|
10
7
|
|
11
8
|
module WebAuthn
|
12
9
|
module AttestationStatement
|
13
10
|
class TPM < Base
|
14
|
-
CERTIFICATE_V3 = 2
|
15
|
-
CERTIFICATE_EMPTY_NAME = OpenSSL::X509::Name.new([]).freeze
|
16
|
-
CERTIFICATE_SAN_DIRECTORY_NAME = 4
|
17
|
-
OID_TCG_AT_TPM_MANUFACTURER = "2.23.133.2.1"
|
18
|
-
OID_TCG_AT_TPM_MODEL = "2.23.133.2.2"
|
19
|
-
OID_TCG_AT_TPM_VERSION = "2.23.133.2.3"
|
20
|
-
OID_TCG_KP_AIK_CERTIFICATE = "2.23.133.8.3"
|
21
11
|
TPM_V2 = "2.0"
|
22
12
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
13
|
+
COSE_ALG_TO_TPM = {
|
14
|
+
"RS1" => { signature: ::TPM::ALG_RSASSA, hash: ::TPM::ALG_SHA1 },
|
15
|
+
"RS256" => { signature: ::TPM::ALG_RSASSA, hash: ::TPM::ALG_SHA256 },
|
16
|
+
"PS256" => { signature: ::TPM::ALG_RSAPSS, hash: ::TPM::ALG_SHA256 },
|
17
|
+
"ES256" => { signature: ::TPM::ALG_ECDSA, hash: ::TPM::ALG_SHA256 },
|
18
|
+
}.freeze
|
27
19
|
|
20
|
+
def valid?(authenticator_data, client_data_hash)
|
21
|
+
attestation_type == ATTESTATION_TYPE_ATTCA &&
|
28
22
|
ver == TPM_V2 &&
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
raise(
|
38
|
-
WebAuthn::AttestationStatement::Base::NotSupportedError,
|
39
|
-
"Attestation type ECDAA is not supported"
|
40
|
-
)
|
41
|
-
end
|
23
|
+
valid_key_attestation?(
|
24
|
+
authenticator_data.data + client_data_hash,
|
25
|
+
authenticator_data.credential.public_key_object,
|
26
|
+
authenticator_data.aaguid
|
27
|
+
) &&
|
28
|
+
matching_aaguid?(authenticator_data.attested_credential_data.raw_aaguid) &&
|
29
|
+
trustworthy?(aaguid: authenticator_data.aaguid) &&
|
30
|
+
[attestation_type, attestation_trust_path]
|
42
31
|
end
|
43
32
|
|
44
33
|
private
|
45
34
|
|
46
|
-
def
|
47
|
-
|
48
|
-
.new(
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
certificate_in_use?(attestation_certificate) &&
|
59
|
-
extensions.find { |ext| ext.oid == 'basicConstraints' }&.value == "CA:FALSE" &&
|
60
|
-
extensions.find { |ext| ext.oid == "extendedKeyUsage" }&.value == OID_TCG_KP_AIK_CERTIFICATE
|
61
|
-
end
|
62
|
-
|
63
|
-
def valid_subject_alternative_name?
|
64
|
-
extension = attestation_certificate.extensions.detect { |ext| ext.oid == "subjectAltName" }
|
65
|
-
return unless extension&.critical?
|
66
|
-
|
67
|
-
san_asn1 = OpenSSL::ASN1.decode(extension).find do |val|
|
68
|
-
val.tag_class == :UNIVERSAL && val.tag == OpenSSL::ASN1::OCTET_STRING
|
69
|
-
end
|
70
|
-
directory_name = OpenSSL::ASN1.decode(san_asn1.value).find do |val|
|
71
|
-
val.tag_class == :CONTEXT_SPECIFIC && val.tag == CERTIFICATE_SAN_DIRECTORY_NAME
|
72
|
-
end
|
73
|
-
name = OpenSSL::X509::Name.new(directory_name.value.first).to_a
|
74
|
-
manufacturer = name.assoc(OID_TCG_AT_TPM_MANUFACTURER).at(1)
|
75
|
-
model = name.assoc(OID_TCG_AT_TPM_MODEL).at(1)
|
76
|
-
version = name.assoc(OID_TCG_AT_TPM_VERSION).at(1)
|
77
|
-
|
78
|
-
::TPM::VENDOR_IDS[manufacturer] && !model.empty? && !version.empty?
|
79
|
-
end
|
80
|
-
|
81
|
-
def certificate_in_use?(certificate)
|
82
|
-
now = Time.now
|
35
|
+
def valid_key_attestation?(certified_extra_data, key, aaguid)
|
36
|
+
key_attestation =
|
37
|
+
::TPM::KeyAttestation.new(
|
38
|
+
statement["certInfo"],
|
39
|
+
signature,
|
40
|
+
statement["pubArea"],
|
41
|
+
certificates,
|
42
|
+
OpenSSL::Digest.digest(cose_algorithm.hash_function, certified_extra_data),
|
43
|
+
signature_algorithm: tpm_algorithm[:signature],
|
44
|
+
hash_algorithm: tpm_algorithm[:hash],
|
45
|
+
root_certificates: root_certificates(aaguid: aaguid)
|
46
|
+
)
|
83
47
|
|
84
|
-
|
48
|
+
key_attestation.valid? && key_attestation.key && key_attestation.key.to_pem == key.to_pem
|
85
49
|
end
|
86
50
|
|
87
|
-
def
|
88
|
-
|
51
|
+
def valid_certificate_chain?(**_)
|
52
|
+
# Already performed as part of #valid_key_attestation?
|
53
|
+
true
|
89
54
|
end
|
90
55
|
|
91
|
-
def
|
92
|
-
|
56
|
+
def default_root_certificates
|
57
|
+
::TPM::KeyAttestation::ROOT_CERTIFICATES
|
93
58
|
end
|
94
59
|
|
95
|
-
def
|
96
|
-
|
60
|
+
def tpm_algorithm
|
61
|
+
COSE_ALG_TO_TPM[cose_algorithm.name] || raise("Unsupported algorithm #{cose_algorithm.name}")
|
97
62
|
end
|
98
63
|
|
99
64
|
def ver
|
@@ -105,10 +70,8 @@ module WebAuthn
|
|
105
70
|
end
|
106
71
|
|
107
72
|
def attestation_type
|
108
|
-
if raw_certificates
|
73
|
+
if raw_certificates
|
109
74
|
ATTESTATION_TYPE_ATTCA
|
110
|
-
elsif raw_ecdaa_key_id && !raw_certificates
|
111
|
-
ATTESTATION_TYPE_ECDAA
|
112
75
|
else
|
113
76
|
raise "Attestation type invalid"
|
114
77
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require "webauthn/authenticator_data"
|
4
4
|
require "webauthn/authenticator_response"
|
5
5
|
require "webauthn/encoder"
|
6
|
-
require "webauthn/signature_verifier"
|
7
6
|
require "webauthn/public_key"
|
8
7
|
|
9
8
|
module WebAuthn
|
@@ -37,8 +36,7 @@ module WebAuthn
|
|
37
36
|
@user_handle = user_handle
|
38
37
|
end
|
39
38
|
|
40
|
-
def verify(expected_challenge, expected_origin = nil, public_key:, sign_count:, user_verification: nil,
|
41
|
-
rp_id: nil)
|
39
|
+
def verify(expected_challenge, expected_origin = nil, public_key:, sign_count:, user_verification: nil, rp_id: nil)
|
42
40
|
super(expected_challenge, expected_origin, user_verification: user_verification, rp_id: rp_id)
|
43
41
|
verify_item(:signature, WebAuthn::PublicKey.deserialize(public_key))
|
44
42
|
verify_item(:sign_count, sign_count)
|
@@ -47,7 +45,7 @@ module WebAuthn
|
|
47
45
|
end
|
48
46
|
|
49
47
|
def authenticator_data
|
50
|
-
@authenticator_data ||= WebAuthn::AuthenticatorData.
|
48
|
+
@authenticator_data ||= WebAuthn::AuthenticatorData.deserialize(authenticator_data_bytes)
|
51
49
|
end
|
52
50
|
|
53
51
|
private
|
@@ -55,9 +53,7 @@ module WebAuthn
|
|
55
53
|
attr_reader :authenticator_data_bytes, :signature
|
56
54
|
|
57
55
|
def valid_signature?(webauthn_public_key)
|
58
|
-
|
59
|
-
.new(webauthn_public_key.alg, webauthn_public_key.pkey)
|
60
|
-
.verify(signature, authenticator_data_bytes + client_data.hash)
|
56
|
+
webauthn_public_key.verify(signature, authenticator_data_bytes + client_data.hash)
|
61
57
|
end
|
62
58
|
|
63
59
|
def valid_sign_count?(stored_sign_count)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "cbor"
|
4
|
+
require "forwardable"
|
4
5
|
require "uri"
|
5
6
|
require "openssl"
|
6
7
|
|
7
|
-
require "webauthn/
|
8
|
+
require "webauthn/attestation_object"
|
8
9
|
require "webauthn/authenticator_response"
|
9
|
-
require "webauthn/attestation_statement"
|
10
10
|
require "webauthn/client_data"
|
11
11
|
require "webauthn/encoder"
|
12
12
|
|
@@ -16,6 +16,8 @@ module WebAuthn
|
|
16
16
|
class AttestedCredentialVerificationError < VerificationError; end
|
17
17
|
|
18
18
|
class AuthenticatorAttestationResponse < AuthenticatorResponse
|
19
|
+
extend Forwardable
|
20
|
+
|
19
21
|
def self.from_client(response)
|
20
22
|
encoder = WebAuthn.configuration.encoder
|
21
23
|
|
@@ -30,7 +32,7 @@ module WebAuthn
|
|
30
32
|
def initialize(attestation_object:, **options)
|
31
33
|
super(**options)
|
32
34
|
|
33
|
-
@
|
35
|
+
@attestation_object_bytes = attestation_object
|
34
36
|
end
|
35
37
|
|
36
38
|
def verify(expected_challenge, expected_origin = nil, user_verification: nil, rp_id: nil)
|
@@ -39,107 +41,40 @@ module WebAuthn
|
|
39
41
|
verify_item(:attested_credential)
|
40
42
|
if WebAuthn.configuration.verify_attestation_statement
|
41
43
|
verify_item(:attestation_statement)
|
42
|
-
verify_item(:attestation_trustworthiness) if WebAuthn.configuration.attestation_root_certificates_finders.any?
|
43
44
|
end
|
44
45
|
|
45
46
|
true
|
46
47
|
end
|
47
48
|
|
48
|
-
def
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
def attestation_statement
|
53
|
-
@attestation_statement ||=
|
54
|
-
WebAuthn::AttestationStatement.from(attestation["fmt"], attestation["attStmt"])
|
49
|
+
def attestation_object
|
50
|
+
@attestation_object ||= WebAuthn::AttestationObject.deserialize(attestation_object_bytes)
|
55
51
|
end
|
56
52
|
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
def_delegators(
|
54
|
+
:attestation_object,
|
55
|
+
:aaguid,
|
56
|
+
:attestation_statement,
|
57
|
+
:attestation_certificate_key_id,
|
58
|
+
:authenticator_data,
|
59
|
+
:credential
|
60
|
+
)
|
60
61
|
|
61
|
-
|
62
|
-
attestation["fmt"]
|
63
|
-
end
|
64
|
-
|
65
|
-
def attestation
|
66
|
-
@attestation ||= CBOR.decode(attestation_object)
|
67
|
-
end
|
68
|
-
|
69
|
-
def aaguid
|
70
|
-
raw_aaguid = authenticator_data.attested_credential_data.raw_aaguid
|
71
|
-
unless raw_aaguid == WebAuthn::AuthenticatorData::AttestedCredentialData::ZEROED_AAGUID
|
72
|
-
authenticator_data.attested_credential_data.aaguid
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def attestation_certificate_key
|
77
|
-
raw_subject_key_identifier(attestation_statement.attestation_certificate)&.unpack("H*")&.[](0)
|
78
|
-
end
|
62
|
+
alias_method :attestation_certificate_key, :attestation_certificate_key_id
|
79
63
|
|
80
64
|
private
|
81
65
|
|
82
|
-
attr_reader :
|
66
|
+
attr_reader :attestation_object_bytes
|
83
67
|
|
84
68
|
def type
|
85
69
|
WebAuthn::TYPES[:create]
|
86
70
|
end
|
87
71
|
|
88
72
|
def valid_attested_credential?
|
89
|
-
|
90
|
-
authenticator_data.attested_credential_data.valid?
|
73
|
+
attestation_object.valid_attested_credential?
|
91
74
|
end
|
92
75
|
|
93
76
|
def valid_attestation_statement?
|
94
|
-
@attestation_type, @attestation_trust_path =
|
95
|
-
end
|
96
|
-
|
97
|
-
def valid_attestation_trustworthiness?
|
98
|
-
case @attestation_type
|
99
|
-
when WebAuthn::AttestationStatement::ATTESTATION_TYPE_NONE
|
100
|
-
WebAuthn.configuration.acceptable_attestation_types.include?('None')
|
101
|
-
when WebAuthn::AttestationStatement::ATTESTATION_TYPE_SELF
|
102
|
-
WebAuthn.configuration.acceptable_attestation_types.include?('Self')
|
103
|
-
else
|
104
|
-
WebAuthn.configuration.acceptable_attestation_types.include?(@attestation_type) &&
|
105
|
-
attestation_root_certificates_store.verify(leaf_certificate, signing_certificates)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def raw_subject_key_identifier(certificate)
|
110
|
-
extension = certificate.extensions.detect { |ext| ext.oid == "subjectKeyIdentifier" }
|
111
|
-
return unless extension
|
112
|
-
|
113
|
-
ext_asn1 = OpenSSL::ASN1.decode(extension.to_der)
|
114
|
-
ext_value = ext_asn1.value.last
|
115
|
-
OpenSSL::ASN1.decode(ext_value.value).value
|
116
|
-
end
|
117
|
-
|
118
|
-
def attestation_root_certificates_store
|
119
|
-
certificates =
|
120
|
-
WebAuthn.configuration.attestation_root_certificates_finders.reduce([]) do |certs, finder|
|
121
|
-
if certs.empty?
|
122
|
-
finder.find(attestation_format: attestation_format,
|
123
|
-
aaguid: aaguid,
|
124
|
-
attestation_certificate_key_id: attestation_certificate_key) || []
|
125
|
-
else
|
126
|
-
certs
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
OpenSSL::X509::Store.new.tap do |store|
|
131
|
-
certificates.each do |cert|
|
132
|
-
store.add_cert(cert)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def signing_certificates
|
138
|
-
@attestation_trust_path[1..-1]
|
139
|
-
end
|
140
|
-
|
141
|
-
def leaf_certificate
|
142
|
-
@attestation_trust_path.first
|
77
|
+
@attestation_type, @attestation_trust_path = attestation_object.valid_attestation_statement?(client_data.hash)
|
143
78
|
end
|
144
79
|
end
|
145
80
|
end
|