webauthn 0.2.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5909a9f479ed26b128466cf3843acb514a669e81f0d751d88ff2c5a5cd0a9c7c
4
- data.tar.gz: d03b48fa85cf79bae08461ca31635d3b02f21f421ae4fe6ce1f2257e1c50a1f3
3
+ metadata.gz: 004107dfab888cb866245f75d634764ffb859974f12e5d70b30304a6d8c25ce1
4
+ data.tar.gz: 2cdfacf2e938a030f494717d644ae394f1f55a759e5eff125face25f7eda16d7
5
5
  SHA512:
6
- metadata.gz: 232ffa7b3009a5c955711d50fcd16f90f60b8ddc2034f5c626090e42c50175cae27217cad53abf1c09fec8448d3fb507cfb93ca2ed2377ed273084f61ddd010a
7
- data.tar.gz: bf6633710657c45f5ef153eaa234fc278f533b2b429f9c475e366194d65e8d06a321c8ef33e01c79d5bf0170fe19c19f60a77d7298912df6cff629eb5dee1c3c
6
+ metadata.gz: c3080d2ca2560908ff75ab0a100226c9526e3909cdbadc6d166050fbea710b0f48f52f56ea315ff7e2e98d4a877ad5da1e85a12cb9f3f57784b99f5a8416e76a
7
+ data.tar.gz: e1d08103e408ec0e9b4561d1b24b886a013392ddf13044cf62b13046ea8754aec2ff184f75d48ec1710d4c53bb841f87289e8bbe5c2d5c496f40b683a1b353be
data/.rubocop.yml CHANGED
@@ -28,8 +28,164 @@ Performance:
28
28
  Security:
29
29
  Enabled: true
30
30
 
31
+ Style/BlockComments:
32
+ Enabled: true
33
+
34
+ Style/BracesAroundHashParameters:
35
+ Enabled: true
36
+
37
+ Style/CaseEquality:
38
+ Enabled: true
39
+
40
+ Style/ClassAndModuleChildren:
41
+ Enabled: true
42
+
43
+ Style/ClassMethods:
44
+ Enabled: true
45
+
46
+ Style/ClassVars:
47
+ Enabled: true
48
+
49
+ Style/CommentAnnotation:
50
+ Enabled: true
51
+
52
+ Style/ConditionalAssignment:
53
+ Enabled: true
54
+
55
+ Style/DefWithParentheses:
56
+ Enabled: true
57
+
58
+ Style/Dir:
59
+ Enabled: true
60
+
61
+ Style/EachForSimpleLoop:
62
+ Enabled: true
63
+
64
+ Style/EachWithObject:
65
+ Enabled: true
66
+
67
+ Style/EmptyBlockParameter:
68
+ Enabled: true
69
+
70
+ Style/EmptyCaseCondition:
71
+ Enabled: true
72
+
73
+ Style/EmptyElse:
74
+ Enabled: true
75
+
76
+ Style/EmptyLambdaParameter:
77
+ Enabled: true
78
+
79
+ Style/EmptyLiteral:
80
+ Enabled: true
81
+
82
+ Style/EvenOdd:
83
+ Enabled: true
84
+
85
+ Style/ExpandPathArguments:
86
+ Enabled: true
87
+
88
+ Style/For:
89
+ Enabled: true
90
+
31
91
  Style/FrozenStringLiteralComment:
32
92
  Enabled: true
33
93
 
94
+ Style/GlobalVars:
95
+ Enabled: true
96
+
97
+ Style/HashSyntax:
98
+ Enabled: true
99
+
100
+ Style/IdenticalConditionalBranches:
101
+ Enabled: true
102
+
103
+ Style/IfInsideElse:
104
+ Enabled: true
105
+
106
+ Style/InverseMethods:
107
+ Enabled: true
108
+
109
+ Style/MethodCallWithoutArgsParentheses:
110
+ Enabled: true
111
+
112
+ Style/MethodDefParentheses:
113
+ Enabled: true
114
+
115
+ Style/MultilineMemoization:
116
+ Enabled: true
117
+
118
+ Style/MutableConstant:
119
+ Enabled: true
120
+
121
+ Style/NestedParenthesizedCalls:
122
+ Enabled: true
123
+
124
+ Style/OptionalArguments:
125
+ Enabled: true
126
+
127
+ Style/ParenthesesAroundCondition:
128
+ Enabled: true
129
+
130
+ Style/RedundantBegin:
131
+ Enabled: true
132
+
133
+ Style/RedundantConditional:
134
+ Enabled: true
135
+
136
+ Style/RedundantException:
137
+ Enabled: true
138
+
34
139
  Style/RedundantFreeze:
35
140
  Enabled: true
141
+
142
+ Style/RedundantParentheses:
143
+ Enabled: true
144
+
145
+ Style/RedundantReturn:
146
+ Enabled: true
147
+
148
+ Style/RedundantSelf:
149
+ Enabled: true
150
+
151
+ Style/Semicolon:
152
+ Enabled: true
153
+
154
+ Style/SingleLineMethods:
155
+ Enabled: true
156
+
157
+ Style/SpecialGlobalVars:
158
+ Enabled: true
159
+
160
+ Style/SymbolLiteral:
161
+ Enabled: true
162
+
163
+ Style/TrailingBodyOnClass:
164
+ Enabled: true
165
+
166
+ Style/TrailingBodyOnMethodDefinition:
167
+ Enabled: true
168
+
169
+ Style/TrailingBodyOnModule:
170
+ Enabled: true
171
+
172
+ Style/TrailingMethodEndStatement:
173
+ Enabled: true
174
+
175
+ Style/TrivialAccessors:
176
+ Enabled: true
177
+
178
+ Style/UnneededInterpolation:
179
+ Enabled: true
180
+
181
+ Style/UnneededPercentQ:
182
+ Enabled: true
183
+
184
+ Style/UnpackFirst:
185
+ Enabled: true
186
+
187
+ Style/YodaCondition:
188
+ Enabled: true
189
+
190
+ Style/ZeroLengthPredicate:
191
+ Enabled: true
data/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [v1.0.0] - 2018-09-07
4
+
5
+ ### Added
6
+
7
+ - _Authentication_ ceremony
8
+ - Support multiple credentials per user by letting `WebAuthn::AuthenticatorAssertionResponse.valid?` receive multiple allowed credentials
9
+
10
+ ### Changed
11
+
12
+ - _Registration_ ceremony
13
+ - Use 32-byte challenge instead of 16-byte
14
+ - _Authentication_ ceremony
15
+ - Use 32-byte challenge instead of 16-byte
4
16
 
5
17
  ## [v0.2.0] - 2018-06-08
6
18
 
@@ -39,6 +51,6 @@
39
51
  - `WebAuthn::AuthenticatorAttestationResponse.valid?` can be used to validate fido-u2f attestations returned by the browser
40
52
  - Works with ruby 2.5
41
53
 
42
- [Unreleased]: https://github.com/cedarcode/webauthn-ruby/compare/v0.2.0...HEAD/
54
+ [v1.0.0]: https://github.com/cedarcode/webauthn-ruby/compare/v0.2.0...v1.0.0/
43
55
  [v0.2.0]: https://github.com/cedarcode/webauthn-ruby/compare/v0.1.0...v0.2.0/
44
56
  [v0.1.0]: https://github.com/cedarcode/webauthn-ruby/compare/v0.0.0...v0.1.0/
data/README.md CHANGED
@@ -1,12 +1,10 @@
1
1
  # WebAuthn :key:
2
2
 
3
- Easily implement WebAuthn in your ruby web server
3
+ Easily implement WebAuthn in your ruby/rails app
4
4
 
5
5
  [![Gem](https://img.shields.io/gem/v/webauthn.svg?style=flat-square)](https://rubygems.org/gems/webauthn)
6
6
  [![Travis](https://img.shields.io/travis/cedarcode/webauthn-ruby.svg?style=flat-square)](https://travis-ci.org/cedarcode/webauthn-ruby)
7
7
 
8
- ## WARNING: This gem is in the early development phase. Use on production at your own risk.
9
-
10
8
  ## What is WebAuthn?
11
9
 
12
10
  - [WebAuthn article with Google IO 2018 talk](https://developers.google.com/web/updates/2018/05/webauthn)
@@ -16,7 +14,7 @@ Easily implement WebAuthn in your ruby web server
16
14
 
17
15
  ## Prerequisites
18
16
 
19
- This gem will help your ruby server act as a conforming [_Relying-Party_](https://www.w3.org/TR/webauthn/#relying-party), in WebAuthn terminology. But for the [_Registration_](https://www.w3.org/TR/webauthn/#registration) and [_Authentiction_](https://www.w3.org/TR/webauthn/#authentication) ceremonies to work, you will also need
17
+ This gem will help your ruby server act as a conforming [_Relying-Party_](https://www.w3.org/TR/webauthn/#relying-party), in WebAuthn terminology. But for the [_Registration_](https://www.w3.org/TR/webauthn/#registration) and [_Authentication_](https://www.w3.org/TR/webauthn/#authentication) ceremonies to work, you will also need
20
18
 
21
19
  ### A conforming User Agent
22
20
 
@@ -153,6 +151,24 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
153
151
 
154
152
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
155
153
 
154
+ ### Commit message format
155
+
156
+ Each commit message follows the `<type>: <message>` format.
157
+
158
+ The "message" starts with lowercase and the "type" is one of:
159
+
160
+ * __build__: Changes that affect the build system or external dependencies
161
+ * __ci__: Changes to the CI configuration files and scripts
162
+ * __docs__: Documentation only changes
163
+ * __feat__: A new feature
164
+ * __fix__: A bug fix
165
+ * __perf__: A code change that improves performance
166
+ * __refactor__: A code change that neither fixes a bug nor adds a feature
167
+ * __style__: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
168
+ * __test__: Adding missing tests or correcting existing tests
169
+
170
+ Inspired in a subset of [Angular's Commit Message Guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines).
171
+
156
172
  ## Contributing
157
173
 
158
174
  Bug reports and pull requests are welcome on GitHub at https://github.com/cedarcode/webauthn-ruby.
data/Rakefile CHANGED
@@ -7,4 +7,4 @@ require "rubocop/rake_task"
7
7
  RSpec::Core::RakeTask.new(:spec)
8
8
  RuboCop::RakeTask.new
9
9
 
10
- task :default => [:rubocop, :spec]
10
+ task default: [:rubocop, :spec]
data/lib/cose/ecdsa.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module COSE
4
+ module ECDSA
5
+ ALG_ES256 = -7
6
+ end
7
+ end
data/lib/webauthn.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "cose/ecdsa"
3
4
  require "webauthn/authenticator_attestation_response"
4
5
  require "webauthn/authenticator_assertion_response"
5
- require "webauthn/cose/ecdsa"
6
6
  require "webauthn/utils"
7
7
  require "webauthn/version"
8
8
 
@@ -34,7 +34,7 @@ module WebAuthn
34
34
  end
35
35
 
36
36
  def self.challenge
37
- SecureRandom.random_bytes(16)
37
+ SecureRandom.random_bytes(32)
38
38
  end
39
39
  private_class_method :challenge
40
40
  end
@@ -12,10 +12,10 @@ module WebAuthn
12
12
  @signature = signature
13
13
  end
14
14
 
15
- def valid?(original_challenge, original_origin, allowed_credential:)
15
+ def valid?(original_challenge, original_origin, allowed_credentials:)
16
16
  super(original_challenge, original_origin) &&
17
- valid_credential?(allowed_credential[:id]) &&
18
- valid_signature?(allowed_credential[:public_key])
17
+ valid_credential?(allowed_credentials) &&
18
+ valid_signature?(credential_public_key(allowed_credentials))
19
19
  end
20
20
 
21
21
  private
@@ -36,14 +36,24 @@ module WebAuthn
36
36
  )
37
37
  end
38
38
 
39
- def valid_credential?(allowed_credential_id)
40
- allowed_credential_id == credential_id
39
+ def valid_credential?(allowed_credentials)
40
+ allowed_credential_ids = allowed_credentials.map { |credential| credential[:id] }
41
+
42
+ allowed_credential_ids.include?(credential_id)
41
43
  end
42
44
 
43
45
  def authenticator_data
44
46
  @authenticator_data ||= WebAuthn::AuthenticatorData.new(authenticator_data_bytes)
45
47
  end
46
48
 
49
+ def credential_public_key(allowed_credentials)
50
+ matched_credential = allowed_credentials.find do |credential|
51
+ credential[:id] == credential_id
52
+ end
53
+
54
+ matched_credential[:public_key]
55
+ end
56
+
47
57
  def type
48
58
  WebAuthn::TYPES[:get]
49
59
  end
@@ -58,7 +58,7 @@ module WebAuthn
58
58
  end
59
59
 
60
60
  def flags
61
- @flags ||= data_at(flags_position, FLAGS_LENGTH).unpack("b*").first
61
+ @flags ||= data_at(flags_position, FLAGS_LENGTH).unpack1("b*")
62
62
  end
63
63
 
64
64
  def flags_position
@@ -48,10 +48,7 @@ module WebAuthn
48
48
  end
49
49
 
50
50
  def id_length
51
- @id_length ||=
52
- data_at(id_length_position, ID_LENGTH_LENGTH)
53
- .unpack(UINT16_BIG_ENDIAN_FORMAT)
54
- .first
51
+ @id_length ||= data_at(id_length_position, ID_LENGTH_LENGTH).unpack1(UINT16_BIG_ENDIAN_FORMAT)
55
52
  end
56
53
 
57
54
  def id_length_position
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "webauthn/cose/ecdsa"
4
- require "webauthn/cose/key/ec2"
3
+ require "cose/ecdsa"
4
+ require "cose/key/ec2"
5
5
 
6
6
  module WebAuthn
7
7
  class AuthenticatorData
@@ -17,7 +17,7 @@ module WebAuthn
17
17
  data.size >= COORDINATE_LENGTH * 2 &&
18
18
  cose_key.x_coordinate.length == COORDINATE_LENGTH &&
19
19
  cose_key.y_coordinate.length == COORDINATE_LENGTH &&
20
- cose_key.algorithm == WebAuthn::COSE::ECDSA::ALG_ES256
20
+ cose_key.algorithm == COSE::ECDSA::ALG_ES256
21
21
  end
22
22
 
23
23
  def to_str
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebAuthn
4
- VERSION = "0.2.0"
4
+ VERSION = "1.0.0"
5
5
  end
data/webauthn.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "webauthn/version"
6
6
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ["Gonzalo Rodriguez", "Braulio Martinez"]
11
11
  spec.email = ["gonzalo@cedarcode.com", "braulio@cedarcode.com"]
12
12
 
13
- spec.summary = %q{Web Authentication API Relying Party in ruby}
13
+ spec.summary = "WebAuthn in ruby ― Ruby implementation of a WebAuthn Relying Party"
14
14
  spec.homepage = "https://github.com/cedarcode/webauthn-ruby"
15
15
  spec.license = "MIT"
16
16
 
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ["lib"]
29
29
 
30
30
  spec.add_dependency "cbor", "~> 0.5.9.2"
31
+ spec.add_dependency "cose", "~> 0.1.0"
31
32
 
32
33
  spec.add_development_dependency "bundler", "~> 1.16"
33
34
  spec.add_development_dependency "byebug", "~> 10.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: 0.2.0
4
+ version: 1.0.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: 2018-06-08 00:00:00.000000000 Z
12
+ date: 2018-09-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: cbor
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: 0.5.9.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: cose
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.1.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.1.0
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: bundler
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +128,7 @@ files:
114
128
  - Rakefile
115
129
  - bin/console
116
130
  - bin/setup
131
+ - lib/cose/ecdsa.rb
117
132
  - lib/webauthn.rb
118
133
  - lib/webauthn/attestation_statement.rb
119
134
  - lib/webauthn/attestation_statement/base.rb
@@ -126,8 +141,6 @@ files:
126
141
  - lib/webauthn/authenticator_data/attested_credential_data/public_key_u2f.rb
127
142
  - lib/webauthn/authenticator_response.rb
128
143
  - lib/webauthn/client_data.rb
129
- - lib/webauthn/cose/ecdsa.rb
130
- - lib/webauthn/cose/key/ec2.rb
131
144
  - lib/webauthn/utils.rb
132
145
  - lib/webauthn/version.rb
133
146
  - webauthn.gemspec
@@ -157,5 +170,5 @@ rubyforge_project:
157
170
  rubygems_version: 2.7.7
158
171
  signing_key:
159
172
  specification_version: 4
160
- summary: Web Authentication API Relying Party in ruby
173
+ summary: WebAuthn in ruby ― Ruby implementation of a WebAuthn Relying Party
161
174
  test_files: []
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module WebAuthn
4
- module COSE
5
- module ECDSA
6
- ALG_ES256 = -7
7
- end
8
- end
9
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module WebAuthn
4
- # COSE module has the potential to being extracted out of WebAuthn
5
- module COSE
6
- module Key
7
- class EC2
8
- KTY_LABEL = 1
9
- ALG_LABEL = 3
10
-
11
- CRV_LABEL = -1
12
- X_LABEL = -2
13
- Y_LABEL = -3
14
-
15
- KTY_EC2 = 2
16
-
17
- attr_reader :algorithm, :curve, :x_coordinate, :y_coordinate
18
-
19
- def initialize(algorithm: nil, curve:, x_coordinate:, y_coordinate:)
20
- if !curve
21
- raise ArgumentError, "Required curve is missing"
22
- elsif !x_coordinate
23
- raise ArgumentError, "Required x-coordinate is missing"
24
- elsif !y_coordinate
25
- raise ArgumentError, "Required y-coordinate is missing"
26
- else
27
- @algorithm = algorithm
28
- @curve = curve
29
- @x_coordinate = x_coordinate
30
- @y_coordinate = y_coordinate
31
- end
32
- end
33
-
34
- def self.from_map(map)
35
- if map[KTY_LABEL] == KTY_EC2
36
- new(
37
- algorithm: map[ALG_LABEL],
38
- curve: map[CRV_LABEL],
39
- x_coordinate: map[X_LABEL],
40
- y_coordinate: map[Y_LABEL]
41
- )
42
- else
43
- raise "Not an EC2 key"
44
- end
45
- end
46
-
47
- def self.from_cbor(cbor)
48
- from_map(CBOR.decode(cbor))
49
- end
50
- end
51
- end
52
- end
53
- end