webauthn 2.0.0 → 3.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +65 -13
  4. data/.travis.yml +21 -15
  5. data/Appraisals +4 -0
  6. data/CHANGELOG.md +73 -19
  7. data/CONTRIBUTING.md +0 -5
  8. data/README.md +76 -10
  9. data/SECURITY.md +4 -4
  10. data/gemfiles/openssl_2_2.gemfile +7 -0
  11. data/lib/cose/rsapkcs1_algorithm.rb +43 -0
  12. data/lib/webauthn/attestation_object.rb +47 -0
  13. data/lib/webauthn/attestation_statement.rb +21 -21
  14. data/lib/webauthn/attestation_statement/android_key.rb +28 -30
  15. data/lib/webauthn/attestation_statement/android_safetynet.rb +30 -20
  16. data/lib/webauthn/attestation_statement/base.rb +122 -15
  17. data/lib/webauthn/attestation_statement/fido_u2f.rb +13 -9
  18. data/lib/webauthn/attestation_statement/packed.rb +14 -42
  19. data/lib/webauthn/attestation_statement/tpm.rb +38 -54
  20. data/lib/webauthn/authenticator_assertion_response.rb +11 -39
  21. data/lib/webauthn/authenticator_attestation_response.rb +30 -49
  22. data/lib/webauthn/authenticator_data.rb +51 -51
  23. data/lib/webauthn/authenticator_data/attested_credential_data.rb +34 -49
  24. data/lib/webauthn/authenticator_response.rb +20 -14
  25. data/lib/webauthn/configuration.rb +38 -21
  26. data/lib/webauthn/credential.rb +9 -8
  27. data/lib/webauthn/credential_creation_options.rb +1 -1
  28. data/lib/webauthn/fake_authenticator.rb +7 -3
  29. data/lib/webauthn/fake_authenticator/attestation_object.rb +7 -3
  30. data/lib/webauthn/fake_authenticator/authenticator_data.rb +2 -4
  31. data/lib/webauthn/fake_client.rb +19 -6
  32. data/lib/webauthn/public_key.rb +68 -0
  33. data/lib/webauthn/public_key_credential.rb +25 -8
  34. data/lib/webauthn/public_key_credential/creation_options.rb +5 -5
  35. data/lib/webauthn/public_key_credential/options.rb +6 -9
  36. data/lib/webauthn/public_key_credential/request_options.rb +1 -1
  37. data/lib/webauthn/relying_party.rb +117 -0
  38. data/lib/webauthn/u2f_migrator.rb +5 -4
  39. data/lib/webauthn/version.rb +1 -1
  40. data/script/ci/install-openssl +7 -0
  41. data/script/ci/install-ruby +13 -0
  42. data/webauthn.gemspec +13 -8
  43. metadata +69 -40
  44. data/lib/android_safetynet/attestation_response.rb +0 -84
  45. data/lib/cose/algorithm.rb +0 -38
  46. data/lib/tpm/constants.rb +0 -22
  47. data/lib/tpm/s_attest.rb +0 -26
  48. data/lib/tpm/s_attest/s_certify_info.rb +0 -14
  49. data/lib/tpm/sized_buffer.rb +0 -13
  50. data/lib/tpm/t_public.rb +0 -32
  51. data/lib/tpm/t_public/s_ecc_parms.rb +0 -17
  52. data/lib/tpm/t_public/s_rsa_parms.rb +0 -17
  53. data/lib/webauthn/attestation_statement/android_key/authorization_list.rb +0 -39
  54. data/lib/webauthn/attestation_statement/android_key/key_description.rb +0 -37
  55. data/lib/webauthn/attestation_statement/tpm/cert_info.rb +0 -44
  56. data/lib/webauthn/attestation_statement/tpm/pub_area.rb +0 -85
  57. data/lib/webauthn/signature_verifier.rb +0 -65
@@ -28,10 +28,11 @@ module WebAuthn
28
28
  end
29
29
 
30
30
  def credential
31
- @credential ||= begin
32
- hash = authenticator_data.send(:credential)
33
- WebAuthn::AuthenticatorData::AttestedCredentialData::Credential.new(hash[:id], hash[:public_key].serialize)
34
- end
31
+ @credential ||=
32
+ begin
33
+ hash = authenticator_data.send(:credential)
34
+ WebAuthn::AuthenticatorData::AttestedCredentialData::Credential.new(hash[:id], hash[:public_key].serialize)
35
+ end
35
36
  end
36
37
 
37
38
  def attestation_type
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebAuthn
4
- VERSION = "2.0.0"
4
+ VERSION = "3.0.0.alpha1"
5
5
  end
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ if [[ "$LIBSSL" == "1.0" ]]; then
6
+ sudo apt-get install libssl1.0-dev
7
+ fi
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ source "$HOME/.rvm/scripts/rvm"
6
+
7
+ if [[ "$LIBSSL" == "1.0" ]]; then
8
+ rvm use --install $RB --autolibs=read-only --disable-binary
9
+ elif [[ "$LIBSSL" == "1.1" ]]; then
10
+ rvm use --install $RB --binary --fuzzy
11
+ fi
12
+
13
+ [[ "`ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION'`" =~ "OpenSSL $LIBSSL" ]] || { echo "Wrong libssl version"; exit 1; }
@@ -22,27 +22,32 @@ Gem::Specification.new do |spec|
22
22
  "source_code_uri" => "https://github.com/cedarcode/webauthn-ruby"
23
23
  }
24
24
 
25
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
- f.match(%r{^(test|spec|features|assets)/})
27
- end
25
+ spec.files =
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features|assets)/})
28
+ end
29
+
28
30
  spec.bindir = "exe"
29
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
32
  spec.require_paths = ["lib"]
31
33
 
32
- spec.required_ruby_version = ">= 2.3"
34
+ spec.required_ruby_version = ">= 2.4"
33
35
 
36
+ spec.add_dependency "android_key_attestation", "~> 0.3.0"
34
37
  spec.add_dependency "awrence", "~> 1.1"
35
38
  spec.add_dependency "bindata", "~> 2.4"
36
39
  spec.add_dependency "cbor", "~> 0.5.9"
37
- spec.add_dependency "cose", "~> 0.8.0"
38
- spec.add_dependency "jwt", [">= 1.5", "< 3.0"]
40
+ spec.add_dependency "cose", "~> 1.0"
39
41
  spec.add_dependency "openssl", "~> 2.0"
42
+ spec.add_dependency "safety_net_attestation", "~> 0.4.0"
40
43
  spec.add_dependency "securecompare", "~> 1.0"
44
+ spec.add_dependency "tpm-key_attestation", "~> 0.9.0"
41
45
 
42
- spec.add_development_dependency "appraisal", "~> 2.2.0"
46
+ spec.add_development_dependency "appraisal", "~> 2.3.0"
43
47
  spec.add_development_dependency "bundler", ">= 1.17", "< 3.0"
44
48
  spec.add_development_dependency "byebug", "~> 11.0"
45
49
  spec.add_development_dependency "rake", "~> 13.0"
46
50
  spec.add_development_dependency "rspec", "~> 3.8"
47
- spec.add_development_dependency "rubocop", "0.75.0"
51
+ spec.add_development_dependency "rubocop", "0.80.1"
52
+ spec.add_development_dependency "rubocop-rspec", "~> 1.38.1"
48
53
  end
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.0.0
4
+ version: 3.0.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gonzalo Rodriguez
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-10-03 00:00:00.000000000 Z
12
+ date: 2020-06-27 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: android_key_attestation
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.3.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.3.0
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: awrence
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -59,48 +73,42 @@ dependencies:
59
73
  requirements:
60
74
  - - "~>"
61
75
  - !ruby/object:Gem::Version
62
- version: 0.8.0
76
+ version: '1.0'
63
77
  type: :runtime
64
78
  prerelease: false
65
79
  version_requirements: !ruby/object:Gem::Requirement
66
80
  requirements:
67
81
  - - "~>"
68
82
  - !ruby/object:Gem::Version
69
- version: 0.8.0
83
+ version: '1.0'
70
84
  - !ruby/object:Gem::Dependency
71
- name: jwt
85
+ name: openssl
72
86
  requirement: !ruby/object:Gem::Requirement
73
87
  requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: '1.5'
77
- - - "<"
88
+ - - "~>"
78
89
  - !ruby/object:Gem::Version
79
- version: '3.0'
90
+ version: '2.0'
80
91
  type: :runtime
81
92
  prerelease: false
82
93
  version_requirements: !ruby/object:Gem::Requirement
83
94
  requirements:
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: '1.5'
87
- - - "<"
95
+ - - "~>"
88
96
  - !ruby/object:Gem::Version
89
- version: '3.0'
97
+ version: '2.0'
90
98
  - !ruby/object:Gem::Dependency
91
- name: openssl
99
+ name: safety_net_attestation
92
100
  requirement: !ruby/object:Gem::Requirement
93
101
  requirements:
94
102
  - - "~>"
95
103
  - !ruby/object:Gem::Version
96
- version: '2.0'
104
+ version: 0.4.0
97
105
  type: :runtime
98
106
  prerelease: false
99
107
  version_requirements: !ruby/object:Gem::Requirement
100
108
  requirements:
101
109
  - - "~>"
102
110
  - !ruby/object:Gem::Version
103
- version: '2.0'
111
+ version: 0.4.0
104
112
  - !ruby/object:Gem::Dependency
105
113
  name: securecompare
106
114
  requirement: !ruby/object:Gem::Requirement
@@ -115,20 +123,34 @@ dependencies:
115
123
  - - "~>"
116
124
  - !ruby/object:Gem::Version
117
125
  version: '1.0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: tpm-key_attestation
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: 0.9.0
133
+ type: :runtime
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: 0.9.0
118
140
  - !ruby/object:Gem::Dependency
119
141
  name: appraisal
120
142
  requirement: !ruby/object:Gem::Requirement
121
143
  requirements:
122
144
  - - "~>"
123
145
  - !ruby/object:Gem::Version
124
- version: 2.2.0
146
+ version: 2.3.0
125
147
  type: :development
126
148
  prerelease: false
127
149
  version_requirements: !ruby/object:Gem::Requirement
128
150
  requirements:
129
151
  - - "~>"
130
152
  - !ruby/object:Gem::Version
131
- version: 2.2.0
153
+ version: 2.3.0
132
154
  - !ruby/object:Gem::Dependency
133
155
  name: bundler
134
156
  requirement: !ruby/object:Gem::Requirement
@@ -197,14 +219,28 @@ dependencies:
197
219
  requirements:
198
220
  - - '='
199
221
  - !ruby/object:Gem::Version
200
- version: 0.75.0
222
+ version: 0.80.1
201
223
  type: :development
202
224
  prerelease: false
203
225
  version_requirements: !ruby/object:Gem::Requirement
204
226
  requirements:
205
227
  - - '='
206
228
  - !ruby/object:Gem::Version
207
- version: 0.75.0
229
+ version: 0.80.1
230
+ - !ruby/object:Gem::Dependency
231
+ name: rubocop-rspec
232
+ requirement: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: 1.38.1
237
+ type: :development
238
+ prerelease: false
239
+ version_requirements: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: 1.38.1
208
244
  description: |-
209
245
  WebAuthn ruby server library ― Make your application a W3C Web Authentication conformant
210
246
  Relying Party and allow your users to authenticate with U2F and FIDO2 authenticators.
@@ -233,21 +269,13 @@ files:
233
269
  - gemfiles/cose_head.gemfile
234
270
  - gemfiles/openssl_2_0.gemfile
235
271
  - gemfiles/openssl_2_1.gemfile
272
+ - gemfiles/openssl_2_2.gemfile
236
273
  - gemfiles/openssl_head.gemfile
237
- - lib/android_safetynet/attestation_response.rb
238
- - lib/cose/algorithm.rb
239
- - lib/tpm/constants.rb
240
- - lib/tpm/s_attest.rb
241
- - lib/tpm/s_attest/s_certify_info.rb
242
- - lib/tpm/sized_buffer.rb
243
- - lib/tpm/t_public.rb
244
- - lib/tpm/t_public/s_ecc_parms.rb
245
- - lib/tpm/t_public/s_rsa_parms.rb
274
+ - lib/cose/rsapkcs1_algorithm.rb
246
275
  - lib/webauthn.rb
276
+ - lib/webauthn/attestation_object.rb
247
277
  - lib/webauthn/attestation_statement.rb
248
278
  - lib/webauthn/attestation_statement/android_key.rb
249
- - lib/webauthn/attestation_statement/android_key/authorization_list.rb
250
- - lib/webauthn/attestation_statement/android_key/key_description.rb
251
279
  - lib/webauthn/attestation_statement/android_safetynet.rb
252
280
  - lib/webauthn/attestation_statement/base.rb
253
281
  - lib/webauthn/attestation_statement/fido_u2f.rb
@@ -255,8 +283,6 @@ files:
255
283
  - lib/webauthn/attestation_statement/none.rb
256
284
  - lib/webauthn/attestation_statement/packed.rb
257
285
  - lib/webauthn/attestation_statement/tpm.rb
258
- - lib/webauthn/attestation_statement/tpm/cert_info.rb
259
- - lib/webauthn/attestation_statement/tpm/pub_area.rb
260
286
  - lib/webauthn/authenticator_assertion_response.rb
261
287
  - lib/webauthn/authenticator_attestation_response.rb
262
288
  - lib/webauthn/authenticator_data.rb
@@ -277,6 +303,7 @@ files:
277
303
  - lib/webauthn/fake_authenticator/attestation_object.rb
278
304
  - lib/webauthn/fake_authenticator/authenticator_data.rb
279
305
  - lib/webauthn/fake_client.rb
306
+ - lib/webauthn/public_key.rb
280
307
  - lib/webauthn/public_key_credential.rb
281
308
  - lib/webauthn/public_key_credential/creation_options.rb
282
309
  - lib/webauthn/public_key_credential/entity.rb
@@ -286,10 +313,12 @@ files:
286
313
  - lib/webauthn/public_key_credential/user_entity.rb
287
314
  - lib/webauthn/public_key_credential_with_assertion.rb
288
315
  - lib/webauthn/public_key_credential_with_attestation.rb
316
+ - lib/webauthn/relying_party.rb
289
317
  - lib/webauthn/security_utils.rb
290
- - lib/webauthn/signature_verifier.rb
291
318
  - lib/webauthn/u2f_migrator.rb
292
319
  - lib/webauthn/version.rb
320
+ - script/ci/install-openssl
321
+ - script/ci/install-ruby
293
322
  - webauthn.gemspec
294
323
  homepage: https://github.com/cedarcode/webauthn-ruby
295
324
  licenses:
@@ -306,14 +335,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
306
335
  requirements:
307
336
  - - ">="
308
337
  - !ruby/object:Gem::Version
309
- version: '2.3'
338
+ version: '2.4'
310
339
  required_rubygems_version: !ruby/object:Gem::Requirement
311
340
  requirements:
312
- - - ">="
341
+ - - ">"
313
342
  - !ruby/object:Gem::Version
314
- version: '0'
343
+ version: 1.3.1
315
344
  requirements: []
316
- rubygems_version: 3.0.3
345
+ rubygems_version: 3.1.4
317
346
  signing_key:
318
347
  specification_version: 4
319
348
  summary: WebAuthn ruby server library
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "base64"
4
- require "jwt"
5
- require "webauthn/security_utils"
6
-
7
- module AndroidSafetynet
8
- # Decoupled from WebAuthn, candidate for extraction
9
- # Reference: https://developer.android.com/training/safetynet/attestation.html
10
- class AttestationResponse
11
- class VerificationError < StandardError; end
12
- class LeafCertificateSubjectError < VerificationError; end
13
- class NonceMismatchError < VerificationError; end
14
- class SignatureError < VerificationError; end
15
- class ResponseMissingError < VerificationError; end
16
-
17
- CERTIRICATE_CHAIN_HEADER = "x5c"
18
- VALID_SUBJECT_HOSTNAME = "attest.android.com"
19
- HEADERS_POSITION = 1
20
- PAYLOAD_POSITION = 0
21
-
22
- attr_reader :response
23
-
24
- def initialize(response)
25
- @response = response
26
- end
27
-
28
- def verify(nonce)
29
- if response
30
- valid_nonce?(nonce) || raise(NonceMismatchError)
31
- valid_attestation_domain? || raise(LeafCertificateSubjectError)
32
- valid_signature? || raise(SignatureError)
33
- else
34
- raise(ResponseMissingError)
35
- end
36
- end
37
-
38
- def cts_profile_match?
39
- payload["ctsProfileMatch"]
40
- end
41
-
42
- def certificate_chain
43
- @certificate_chain ||= headers[CERTIRICATE_CHAIN_HEADER].map do |cert|
44
- OpenSSL::X509::Certificate.new(Base64.strict_decode64(cert))
45
- end
46
- end
47
-
48
- private
49
-
50
- def valid_nonce?(nonce)
51
- WebAuthn::SecurityUtils.secure_compare(payload["nonce"], nonce)
52
- end
53
-
54
- def valid_attestation_domain?
55
- common_name = leaf_certificate&.subject&.to_a&.assoc('CN')
56
-
57
- if common_name
58
- common_name[1] == VALID_SUBJECT_HOSTNAME
59
- end
60
- end
61
-
62
- def valid_signature?
63
- JWT.decode(response, leaf_certificate.public_key, true, algorithms: ["ES256", "RS256"])
64
- rescue JWT::VerificationError
65
- false
66
- end
67
-
68
- def leaf_certificate
69
- certificate_chain[0]
70
- end
71
-
72
- def headers
73
- jws_parts[HEADERS_POSITION]
74
- end
75
-
76
- def payload
77
- jws_parts[PAYLOAD_POSITION]
78
- end
79
-
80
- def jws_parts
81
- @jws_parts ||= JWT.decode(response, nil, false)
82
- end
83
- end
84
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "cose/key"
4
-
5
- # TODO: Move this to cose gem
6
- module COSE
7
- # https://tools.ietf.org/html/rfc8152#section-8.1
8
- Algorithm = Struct.new(:id, :name, :hash, :kty, :key_curve) do
9
- @registered = {}
10
-
11
- def self.register(id, name, hash, kty, key_curve = nil)
12
- @registered[id] = COSE::Algorithm.new(id, name, hash, kty, key_curve)
13
- end
14
-
15
- def self.find(id)
16
- @registered[id]
17
- end
18
-
19
- def self.by_name(name)
20
- @registered.values.detect { |algorithm| algorithm.name == name }
21
- end
22
-
23
- def value
24
- id
25
- end
26
- end
27
- end
28
-
29
- COSE::Algorithm.register(-7, "ES256", "SHA256", COSE::Key::EC2::KTY_EC2, "prime256v1")
30
- COSE::Algorithm.register(-35, "ES384", "SHA384", COSE::Key::EC2::KTY_EC2, "prime256v1")
31
- COSE::Algorithm.register(-36, "ES512", "SHA512", COSE::Key::EC2::KTY_EC2, "prime256v1")
32
- COSE::Algorithm.register(-37, "PS256", "SHA256", COSE::Key::RSA::KTY_RSA)
33
- COSE::Algorithm.register(-38, "PS384", "SHA384", COSE::Key::RSA::KTY_RSA)
34
- COSE::Algorithm.register(-39, "PS512", "SHA512", COSE::Key::RSA::KTY_RSA)
35
- COSE::Algorithm.register(-257, "RS256", "SHA256", COSE::Key::RSA::KTY_RSA)
36
- COSE::Algorithm.register(-258, "RS384", "SHA384", COSE::Key::RSA::KTY_RSA)
37
- COSE::Algorithm.register(-259, "RS512", "SHA512", COSE::Key::RSA::KTY_RSA)
38
- COSE::Algorithm.register(-65535, "RS1", "SHA1", COSE::Key::RSA::KTY_RSA)
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TPM
4
- # Section 6 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
5
-
6
- GENERATED_VALUE = 0xFF544347
7
-
8
- ST_ATTEST_CERTIFY = 0x8017
9
-
10
- # Algorithms
11
- ALG_RSA = 0x0001
12
- ALG_SHA1 = 0x0004
13
- ALG_SHA256 = 0x000B
14
- ALG_NULL = 0x0010
15
- ALG_RSASSA = 0x0014
16
- ALG_RSAPSS = 0x0016
17
- ALG_ECDSA = 0x0018
18
- ALG_ECC = 0x0023
19
-
20
- # ECC curves
21
- ECC_NIST_P256 = 0x0003
22
- end