webauthn 2.0.0.beta1 → 2.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 +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +65 -13
- data/.travis.yml +22 -18
- data/Appraisals +4 -0
- data/CHANGELOG.md +72 -25
- data/CONTRIBUTING.md +0 -5
- data/README.md +172 -15
- data/SECURITY.md +4 -4
- data/gemfiles/openssl_2_2.gemfile +7 -0
- data/lib/cose/rsapkcs1_algorithm.rb +43 -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 +30 -20
- data/lib/webauthn/attestation_statement/base.rb +124 -14
- data/lib/webauthn/attestation_statement/fido_u2f.rb +13 -9
- data/lib/webauthn/attestation_statement/packed.rb +14 -42
- data/lib/webauthn/attestation_statement/tpm.rb +38 -54
- data/lib/webauthn/authenticator_assertion_response.rb +7 -36
- data/lib/webauthn/authenticator_attestation_response.rb +24 -46
- 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 +15 -10
- data/lib/webauthn/configuration.rb +23 -0
- data/lib/webauthn/credential.rb +4 -4
- data/lib/webauthn/credential_creation_options.rb +1 -1
- 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 +17 -4
- data/lib/webauthn/public_key.rb +68 -0
- data/lib/webauthn/public_key_credential.rb +13 -3
- data/lib/webauthn/public_key_credential/creation_options.rb +2 -2
- 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 +14 -9
- metadata +70 -42
- data/lib/android_safetynet/attestation_response.rb +0 -84
- data/lib/cose/algorithm.rb +0 -38
- data/lib/tpm/constants.rb +0 -22
- 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 -65
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "cose"
|
3
4
|
require "openssl"
|
4
5
|
require "webauthn/attestation_statement/base"
|
5
6
|
require "webauthn/attestation_statement/fido_u2f/public_key"
|
6
|
-
require "webauthn/signature_verifier"
|
7
7
|
|
8
8
|
module WebAuthn
|
9
9
|
module AttestationStatement
|
10
10
|
class FidoU2f < Base
|
11
11
|
VALID_ATTESTATION_CERTIFICATE_COUNT = 1
|
12
12
|
VALID_ATTESTATION_CERTIFICATE_ALGORITHM = COSE::Algorithm.by_name("ES256")
|
13
|
+
VALID_ATTESTATION_CERTIFICATE_KEY_CURVE = COSE::Key::Curve.by_name("P-256")
|
13
14
|
|
14
15
|
def valid?(authenticator_data, client_data_hash)
|
15
16
|
valid_format? &&
|
@@ -17,19 +18,20 @@ module WebAuthn
|
|
17
18
|
valid_credential_public_key?(authenticator_data.credential.public_key) &&
|
18
19
|
valid_aaguid?(authenticator_data.attested_credential_data.raw_aaguid) &&
|
19
20
|
valid_signature?(authenticator_data, client_data_hash) &&
|
20
|
-
|
21
|
+
trustworthy?(attestation_certificate_key_id: attestation_certificate_key_id) &&
|
22
|
+
[attestation_type, attestation_trust_path]
|
21
23
|
end
|
22
24
|
|
23
25
|
private
|
24
26
|
|
25
27
|
def valid_format?
|
26
|
-
!!(
|
27
|
-
|
28
|
+
!!(raw_certificates && signature) &&
|
29
|
+
raw_certificates.length == VALID_ATTESTATION_CERTIFICATE_COUNT
|
28
30
|
end
|
29
31
|
|
30
32
|
def valid_certificate_public_key?
|
31
33
|
certificate_public_key.is_a?(OpenSSL::PKey::EC) &&
|
32
|
-
certificate_public_key.group.curve_name ==
|
34
|
+
certificate_public_key.group.curve_name == VALID_ATTESTATION_CERTIFICATE_KEY_CURVE.pkey_name &&
|
33
35
|
certificate_public_key.check_key
|
34
36
|
end
|
35
37
|
|
@@ -45,10 +47,8 @@ module WebAuthn
|
|
45
47
|
attested_credential_data_aaguid == WebAuthn::AuthenticatorData::AttestedCredentialData::ZEROED_AAGUID
|
46
48
|
end
|
47
49
|
|
48
|
-
def
|
49
|
-
|
50
|
-
.new(VALID_ATTESTATION_CERTIFICATE_ALGORITHM, certificate_public_key)
|
51
|
-
.verify(signature, verification_data(authenticator_data, client_data_hash))
|
50
|
+
def algorithm
|
51
|
+
VALID_ATTESTATION_CERTIFICATE_ALGORITHM.id
|
52
52
|
end
|
53
53
|
|
54
54
|
def verification_data(authenticator_data, client_data_hash)
|
@@ -62,6 +62,10 @@ module WebAuthn
|
|
62
62
|
def public_key_u2f(cose_key_data)
|
63
63
|
PublicKey.new(cose_key_data)
|
64
64
|
end
|
65
|
+
|
66
|
+
def attestation_type
|
67
|
+
WebAuthn::AttestationStatement::ATTESTATION_TYPE_BASIC_OR_ATTCA
|
68
|
+
end
|
65
69
|
end
|
66
70
|
end
|
67
71
|
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,31 +26,15 @@ module WebAuthn
|
|
30
26
|
end
|
31
27
|
|
32
28
|
def self_attestation?
|
33
|
-
!
|
29
|
+
!raw_certificates
|
34
30
|
end
|
35
31
|
|
36
32
|
def valid_format?
|
37
|
-
algorithm && signature
|
38
|
-
[raw_attestation_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 attestation_certificate_chain
|
50
|
-
attestation_certificate_chain[1..-1].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)
|
57
|
-
(
|
37
|
+
(certificates&.map(&:public_key) || [credential.public_key_object])
|
58
38
|
.select { |pkey| pkey.is_a?(OpenSSL::PKey::EC) }
|
59
39
|
.all? { |pkey| pkey.check_key }
|
60
40
|
end
|
@@ -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 raw_attestation_certificates&.any?
|
93
|
-
[WebAuthn::AttestationStatement::ATTESTATION_TYPE_BASIC_OR_ATTCA, attestation_certificate_chain]
|
94
|
-
else
|
95
|
-
[WebAuthn::AttestationStatement::ATTESTATION_TYPE_SELF, nil]
|
96
|
-
end
|
97
69
|
end
|
98
70
|
end
|
99
71
|
end
|
@@ -2,73 +2,63 @@
|
|
2
2
|
|
3
3
|
require "cose/algorithm"
|
4
4
|
require "openssl"
|
5
|
+
require "tpm/key_attestation"
|
5
6
|
require "webauthn/attestation_statement/base"
|
6
|
-
require "webauthn/attestation_statement/tpm/cert_info"
|
7
|
-
require "webauthn/attestation_statement/tpm/pub_area"
|
8
|
-
require "webauthn/signature_verifier"
|
9
7
|
|
10
8
|
module WebAuthn
|
11
9
|
module AttestationStatement
|
12
10
|
class TPM < Base
|
13
|
-
CERTIFICATE_V3 = 2
|
14
|
-
CERTIFICATE_EMPTY_NAME = OpenSSL::X509::Name.new([]).freeze
|
15
|
-
OID_TCG_KP_AIK_CERTIFICATE = "2.23.133.8.3"
|
16
11
|
TPM_V2 = "2.0"
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
22
19
|
|
20
|
+
def valid?(authenticator_data, client_data_hash)
|
21
|
+
attestation_type == ATTESTATION_TYPE_ATTCA &&
|
23
22
|
ver == TPM_V2 &&
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
WebAuthn::AttestationStatement::Base::NotSupportedError,
|
33
|
-
"Attestation type ECDAA is not supported"
|
34
|
-
)
|
35
|
-
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]
|
36
31
|
end
|
37
32
|
|
38
33
|
private
|
39
34
|
|
40
|
-
def
|
41
|
-
|
42
|
-
.new(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
extensions.find { |ext| ext.oid == 'basicConstraints' }&.value == "CA:FALSE" &&
|
53
|
-
extensions.find { |ext| ext.oid == "extendedKeyUsage" }&.value == OID_TCG_KP_AIK_CERTIFICATE
|
54
|
-
end
|
55
|
-
|
56
|
-
def certificate_in_use?(certificate)
|
57
|
-
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
|
+
)
|
58
47
|
|
59
|
-
|
48
|
+
key_attestation.valid? && key_attestation.key && key_attestation.key.to_pem == key.to_pem
|
60
49
|
end
|
61
50
|
|
62
|
-
def
|
63
|
-
|
51
|
+
def valid_certificate_chain?(**_)
|
52
|
+
# Already performed as part of #valid_key_attestation?
|
53
|
+
true
|
64
54
|
end
|
65
55
|
|
66
|
-
def
|
67
|
-
|
56
|
+
def default_root_certificates
|
57
|
+
::TPM::KeyAttestation::ROOT_CERTIFICATES
|
68
58
|
end
|
69
59
|
|
70
|
-
def
|
71
|
-
|
60
|
+
def tpm_algorithm
|
61
|
+
COSE_ALG_TO_TPM[cose_algorithm.name] || raise("Unsupported algorithm #{cose_algorithm.name}")
|
72
62
|
end
|
73
63
|
|
74
64
|
def ver
|
@@ -80,18 +70,12 @@ module WebAuthn
|
|
80
70
|
end
|
81
71
|
|
82
72
|
def attestation_type
|
83
|
-
if
|
73
|
+
if raw_certificates
|
84
74
|
ATTESTATION_TYPE_ATTCA
|
85
|
-
elsif raw_ecdaa_key_id && !raw_attestation_certificates
|
86
|
-
ATTESTATION_TYPE_ECDAA
|
87
75
|
else
|
88
76
|
raise "Attestation type invalid"
|
89
77
|
end
|
90
78
|
end
|
91
|
-
|
92
|
-
def attestation_trust_path
|
93
|
-
attestation_certificate_chain
|
94
|
-
end
|
95
79
|
end
|
96
80
|
end
|
97
81
|
end
|
@@ -1,12 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "cose/algorithm"
|
4
|
-
require "cose/key"
|
5
|
-
require "webauthn/attestation_statement/fido_u2f/public_key"
|
6
3
|
require "webauthn/authenticator_data"
|
7
4
|
require "webauthn/authenticator_response"
|
8
5
|
require "webauthn/encoder"
|
9
|
-
require "webauthn/
|
6
|
+
require "webauthn/public_key"
|
10
7
|
|
11
8
|
module WebAuthn
|
12
9
|
class SignatureVerificationError < VerificationError; end
|
@@ -32,34 +29,31 @@ module WebAuthn
|
|
32
29
|
attr_reader :user_handle
|
33
30
|
|
34
31
|
def initialize(authenticator_data:, signature:, user_handle: nil, **options)
|
35
|
-
super(options)
|
32
|
+
super(**options)
|
36
33
|
|
37
34
|
@authenticator_data_bytes = authenticator_data
|
38
35
|
@signature = signature
|
39
36
|
@user_handle = user_handle
|
40
37
|
end
|
41
38
|
|
42
|
-
def verify(expected_challenge, expected_origin = nil, public_key:, sign_count:, user_verification: nil,
|
43
|
-
rp_id: nil)
|
39
|
+
def verify(expected_challenge, expected_origin = nil, public_key:, sign_count:, user_verification: nil, rp_id: nil)
|
44
40
|
super(expected_challenge, expected_origin, user_verification: user_verification, rp_id: rp_id)
|
45
|
-
verify_item(:signature,
|
41
|
+
verify_item(:signature, WebAuthn::PublicKey.deserialize(public_key))
|
46
42
|
verify_item(:sign_count, sign_count)
|
47
43
|
|
48
44
|
true
|
49
45
|
end
|
50
46
|
|
51
47
|
def authenticator_data
|
52
|
-
@authenticator_data ||= WebAuthn::AuthenticatorData.
|
48
|
+
@authenticator_data ||= WebAuthn::AuthenticatorData.deserialize(authenticator_data_bytes)
|
53
49
|
end
|
54
50
|
|
55
51
|
private
|
56
52
|
|
57
53
|
attr_reader :authenticator_data_bytes, :signature
|
58
54
|
|
59
|
-
def valid_signature?(
|
60
|
-
|
61
|
-
.new(credential_cose_key.alg, credential_cose_key.to_pkey)
|
62
|
-
.verify(signature, authenticator_data_bytes + client_data.hash)
|
55
|
+
def valid_signature?(webauthn_public_key)
|
56
|
+
webauthn_public_key.verify(signature, authenticator_data_bytes + client_data.hash)
|
63
57
|
end
|
64
58
|
|
65
59
|
def valid_sign_count?(stored_sign_count)
|
@@ -71,29 +65,6 @@ module WebAuthn
|
|
71
65
|
end
|
72
66
|
end
|
73
67
|
|
74
|
-
def credential_cose_key(public_key)
|
75
|
-
if WebAuthn::AttestationStatement::FidoU2f::PublicKey.uncompressed_point?(public_key)
|
76
|
-
# Gem version v1.11.0 and lower, used to behave so that Credential#public_key
|
77
|
-
# returned an EC P-256 uncompressed point.
|
78
|
-
#
|
79
|
-
# Because of https://github.com/cedarcode/webauthn-ruby/issues/137 this was changed
|
80
|
-
# and Credential#public_key started returning the unchanged COSE_Key formatted
|
81
|
-
# credentialPublicKey (as in https://www.w3.org/TR/webauthn/#credentialpublickey).
|
82
|
-
#
|
83
|
-
# Given that the credential public key is expected to be stored long-term by the gem
|
84
|
-
# user and later be passed as the public_key argument in the
|
85
|
-
# AuthenticatorAssertionResponse.verify call, we then need to support the two formats.
|
86
|
-
COSE::Key::EC2.new(
|
87
|
-
alg: COSE::Algorithm.by_name("ES256").id,
|
88
|
-
crv: 1,
|
89
|
-
x: public_key[1..32],
|
90
|
-
y: public_key[33..-1]
|
91
|
-
)
|
92
|
-
else
|
93
|
-
COSE::Key.deserialize(public_key)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
68
|
def type
|
98
69
|
WebAuthn::TYPES[:get]
|
99
70
|
end
|
@@ -1,20 +1,23 @@
|
|
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
|
|
13
13
|
module WebAuthn
|
14
14
|
class AttestationStatementVerificationError < VerificationError; end
|
15
|
+
class AttestationTrustworthinessVerificationError < VerificationError; end
|
15
16
|
class AttestedCredentialVerificationError < VerificationError; end
|
16
17
|
|
17
18
|
class AuthenticatorAttestationResponse < AuthenticatorResponse
|
19
|
+
extend Forwardable
|
20
|
+
|
18
21
|
def self.from_client(response)
|
19
22
|
encoder = WebAuthn.configuration.encoder
|
20
23
|
|
@@ -27,76 +30,51 @@ module WebAuthn
|
|
27
30
|
attr_reader :attestation_type, :attestation_trust_path
|
28
31
|
|
29
32
|
def initialize(attestation_object:, **options)
|
30
|
-
super(options)
|
33
|
+
super(**options)
|
31
34
|
|
32
|
-
@
|
35
|
+
@attestation_object_bytes = attestation_object
|
33
36
|
end
|
34
37
|
|
35
38
|
def verify(expected_challenge, expected_origin = nil, user_verification: nil, rp_id: nil)
|
36
39
|
super
|
37
40
|
|
38
41
|
verify_item(:attested_credential)
|
39
|
-
|
42
|
+
if WebAuthn.configuration.verify_attestation_statement
|
43
|
+
verify_item(:attestation_statement)
|
44
|
+
end
|
40
45
|
|
41
46
|
true
|
42
47
|
end
|
43
48
|
|
44
|
-
def
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
def attestation_statement
|
49
|
-
@attestation_statement ||=
|
50
|
-
WebAuthn::AttestationStatement.from(attestation["fmt"], attestation["attStmt"])
|
51
|
-
end
|
52
|
-
|
53
|
-
def authenticator_data
|
54
|
-
@authenticator_data ||= WebAuthn::AuthenticatorData.new(attestation["authData"])
|
55
|
-
end
|
56
|
-
|
57
|
-
def attestation_format
|
58
|
-
attestation["fmt"]
|
49
|
+
def attestation_object
|
50
|
+
@attestation_object ||= WebAuthn::AttestationObject.deserialize(attestation_object_bytes)
|
59
51
|
end
|
60
52
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
end
|
53
|
+
def_delegators(
|
54
|
+
:attestation_object,
|
55
|
+
:aaguid,
|
56
|
+
:attestation_statement,
|
57
|
+
:attestation_certificate_key_id,
|
58
|
+
:authenticator_data,
|
59
|
+
:credential
|
60
|
+
)
|
71
61
|
|
72
|
-
|
73
|
-
raw_subject_key_identifier(attestation_statement.attestation_certificate)&.unpack("H*")&.[](0)
|
74
|
-
end
|
62
|
+
alias_method :attestation_certificate_key, :attestation_certificate_key_id
|
75
63
|
|
76
64
|
private
|
77
65
|
|
78
|
-
attr_reader :
|
66
|
+
attr_reader :attestation_object_bytes
|
79
67
|
|
80
68
|
def type
|
81
69
|
WebAuthn::TYPES[:create]
|
82
70
|
end
|
83
71
|
|
84
72
|
def valid_attested_credential?
|
85
|
-
|
86
|
-
authenticator_data.attested_credential_data.valid?
|
73
|
+
attestation_object.valid_attested_credential?
|
87
74
|
end
|
88
75
|
|
89
76
|
def valid_attestation_statement?
|
90
|
-
@attestation_type, @attestation_trust_path =
|
91
|
-
end
|
92
|
-
|
93
|
-
def raw_subject_key_identifier(certificate)
|
94
|
-
extension = certificate.extensions.detect { |ext| ext.oid == "subjectKeyIdentifier" }
|
95
|
-
return unless extension
|
96
|
-
|
97
|
-
ext_asn1 = OpenSSL::ASN1.decode(extension.to_der)
|
98
|
-
ext_value = ext_asn1.value.last
|
99
|
-
OpenSSL::ASN1.decode(ext_value.value).value
|
77
|
+
@attestation_type, @attestation_trust_path = attestation_object.valid_attestation_statement?(client_data.hash)
|
100
78
|
end
|
101
79
|
end
|
102
80
|
end
|