webauthn 2.2.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +64 -0
- data/lib/webauthn/attestation_statement/android_key.rb +0 -7
- data/lib/webauthn/attestation_statement/base.rb +29 -2
- data/lib/webauthn/attestation_statement/fido_u2f.rb +2 -5
- data/lib/webauthn/attestation_statement/packed.rb +8 -10
- data/lib/webauthn/attestation_statement/tpm.rb +0 -1
- data/lib/webauthn/authenticator_assertion_response.rb +1 -4
- data/lib/webauthn/fake_authenticator.rb +7 -3
- data/lib/webauthn/fake_authenticator/attestation_object.rb +7 -3
- data/lib/webauthn/fake_client.rb +13 -3
- data/lib/webauthn/public_key.rb +21 -2
- data/lib/webauthn/public_key_credential.rb +13 -3
- data/lib/webauthn/version.rb +1 -1
- data/webauthn.gemspec +1 -1
- metadata +4 -5
- data/lib/webauthn/signature_verifier.rb +0 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4094023fc463d77a38548e294121f819e874bfb1c075ca43b1fb38e41cfd53a2
|
4
|
+
data.tar.gz: f410f8d7e000822943be953265a32e8f81423908e6aff282486d5afa4ab62eb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23ea57e2264cc45024174e8d7a54bc3d4f373cca916c4453079d5cfccf46caa4dbc5aa4013a54404121274a35e71f97f90a061062a21ca270a8b58d474345fb8
|
7
|
+
data.tar.gz: 3b49c5b5b845fdcfc3b0b647a16aeab07ad219210e8e29835d171e387d6bcc4ac5fe08a4f9d2978ee6db1022363d1c2b06fe0a163a8625038aca51e1d274e903
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v2.3.0] - 2020-06-27
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Ability to access extension outputs with `PublicKeyCredential#client_extension_outputs` and `PublicKeyCredential#authenticator_extension_outputs` ([@santiagorodriguez96])
|
8
|
+
|
3
9
|
## [v2.2.1] - 2020-06-06
|
4
10
|
|
5
11
|
### Fixed
|
@@ -288,6 +294,7 @@ Note: Both additions should help making it compatible with Chrome for Android 70
|
|
288
294
|
- `WebAuthn::AuthenticatorAttestationResponse.valid?` can be used to validate fido-u2f attestations returned by the browser
|
289
295
|
- Works with ruby 2.5
|
290
296
|
|
297
|
+
[v2.3.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.2.1...v2.3.0/
|
291
298
|
[v2.2.1]: https://github.com/cedarcode/webauthn-ruby/compare/v2.2.0...v2.2.1/
|
292
299
|
[v2.2.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.1.0...v2.2.0/
|
293
300
|
[v2.1.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.0.0...v2.1.0/
|
data/README.md
CHANGED
@@ -252,6 +252,54 @@ rescue WebAuthn::Error => e
|
|
252
252
|
end
|
253
253
|
```
|
254
254
|
|
255
|
+
### Extensions
|
256
|
+
|
257
|
+
> The mechanism for generating public key credentials, as well as requesting and generating Authentication assertions, as defined in Web Authentication API, can be extended to suit particular use cases. Each case is addressed by defining a registration extension and/or an authentication extension.
|
258
|
+
|
259
|
+
> When creating a public key credential or requesting an authentication assertion, a WebAuthn Relying Party can request the use of a set of extensions. These extensions will be invoked during the requested ceremony if they are supported by the WebAuthn Client and/or the WebAuthn Authenticator. The Relying Party sends the client extension input for each extension in the get() call (for authentication extensions) or create() call (for registration extensions) to the WebAuthn client. [[source](https://www.w3.org/TR/webauthn-2/#sctn-extensions)]
|
260
|
+
|
261
|
+
Extensions can be requested in the initiation phase in both Credential Registration and Authentication ceremonies by adding the extension parameter when generating the options for create/get:
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
# Credential Registration
|
265
|
+
creation_options = WebAuthn::Credential.options_for_create(
|
266
|
+
user: { id: user.webauthn_id, name: user.name },
|
267
|
+
exclude: user.credentials.map { |c| c.webauthn_id },
|
268
|
+
extensions: { appidExclude: domain.to_s }
|
269
|
+
)
|
270
|
+
|
271
|
+
# OR
|
272
|
+
|
273
|
+
# Credential Authentication
|
274
|
+
options = WebAuthn::Credential.options_for_get(
|
275
|
+
allow: user.credentials.map { |c| c.webauthn_id },
|
276
|
+
extensions: { appid: domain.to_s }
|
277
|
+
)
|
278
|
+
```
|
279
|
+
|
280
|
+
Consequently, after these `options` are sent to the WebAuthn client:
|
281
|
+
|
282
|
+
> The WebAuthn client performs client extension processing for each extension that the client supports, and augments the client data as specified by each extension, by including the extension identifier and client extension output values.
|
283
|
+
|
284
|
+
> For authenticator extensions, as part of the client extension processing, the client also creates the CBOR authenticator extension input value for each extension (often based on the corresponding client extension input value), and passes them to the authenticator in the create() call (for registration extensions) or the get() call (for authentication extensions).
|
285
|
+
|
286
|
+
> The authenticator, in turn, performs additional processing for the extensions that it supports, and returns the CBOR authenticator extension output for each as specified by the extension. Part of the client extension processing for authenticator extensions is to use the authenticator extension output as an input to creating the client extension output. [[source](https://www.w3.org/TR/webauthn-2/#sctn-extensions)]
|
287
|
+
|
288
|
+
Finally, you can check the values returned for each extension by calling `client_extension_outputs` and `authenticator_extension_outputs` respectively.
|
289
|
+
For example, following the initialization phase for the Credential Authentication ceremony specified in the above example:
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
webauthn_credential = WebAuthn::Credential.from_get(credential_get_result_hash)
|
293
|
+
|
294
|
+
webauthn_credential.client_extension_outputs #=> { "appid" => true }
|
295
|
+
webauthn_credential.authenticator_extension_outputs #=> nil
|
296
|
+
```
|
297
|
+
|
298
|
+
A list of all currently defined extensions:
|
299
|
+
|
300
|
+
- [Last published version](https://www.w3.org/TR/webauthn-2/#sctn-defined-extensions)
|
301
|
+
- [Next version (in draft)](https://w3c.github.io/webauthn/#sctn-defined-extensions)
|
302
|
+
|
255
303
|
## API
|
256
304
|
|
257
305
|
#### `WebAuthn.generate_user_id`
|
@@ -342,6 +390,22 @@ credential_with_assertion.verify(
|
|
342
390
|
)
|
343
391
|
```
|
344
392
|
|
393
|
+
#### `PublicKeyCredential#client_extension_outputs`
|
394
|
+
|
395
|
+
```ruby
|
396
|
+
credential = WebAuthn::Credential.from_create(params[:publicKeyCredential])
|
397
|
+
|
398
|
+
credential.client_extension_outputs
|
399
|
+
```
|
400
|
+
|
401
|
+
#### `PublicKeyCredential#authenticator_extension_outputs`
|
402
|
+
|
403
|
+
```ruby
|
404
|
+
credential = WebAuthn::Credential.from_create(params[:publicKeyCredential])
|
405
|
+
|
406
|
+
credential.authenticator_extension_outputs
|
407
|
+
```
|
408
|
+
|
345
409
|
## Attestation
|
346
410
|
|
347
411
|
### Attestation Statement Format
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require "android_key_attestation"
|
4
4
|
require "openssl"
|
5
5
|
require "webauthn/attestation_statement/base"
|
6
|
-
require "webauthn/signature_verifier"
|
7
6
|
|
8
7
|
module WebAuthn
|
9
8
|
module AttestationStatement
|
@@ -21,12 +20,6 @@ module WebAuthn
|
|
21
20
|
|
22
21
|
private
|
23
22
|
|
24
|
-
def valid_signature?(authenticator_data, client_data_hash)
|
25
|
-
WebAuthn::SignatureVerifier
|
26
|
-
.new(algorithm, attestation_certificate.public_key)
|
27
|
-
.verify(signature, authenticator_data.data + client_data_hash)
|
28
|
-
end
|
29
|
-
|
30
23
|
def matching_public_key?(authenticator_data)
|
31
24
|
attestation_certificate.public_key.to_der == authenticator_data.credential.public_key_object.to_der
|
32
25
|
end
|
@@ -1,11 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "cose/algorithm"
|
4
|
+
require "cose/error"
|
5
|
+
require "cose/rsapkcs1_algorithm"
|
3
6
|
require "openssl"
|
4
7
|
require "webauthn/authenticator_data/attested_credential_data"
|
5
8
|
require "webauthn/error"
|
6
9
|
|
7
10
|
module WebAuthn
|
8
11
|
module AttestationStatement
|
12
|
+
class UnsupportedAlgorithm < Error; end
|
13
|
+
|
9
14
|
ATTESTATION_TYPE_NONE = "None"
|
10
15
|
ATTESTATION_TYPE_BASIC = "Basic"
|
11
16
|
ATTESTATION_TYPE_SELF = "Self"
|
@@ -19,8 +24,6 @@ module WebAuthn
|
|
19
24
|
].freeze
|
20
25
|
|
21
26
|
class Base
|
22
|
-
class NotSupportedError < Error; end
|
23
|
-
|
24
27
|
AAGUID_EXTENSION_OID = "1.3.6.1.4.1.45724.1.1.4"
|
25
28
|
|
26
29
|
def initialize(statement)
|
@@ -147,6 +150,30 @@ module WebAuthn
|
|
147
150
|
OpenSSL::ASN1.decode(ext_value.value).value
|
148
151
|
end
|
149
152
|
|
153
|
+
def valid_signature?(authenticator_data, client_data_hash, public_key = attestation_certificate.public_key)
|
154
|
+
raise("Incompatible algorithm and key") unless cose_algorithm.compatible_key?(public_key)
|
155
|
+
|
156
|
+
cose_algorithm.verify(
|
157
|
+
public_key,
|
158
|
+
signature,
|
159
|
+
verification_data(authenticator_data, client_data_hash)
|
160
|
+
)
|
161
|
+
rescue COSE::Error
|
162
|
+
false
|
163
|
+
end
|
164
|
+
|
165
|
+
def verification_data(authenticator_data, client_data_hash)
|
166
|
+
authenticator_data.data + client_data_hash
|
167
|
+
end
|
168
|
+
|
169
|
+
def cose_algorithm
|
170
|
+
@cose_algorithm ||=
|
171
|
+
COSE::Algorithm.find(algorithm).tap do |alg|
|
172
|
+
alg && configuration.algorithms.include?(alg.name) ||
|
173
|
+
raise(UnsupportedAlgorithm, "Unsupported algorithm #{algorithm}")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
150
177
|
def configuration
|
151
178
|
WebAuthn.configuration
|
152
179
|
end
|
@@ -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
|
@@ -48,10 +47,8 @@ module WebAuthn
|
|
48
47
|
attested_credential_data_aaguid == WebAuthn::AuthenticatorData::AttestedCredentialData::ZEROED_AAGUID
|
49
48
|
end
|
50
49
|
|
51
|
-
def
|
52
|
-
|
53
|
-
.new(VALID_ATTESTATION_CERTIFICATE_ALGORITHM, certificate_public_key)
|
54
|
-
.verify(signature, verification_data(authenticator_data, client_data_hash))
|
50
|
+
def algorithm
|
51
|
+
VALID_ATTESTATION_CERTIFICATE_ALGORITHM.id
|
55
52
|
end
|
56
53
|
|
57
54
|
def verification_data(authenticator_data, client_data_hash)
|
@@ -2,7 +2,6 @@
|
|
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
|
@@ -53,15 +52,6 @@ module WebAuthn
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
def valid_signature?(authenticator_data, client_data_hash)
|
57
|
-
signature_verifier = WebAuthn::SignatureVerifier.new(
|
58
|
-
algorithm,
|
59
|
-
attestation_certificate&.public_key || authenticator_data.credential.public_key_object
|
60
|
-
)
|
61
|
-
|
62
|
-
signature_verifier.verify(signature, authenticator_data.data + client_data_hash)
|
63
|
-
end
|
64
|
-
|
65
55
|
def attestation_type
|
66
56
|
if attestation_trust_path
|
67
57
|
WebAuthn::AttestationStatement::ATTESTATION_TYPE_BASIC_OR_ATTCA # FIXME: use metadata if available
|
@@ -69,6 +59,14 @@ module WebAuthn
|
|
69
59
|
WebAuthn::AttestationStatement::ATTESTATION_TYPE_SELF
|
70
60
|
end
|
71
61
|
end
|
62
|
+
|
63
|
+
def valid_signature?(authenticator_data, client_data_hash)
|
64
|
+
super(
|
65
|
+
authenticator_data,
|
66
|
+
client_data_hash,
|
67
|
+
attestation_certificate&.public_key || authenticator_data.credential.public_key_object
|
68
|
+
)
|
69
|
+
end
|
72
70
|
end
|
73
71
|
end
|
74
72
|
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
|
@@ -54,9 +53,7 @@ module WebAuthn
|
|
54
53
|
attr_reader :authenticator_data_bytes, :signature
|
55
54
|
|
56
55
|
def valid_signature?(webauthn_public_key)
|
57
|
-
|
58
|
-
.new(webauthn_public_key.alg, webauthn_public_key.pkey)
|
59
|
-
.verify(signature, authenticator_data_bytes + client_data.hash)
|
56
|
+
webauthn_public_key.verify(signature, authenticator_data_bytes + client_data.hash)
|
60
57
|
end
|
61
58
|
|
62
59
|
def valid_sign_count?(stored_sign_count)
|
@@ -18,7 +18,8 @@ module WebAuthn
|
|
18
18
|
user_present: true,
|
19
19
|
user_verified: false,
|
20
20
|
attested_credential_data: true,
|
21
|
-
sign_count: nil
|
21
|
+
sign_count: nil,
|
22
|
+
extensions: nil
|
22
23
|
)
|
23
24
|
credential_id, credential_key, credential_sign_count = new_credential
|
24
25
|
sign_count ||= credential_sign_count
|
@@ -37,7 +38,8 @@ module WebAuthn
|
|
37
38
|
user_present: user_present,
|
38
39
|
user_verified: user_verified,
|
39
40
|
attested_credential_data: attested_credential_data,
|
40
|
-
sign_count: sign_count
|
41
|
+
sign_count: sign_count,
|
42
|
+
extensions: extensions
|
41
43
|
).serialize
|
42
44
|
end
|
43
45
|
|
@@ -47,7 +49,8 @@ module WebAuthn
|
|
47
49
|
user_present: true,
|
48
50
|
user_verified: false,
|
49
51
|
aaguid: AuthenticatorData::AAGUID,
|
50
|
-
sign_count: nil
|
52
|
+
sign_count: nil,
|
53
|
+
extensions: nil
|
51
54
|
)
|
52
55
|
credential_options = credentials[rp_id]
|
53
56
|
|
@@ -63,6 +66,7 @@ module WebAuthn
|
|
63
66
|
aaguid: aaguid,
|
64
67
|
credential: nil,
|
65
68
|
sign_count: sign_count || credential_sign_count,
|
69
|
+
extensions: extensions
|
66
70
|
).serialize
|
67
71
|
|
68
72
|
signature = credential_key.sign("SHA256", authenticator_data + client_data_hash)
|
@@ -14,7 +14,8 @@ module WebAuthn
|
|
14
14
|
user_present: true,
|
15
15
|
user_verified: false,
|
16
16
|
attested_credential_data: true,
|
17
|
-
sign_count: 0
|
17
|
+
sign_count: 0,
|
18
|
+
extensions: nil
|
18
19
|
)
|
19
20
|
@client_data_hash = client_data_hash
|
20
21
|
@rp_id_hash = rp_id_hash
|
@@ -24,6 +25,7 @@ module WebAuthn
|
|
24
25
|
@user_verified = user_verified
|
25
26
|
@attested_credential_data = attested_credential_data
|
26
27
|
@sign_count = sign_count
|
28
|
+
@extensions = extensions
|
27
29
|
end
|
28
30
|
|
29
31
|
def serialize
|
@@ -44,7 +46,8 @@ module WebAuthn
|
|
44
46
|
:user_present,
|
45
47
|
:user_verified,
|
46
48
|
:attested_credential_data,
|
47
|
-
:sign_count
|
49
|
+
:sign_count,
|
50
|
+
:extensions
|
48
51
|
)
|
49
52
|
|
50
53
|
def authenticator_data
|
@@ -60,7 +63,8 @@ module WebAuthn
|
|
60
63
|
credential: credential_data,
|
61
64
|
user_present: user_present,
|
62
65
|
user_verified: user_verified,
|
63
|
-
sign_count: 0
|
66
|
+
sign_count: 0,
|
67
|
+
extensions: extensions
|
64
68
|
)
|
65
69
|
end
|
66
70
|
end
|
data/lib/webauthn/fake_client.rb
CHANGED
@@ -29,7 +29,8 @@ module WebAuthn
|
|
29
29
|
rp_id: nil,
|
30
30
|
user_present: true,
|
31
31
|
user_verified: false,
|
32
|
-
attested_credential_data: true
|
32
|
+
attested_credential_data: true,
|
33
|
+
extensions: nil
|
33
34
|
)
|
34
35
|
rp_id ||= URI.parse(origin).host
|
35
36
|
|
@@ -41,7 +42,8 @@ module WebAuthn
|
|
41
42
|
client_data_hash: client_data_hash,
|
42
43
|
user_present: user_present,
|
43
44
|
user_verified: user_verified,
|
44
|
-
attested_credential_data: attested_credential_data
|
45
|
+
attested_credential_data: attested_credential_data,
|
46
|
+
extensions: extensions
|
45
47
|
)
|
46
48
|
|
47
49
|
id =
|
@@ -58,6 +60,7 @@ module WebAuthn
|
|
58
60
|
"type" => "public-key",
|
59
61
|
"id" => internal_encoder.encode(id),
|
60
62
|
"rawId" => encoder.encode(id),
|
63
|
+
"clientExtensionResults" => extensions,
|
61
64
|
"response" => {
|
62
65
|
"attestationObject" => encoder.encode(attestation_object),
|
63
66
|
"clientDataJSON" => encoder.encode(client_data_json)
|
@@ -65,7 +68,12 @@ module WebAuthn
|
|
65
68
|
}
|
66
69
|
end
|
67
70
|
|
68
|
-
def get(challenge: fake_challenge,
|
71
|
+
def get(challenge: fake_challenge,
|
72
|
+
rp_id: nil,
|
73
|
+
user_present: true,
|
74
|
+
user_verified: false,
|
75
|
+
sign_count: nil,
|
76
|
+
extensions: nil)
|
69
77
|
rp_id ||= URI.parse(origin).host
|
70
78
|
|
71
79
|
client_data_json = data_json_for(:get, encoder.decode(challenge))
|
@@ -77,12 +85,14 @@ module WebAuthn
|
|
77
85
|
user_present: user_present,
|
78
86
|
user_verified: user_verified,
|
79
87
|
sign_count: sign_count,
|
88
|
+
extensions: extensions
|
80
89
|
)
|
81
90
|
|
82
91
|
{
|
83
92
|
"type" => "public-key",
|
84
93
|
"id" => internal_encoder.encode(assertion[:credential_id]),
|
85
94
|
"rawId" => encoder.encode(assertion[:credential_id]),
|
95
|
+
"clientExtensionResults" => extensions,
|
86
96
|
"response" => {
|
87
97
|
"clientDataJSON" => encoder.encode(client_data_json),
|
88
98
|
"authenticatorData" => encoder.encode(assertion[:authenticator_data]),
|
data/lib/webauthn/public_key.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "webauthn/attestation_statement/fido_u2f/public_key"
|
4
|
-
require "cose/key"
|
5
3
|
require "cose/algorithm"
|
4
|
+
require "cose/error"
|
5
|
+
require "cose/key"
|
6
|
+
require "cose/rsapkcs1_algorithm"
|
7
|
+
require "webauthn/attestation_statement/fido_u2f/public_key"
|
6
8
|
|
7
9
|
module WebAuthn
|
8
10
|
class PublicKey
|
11
|
+
class UnsupportedAlgorithm < Error; end
|
12
|
+
|
9
13
|
def self.deserialize(public_key)
|
10
14
|
cose_key =
|
11
15
|
if WebAuthn::AttestationStatement::FidoU2f::PublicKey.uncompressed_point?(public_key)
|
@@ -45,5 +49,20 @@ module WebAuthn
|
|
45
49
|
def alg
|
46
50
|
@cose_key.alg
|
47
51
|
end
|
52
|
+
|
53
|
+
def verify(signature, verification_data)
|
54
|
+
cose_algorithm.verify(pkey, signature, verification_data)
|
55
|
+
rescue COSE::Error
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def cose_algorithm
|
62
|
+
@cose_algorithm ||= COSE::Algorithm.find(alg) || raise(
|
63
|
+
UnsupportedAlgorithm,
|
64
|
+
"The public key algorithm #{alg} is not among the available COSE algorithms"
|
65
|
+
)
|
66
|
+
end
|
48
67
|
end
|
49
68
|
end
|
@@ -4,21 +4,23 @@ require "webauthn/encoder"
|
|
4
4
|
|
5
5
|
module WebAuthn
|
6
6
|
class PublicKeyCredential
|
7
|
-
attr_reader :type, :id, :raw_id, :response
|
7
|
+
attr_reader :type, :id, :raw_id, :client_extension_outputs, :response
|
8
8
|
|
9
9
|
def self.from_client(credential)
|
10
10
|
new(
|
11
11
|
type: credential["type"],
|
12
12
|
id: credential["id"],
|
13
13
|
raw_id: WebAuthn.configuration.encoder.decode(credential["rawId"]),
|
14
|
+
client_extension_outputs: credential["clientExtensionResults"],
|
14
15
|
response: response_class.from_client(credential["response"])
|
15
16
|
)
|
16
17
|
end
|
17
18
|
|
18
|
-
def initialize(type:, id:, raw_id:, response:)
|
19
|
+
def initialize(type:, id:, raw_id:, client_extension_outputs: {}, response:)
|
19
20
|
@type = type
|
20
21
|
@id = id
|
21
22
|
@raw_id = raw_id
|
23
|
+
@client_extension_outputs = client_extension_outputs
|
22
24
|
@response = response
|
23
25
|
end
|
24
26
|
|
@@ -30,7 +32,11 @@ module WebAuthn
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def sign_count
|
33
|
-
|
35
|
+
authenticator_data&.sign_count
|
36
|
+
end
|
37
|
+
|
38
|
+
def authenticator_extension_outputs
|
39
|
+
authenticator_data.extension_data if authenticator_data&.extension_data_included?
|
34
40
|
end
|
35
41
|
|
36
42
|
private
|
@@ -43,6 +49,10 @@ module WebAuthn
|
|
43
49
|
raw_id && id && raw_id == WebAuthn.standard_encoder.decode(id)
|
44
50
|
end
|
45
51
|
|
52
|
+
def authenticator_data
|
53
|
+
response&.authenticator_data
|
54
|
+
end
|
55
|
+
|
46
56
|
def encoder
|
47
57
|
WebAuthn.configuration.encoder
|
48
58
|
end
|
data/lib/webauthn/version.rb
CHANGED
data/webauthn.gemspec
CHANGED
@@ -43,7 +43,7 @@ Gem::Specification.new do |spec|
|
|
43
43
|
spec.add_dependency "securecompare", "~> 1.0"
|
44
44
|
spec.add_dependency "tpm-key_attestation", "~> 0.9.0"
|
45
45
|
|
46
|
-
spec.add_development_dependency "appraisal", "~> 2.
|
46
|
+
spec.add_development_dependency "appraisal", "~> 2.3.0"
|
47
47
|
spec.add_development_dependency "bundler", ">= 1.17", "< 3.0"
|
48
48
|
spec.add_development_dependency "byebug", "~> 11.0"
|
49
49
|
spec.add_development_dependency "rake", "~> 13.0"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: webauthn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gonzalo Rodriguez
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-06-
|
12
|
+
date: 2020-06-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: android_key_attestation
|
@@ -143,14 +143,14 @@ dependencies:
|
|
143
143
|
requirements:
|
144
144
|
- - "~>"
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: 2.
|
146
|
+
version: 2.3.0
|
147
147
|
type: :development
|
148
148
|
prerelease: false
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
151
|
- - "~>"
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: 2.
|
153
|
+
version: 2.3.0
|
154
154
|
- !ruby/object:Gem::Dependency
|
155
155
|
name: bundler
|
156
156
|
requirement: !ruby/object:Gem::Requirement
|
@@ -314,7 +314,6 @@ files:
|
|
314
314
|
- lib/webauthn/public_key_credential_with_assertion.rb
|
315
315
|
- lib/webauthn/public_key_credential_with_attestation.rb
|
316
316
|
- lib/webauthn/security_utils.rb
|
317
|
-
- lib/webauthn/signature_verifier.rb
|
318
317
|
- lib/webauthn/u2f_migrator.rb
|
319
318
|
- lib/webauthn/version.rb
|
320
319
|
- script/ci/install-openssl
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "cose"
|
4
|
-
require "cose/rsapkcs1_algorithm"
|
5
|
-
require "openssl"
|
6
|
-
require "webauthn/error"
|
7
|
-
|
8
|
-
module WebAuthn
|
9
|
-
class SignatureVerifier
|
10
|
-
class UnsupportedAlgorithm < Error; end
|
11
|
-
|
12
|
-
def initialize(algorithm, public_key)
|
13
|
-
@algorithm = algorithm
|
14
|
-
@public_key = public_key
|
15
|
-
|
16
|
-
validate
|
17
|
-
end
|
18
|
-
|
19
|
-
def verify(signature, verification_data)
|
20
|
-
cose_algorithm.verify(public_key, signature, verification_data)
|
21
|
-
rescue COSE::Error
|
22
|
-
false
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
attr_reader :algorithm, :public_key
|
28
|
-
|
29
|
-
def cose_algorithm
|
30
|
-
case algorithm
|
31
|
-
when COSE::Algorithm::Base
|
32
|
-
algorithm
|
33
|
-
else
|
34
|
-
COSE::Algorithm.find(algorithm)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def validate
|
39
|
-
if !cose_algorithm
|
40
|
-
raise UnsupportedAlgorithm, "Unsupported algorithm #{algorithm}"
|
41
|
-
elsif !supported_algorithms.include?(cose_algorithm.name)
|
42
|
-
raise UnsupportedAlgorithm, "Unsupported algorithm #{algorithm}"
|
43
|
-
elsif !cose_algorithm.compatible_key?(public_key)
|
44
|
-
raise("Incompatible algorithm and key")
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def supported_algorithms
|
49
|
-
WebAuthn.configuration.algorithms
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|