webauthn 2.5.0 → 3.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -56
  3. data/.travis.yml +39 -0
  4. data/Appraisals +12 -0
  5. data/CHANGELOG.md +0 -30
  6. data/README.md +2 -3
  7. data/SECURITY.md +3 -6
  8. data/gemfiles/cose_head.gemfile +7 -0
  9. data/gemfiles/openssl_2_0.gemfile +7 -0
  10. data/gemfiles/openssl_head.gemfile +7 -0
  11. data/lib/cose/rsapkcs1_algorithm.rb +0 -7
  12. data/lib/webauthn/attestation_object.rb +9 -5
  13. data/lib/webauthn/attestation_statement/android_key.rb +4 -0
  14. data/lib/webauthn/attestation_statement/android_safetynet.rb +5 -1
  15. data/lib/webauthn/attestation_statement/base.rb +14 -17
  16. data/lib/webauthn/attestation_statement/none.rb +1 -7
  17. data/lib/webauthn/attestation_statement.rb +3 -6
  18. data/lib/webauthn/authenticator_assertion_response.rb +4 -3
  19. data/lib/webauthn/authenticator_attestation_response.rb +10 -7
  20. data/lib/webauthn/authenticator_data/attested_credential_data.rb +10 -4
  21. data/lib/webauthn/authenticator_response.rb +6 -5
  22. data/lib/webauthn/configuration.rb +36 -38
  23. data/lib/webauthn/credential.rb +5 -4
  24. data/lib/webauthn/credential_creation_options.rb +0 -2
  25. data/lib/webauthn/credential_request_options.rb +0 -2
  26. data/lib/webauthn/fake_authenticator.rb +2 -10
  27. data/lib/webauthn/fake_client.rb +5 -12
  28. data/lib/webauthn/public_key_credential/creation_options.rb +3 -3
  29. data/lib/webauthn/public_key_credential/entity.rb +4 -3
  30. data/lib/webauthn/public_key_credential/options.rb +6 -9
  31. data/lib/webauthn/public_key_credential/request_options.rb +1 -1
  32. data/lib/webauthn/public_key_credential.rb +15 -8
  33. data/lib/webauthn/relying_party.rb +117 -0
  34. data/lib/webauthn/version.rb +1 -1
  35. data/script/ci/install-openssl +7 -0
  36. data/script/ci/install-ruby +13 -0
  37. data/webauthn.gemspec +6 -7
  38. metadata +26 -35
  39. data/.github/workflows/build.yml +0 -36
  40. data/lib/webauthn/attestation_statement/apple.rb +0 -65
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e6487b19f172c0c7e96af23d04e47f91bebd2ef7d20f144f99f85e761a2db86
4
- data.tar.gz: 7623405e7cd01708f29897a0d4183fbc8c9b2a3dfb06b9c182646ddaf9c6cb0d
3
+ metadata.gz: 1f418eb52a085d8e7c03bd1c3d11b88ed8fe467f4ec01d3178836689d470f436
4
+ data.tar.gz: 4ab67e8804cbd7d785e29b94760af6db9d6b1e52de1a58bafc74aed19b5b7e21
5
5
  SHA512:
6
- metadata.gz: d2f8d2137b2ee140a3258fbbff8d62e49264b2eafa80f0726dacc16a742addf75625b9da51696db6f3862a85e63f44ca5fc2b73320b1c256dd1c57f96121de24
7
- data.tar.gz: dcb2ea914a14944b4bf7c4682394df12e00ddd4a4b0cc1076a03a7368bf4d563d08b61fbbe27ece3ddcbc05a9ed542d8236c1bfce833669c9b60c5d3387b35b4
6
+ metadata.gz: ccdcd22e494079eb67c122c03e3061166f91de50dd0c2bf8662748c976483a9d472fa9f8d6b67db9abf484e8eac182195b5f1e1cca8aaaf88146d831919f1c93
7
+ data.tar.gz: b2506b530c796ee57e5d037e7dd3692b1e359408fd9757141b6c4f042c43d57344baa09dc63d17ace549927eeea50abaa8c0771a0ab6da0ece5d880362d890db
data/.rubocop.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  require:
2
2
  - rubocop-rspec
3
- - rubocop-rake
4
3
 
5
4
  inherit_mode:
6
5
  merge:
@@ -9,7 +8,6 @@ inherit_mode:
9
8
  AllCops:
10
9
  TargetRubyVersion: 2.4
11
10
  DisabledByDefault: true
12
- NewCops: disable
13
11
  Exclude:
14
12
  - "gemfiles/**/*"
15
13
  - "vendor/**/*"
@@ -26,12 +24,6 @@ Layout:
26
24
  Layout/ClassStructure:
27
25
  Enabled: true
28
26
 
29
- Layout/EmptyLineBetweenDefs:
30
- AllowAdjacentOneLineDefs: true
31
-
32
- Layout/EmptyLinesAroundAttributeAccessor:
33
- Enabled: true
34
-
35
27
  Layout/FirstMethodArgumentLineBreak:
36
28
  Enabled: true
37
29
 
@@ -46,60 +38,12 @@ Layout/MultilineAssignmentLayout:
46
38
  Layout/MultilineMethodArgumentLineBreaks:
47
39
  Enabled: true
48
40
 
49
- Layout/SpaceAroundMethodCallOperator:
50
- Enabled: true
51
-
52
41
  Lint:
53
42
  Enabled: true
54
43
 
55
- Lint/DeprecatedOpenSSLConstant:
56
- Enabled: true
57
-
58
- Lint/MixedRegexpCaptureTypes:
59
- Enabled: true
60
-
61
- Lint/RaiseException:
62
- Enabled: true
63
-
64
- Lint/StructNewOverride:
65
- Enabled: true
66
-
67
- Lint/BinaryOperatorWithIdenticalOperands:
68
- Enabled: true
69
-
70
- Lint/DuplicateElsifCondition:
71
- Enabled: true
72
-
73
- Lint/DuplicateRescueException:
74
- Enabled: true
75
-
76
- Lint/EmptyConditionalBody:
77
- Enabled: true
78
-
79
- Lint/FloatComparison:
80
- Enabled: true
81
-
82
- Lint/MissingSuper:
83
- Enabled: true
84
-
85
- Lint/OutOfRangeRegexpRef:
86
- Enabled: true
87
-
88
- Lint/SelfAssignment:
89
- Enabled: true
90
-
91
- Lint/TopLevelReturnWithArgument:
92
- Enabled: true
93
-
94
- Lint/UnreachableLoop:
95
- Enabled: true
96
-
97
44
  Naming:
98
45
  Enabled: true
99
46
 
100
- Naming/VariableNumber:
101
- Enabled: false
102
-
103
47
  RSpec/Be:
104
48
  Enabled: true
105
49
 
data/.travis.yml ADDED
@@ -0,0 +1,39 @@
1
+ dist: bionic
2
+ language: ruby
3
+
4
+ cache:
5
+ bundler: true
6
+ directories:
7
+ - /home/travis/.rvm/
8
+
9
+ env:
10
+ - LIBSSL=1.1 RB=2.7.1
11
+ - LIBSSL=1.1 RB=2.6.6
12
+ - LIBSSL=1.1 RB=2.5.8
13
+ - LIBSSL=1.1 RB=2.4.10
14
+ - LIBSSL=1.1 RB=ruby-head
15
+ - LIBSSL=1.0 RB=2.7.1
16
+ - LIBSSL=1.0 RB=2.6.6
17
+ - LIBSSL=1.0 RB=2.5.8
18
+ - LIBSSL=1.0 RB=2.4.10
19
+ - LIBSSL=1.0 RB=ruby-head
20
+
21
+ gemfile:
22
+ - gemfiles/cose_head.gemfile
23
+ - gemfiles/openssl_head.gemfile
24
+ - gemfiles/openssl_2_2.gemfile
25
+ - gemfiles/openssl_2_1.gemfile
26
+ - gemfiles/openssl_2_0.gemfile
27
+
28
+ matrix:
29
+ fast_finish: true
30
+ allow_failures:
31
+ - env: LIBSSL=1.1 RB=ruby-head
32
+ - env: LIBSSL=1.0 RB=ruby-head
33
+ - gemfile: gemfiles/cose_head.gemfile
34
+ - gemfile: gemfiles/openssl_head.gemfile
35
+
36
+ before_install:
37
+ - ./script/ci/install-openssl
38
+ - ./script/ci/install-ruby
39
+ - gem install bundler -v "~> 2.0"
data/Appraisals CHANGED
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ appraise "cose_head" do
4
+ gem "cose", git: "https://github.com/cedarcode/cose-ruby"
5
+ end
6
+
7
+ appraise "openssl_head" do
8
+ gem "openssl", git: "https://github.com/ruby/openssl"
9
+ end
10
+
3
11
  appraise "openssl_2_2" do
4
12
  gem "openssl", "~> 2.2.0"
5
13
  end
@@ -7,3 +15,7 @@ end
7
15
  appraise "openssl_2_1" do
8
16
  gem "openssl", "~> 2.1.0"
9
17
  end
18
+
19
+ appraise "openssl_2_0" do
20
+ gem "openssl", "~> 2.0.0"
21
+ end
data/CHANGELOG.md CHANGED
@@ -6,30 +6,6 @@
6
6
 
7
7
  - Ability to define multiple relying parties with the introduction of the `WebAuthn::RelyingParty` class ([@padulafacundo], [@brauliomartinezlm])
8
8
 
9
- ## [v2.5.0] - 2021-03-14
10
-
11
- ### Added
12
-
13
- - Support 'apple' attestation statement format ([#343](https://github.com/cedarcode/webauthn-ruby/pull/343) / [@juanarias93], [@santiagorodriguez96])
14
- - Allow specifying an array of ids as `allow_credentials:` for `FakeClient#get` method ([#335](https://github.com/cedarcode/webauthn-ruby/pull/335) / [@kingjan1999])
15
-
16
- ### Removed
17
-
18
- - No longer accept "removed from the WebAuthn spec" options `rp: { icon: }` and `user: { icon: }` for `WebAuthn::Credential.options_for_create` method ([#326](https://github.com/cedarcode/webauthn-ruby/pull/326) / [@santiagorodriguez96])
19
-
20
- ## [v2.4.1] - 2021-02-15
21
-
22
- ### Fixed
23
-
24
- - Fix verification of new credential if no attestation provided and 'None' type is not among configured `acceptable_attestation_types`. I.e. reject it instead of letting it go through.
25
-
26
- ## [v2.4.0] - 2020-09-03
27
-
28
- ### Added
29
-
30
- - Support for ES256K credentials
31
- - `FakeClient#get` accepts `user_handle:` keyword argument ([@lgarron])
32
-
33
9
  ## [v2.3.0] - 2020-06-27
34
10
 
35
11
  ### Added
@@ -325,9 +301,6 @@ Note: Both additions should help making it compatible with Chrome for Android 70
325
301
  - Works with ruby 2.5
326
302
 
327
303
  [v3.0.0.alpha1]: https://github.com/cedarcode/webauthn-ruby/compare/2-stable...v3.0.0.alpha1/
328
- [v2.5.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.4.1...v2.5.0/
329
- [v2.4.1]: https://github.com/cedarcode/webauthn-ruby/compare/v2.4.0...v2.4.1/
330
- [v2.4.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.3.0...v2.4.0/
331
304
  [v2.3.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.2.1...v2.3.0/
332
305
  [v2.2.1]: https://github.com/cedarcode/webauthn-ruby/compare/v2.2.0...v2.2.1/
333
306
  [v2.2.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.1.0...v2.2.0/
@@ -364,6 +337,3 @@ Note: Both additions should help making it compatible with Chrome for Android 70
364
337
  [@ssuttner]: https://github.com/ssuttner
365
338
  [@padulafacundo]: https://github.com/padulafacundo
366
339
  [@santiagorodriguez96]: https://github.com/santiagorodriguez96
367
- [@lgarron]: https://github.com/lgarron
368
- [@juanarias93]: https://github.com/juanarias93
369
- [@kingjan1999]: https://github.com/@kingjan1999
data/README.md CHANGED
@@ -6,7 +6,7 @@ For the current release version see https://github.com/cedarcode/webauthn-ruby/b
6
6
  ![banner](assets/webauthn-ruby.png)
7
7
 
8
8
  [![Gem](https://img.shields.io/gem/v/webauthn.svg?style=flat-square)](https://rubygems.org/gems/webauthn)
9
- [![Travis](https://img.shields.io/travis/cedarcode/webauthn-ruby/master.svg?style=flat-square)](https://travis-ci.com/cedarcode/webauthn-ruby)
9
+ [![Travis](https://img.shields.io/travis/cedarcode/webauthn-ruby/master.svg?style=flat-square)](https://travis-ci.org/cedarcode/webauthn-ruby)
10
10
  [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-informational.svg?style=flat-square)](https://conventionalcommits.org)
11
11
  [![Join the chat at https://gitter.im/cedarcode/webauthn-ruby](https://badges.gitter.im/cedarcode/webauthn-ruby.svg)](https://gitter.im/cedarcode/webauthn-ruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
12
12
 
@@ -408,7 +408,7 @@ credential.authenticator_extension_outputs
408
408
 
409
409
  ## Attestation
410
410
 
411
- ### Attestation Statement Formats
411
+ ### Attestation Statement Format
412
412
 
413
413
  | Attestation Statement Format | Supported? |
414
414
  | -------- | :--------: |
@@ -417,7 +417,6 @@ credential.authenticator_extension_outputs
417
417
  | tpm (x5c attestation) | Yes |
418
418
  | android-key | Yes |
419
419
  | android-safetynet | Yes |
420
- | apple | Yes |
421
420
  | fido-u2f | Yes |
422
421
  | none | Yes |
423
422
 
data/SECURITY.md CHANGED
@@ -4,12 +4,9 @@
4
4
 
5
5
  | Version | Supported |
6
6
  | ------- | ------------------ |
7
- | 2.5.z | :white_check_mark: |
8
- | 2.4.z | :white_check_mark: |
9
- | 2.3.z | :white_check_mark: |
10
- | 2.2.z | :x: |
11
- | 2.1.z | :x: |
12
- | 2.0.z | :x: |
7
+ | 2.2.z | :white_check_mark: |
8
+ | 2.1.z | :white_check_mark: |
9
+ | 2.0.z | :white_check_mark: |
13
10
  | 1.18.z | :white_check_mark: |
14
11
  | < 1.18 | :x: |
15
12
 
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "cose", git: "https://github.com/cedarcode/cose-ruby"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "openssl", "~> 2.0.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "openssl", git: "https://github.com/ruby/openssl"
6
+
7
+ gemspec path: "../"
@@ -40,11 +40,4 @@ end
40
40
  COSE::Algorithm.register(RSAPKCS1Algorithm.new(-257, "RS256", hash_function: "SHA256"))
41
41
  COSE::Algorithm.register(RSAPKCS1Algorithm.new(-258, "RS384", hash_function: "SHA384"))
42
42
  COSE::Algorithm.register(RSAPKCS1Algorithm.new(-259, "RS512", hash_function: "SHA512"))
43
-
44
- # Patch openssl-signature_algorithm gem to support discouraged/deprecated RSA-PKCS#1 with SHA-1
45
- # (RS1 in JOSE/COSE terminology) algorithm needed for WebAuthn.
46
- OpenSSL::SignatureAlgorithm::RSAPKCS1.const_set(
47
- :ACCEPTED_HASH_FUNCTIONS,
48
- OpenSSL::SignatureAlgorithm::RSAPKCS1::ACCEPTED_HASH_FUNCTIONS + ["SHA1"]
49
- )
50
43
  COSE::Algorithm.register(RSAPKCS1Algorithm.new(-65535, "RS1", hash_function: "SHA1"))
@@ -10,18 +10,22 @@ module WebAuthn
10
10
  class AttestationObject
11
11
  extend Forwardable
12
12
 
13
- def self.deserialize(attestation_object)
14
- from_map(CBOR.decode(attestation_object))
13
+ def self.deserialize(attestation_object, relying_party)
14
+ from_map(CBOR.decode(attestation_object), relying_party)
15
15
  end
16
16
 
17
- def self.from_map(map)
17
+ def self.from_map(map, relying_party)
18
18
  new(
19
19
  authenticator_data: WebAuthn::AuthenticatorData.deserialize(map["authData"]),
20
- attestation_statement: WebAuthn::AttestationStatement.from(map["fmt"], map["attStmt"])
20
+ attestation_statement: WebAuthn::AttestationStatement.from(
21
+ map["fmt"],
22
+ map["attStmt"],
23
+ relying_party: relying_party
24
+ )
21
25
  )
22
26
  end
23
27
 
24
- attr_reader :authenticator_data, :attestation_statement
28
+ attr_reader :authenticator_data, :attestation_statement, :relying_party
25
29
 
26
30
  def initialize(authenticator_data:, attestation_statement:)
27
31
  @authenticator_data = authenticator_data
@@ -20,6 +20,10 @@ module WebAuthn
20
20
 
21
21
  private
22
22
 
23
+ def matching_public_key?(authenticator_data)
24
+ attestation_certificate.public_key.to_der == authenticator_data.credential.public_key_object.to_der
25
+ end
26
+
23
27
  def valid_attestation_challenge?(client_data_hash)
24
28
  android_key_attestation.verify_challenge(client_data_hash)
25
29
  rescue AndroidKeyAttestation::ChallengeMismatchError
@@ -16,6 +16,10 @@ module WebAuthn
16
16
  [attestation_type, attestation_trust_path]
17
17
  end
18
18
 
19
+ def attestation_certificate
20
+ attestation_trust_path.first
21
+ end
22
+
19
23
  private
20
24
 
21
25
  def valid_response?(authenticator_data, client_data_hash)
@@ -48,7 +52,7 @@ module WebAuthn
48
52
  end
49
53
 
50
54
  # SafetyNetAttestation returns full chain including root, WebAuthn expects only the x5c certificates
51
- def certificates
55
+ def attestation_trust_path
52
56
  attestation_response.certificate_chain[0..-2]
53
57
  end
54
58
 
@@ -16,20 +16,19 @@ module WebAuthn
16
16
  ATTESTATION_TYPE_SELF = "Self"
17
17
  ATTESTATION_TYPE_ATTCA = "AttCA"
18
18
  ATTESTATION_TYPE_BASIC_OR_ATTCA = "Basic_or_AttCA"
19
- ATTESTATION_TYPE_ANONCA = "AnonCA"
20
19
 
21
20
  ATTESTATION_TYPES_WITH_ROOT = [
22
21
  ATTESTATION_TYPE_BASIC,
23
22
  ATTESTATION_TYPE_BASIC_OR_ATTCA,
24
- ATTESTATION_TYPE_ATTCA,
25
- ATTESTATION_TYPE_ANONCA
23
+ ATTESTATION_TYPE_ATTCA
26
24
  ].freeze
27
25
 
28
26
  class Base
29
27
  AAGUID_EXTENSION_OID = "1.3.6.1.4.1.45724.1.1.4"
30
28
 
31
- def initialize(statement)
29
+ def initialize(statement, relying_party = WebAuthn.configuration.relying_party)
32
30
  @statement = statement
31
+ @relying_party = relying_party
33
32
  end
34
33
 
35
34
  def valid?(_authenticator_data, _client_data_hash)
@@ -44,13 +43,19 @@ module WebAuthn
44
43
  certificates&.first
45
44
  end
46
45
 
46
+ def certificate_chain
47
+ if certificates
48
+ certificates[1..-1]
49
+ end
50
+ end
51
+
47
52
  def attestation_certificate_key_id
48
53
  raw_subject_key_identifier&.unpack("H*")&.[](0)
49
54
  end
50
55
 
51
56
  private
52
57
 
53
- attr_reader :statement
58
+ attr_reader :statement, :relying_party
54
59
 
55
60
  def matching_aaguid?(attested_credential_data_aaguid)
56
61
  extension = attestation_certificate&.extensions&.detect { |ext| ext.oid == AAGUID_EXTENSION_OID }
@@ -64,10 +69,6 @@ module WebAuthn
64
69
  end
65
70
  end
66
71
 
67
- def matching_public_key?(authenticator_data)
68
- attestation_certificate.public_key.to_der == authenticator_data.credential.public_key_object.to_der
69
- end
70
-
71
72
  def certificates
72
73
  @certificates ||=
73
74
  raw_certificates&.map do |raw_certificate|
@@ -95,10 +96,10 @@ module WebAuthn
95
96
 
96
97
  def trustworthy?(aaguid: nil, attestation_certificate_key_id: nil)
97
98
  if ATTESTATION_TYPES_WITH_ROOT.include?(attestation_type)
98
- configuration.acceptable_attestation_types.include?(attestation_type) &&
99
+ relying_party.acceptable_attestation_types.include?(attestation_type) &&
99
100
  valid_certificate_chain?(aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id)
100
101
  else
101
- configuration.acceptable_attestation_types.include?(attestation_type)
102
+ relying_party.acceptable_attestation_types.include?(attestation_type)
102
103
  end
103
104
  end
104
105
 
@@ -122,7 +123,7 @@ module WebAuthn
122
123
 
123
124
  def root_certificates(aaguid: nil, attestation_certificate_key_id: nil)
124
125
  root_certificates =
125
- configuration.attestation_root_certificates_finders.reduce([]) do |certs, finder|
126
+ relying_party.attestation_root_certificates_finders.reduce([]) do |certs, finder|
126
127
  if certs.empty?
127
128
  finder.find(
128
129
  attestation_format: format,
@@ -169,14 +170,10 @@ module WebAuthn
169
170
  def cose_algorithm
170
171
  @cose_algorithm ||=
171
172
  COSE::Algorithm.find(algorithm).tap do |alg|
172
- alg && configuration.algorithms.include?(alg.name) ||
173
+ alg && relying_party.algorithms.include?(alg.name) ||
173
174
  raise(UnsupportedAlgorithm, "Unsupported algorithm #{algorithm}")
174
175
  end
175
176
  end
176
-
177
- def configuration
178
- WebAuthn.configuration
179
- end
180
177
  end
181
178
  end
182
179
  end