jwt 2.5.0 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -23
  3. data/CONTRIBUTING.md +7 -7
  4. data/README.md +125 -47
  5. data/lib/jwt/base64.rb +16 -2
  6. data/lib/jwt/claims_validator.rb +1 -1
  7. data/lib/jwt/configuration/container.rb +14 -3
  8. data/lib/jwt/decode.rb +41 -24
  9. data/lib/jwt/deprecations.rb +29 -0
  10. data/lib/jwt/encode.rb +23 -19
  11. data/lib/jwt/error.rb +1 -0
  12. data/lib/jwt/{algos → jwa}/ecdsa.rb +19 -7
  13. data/lib/jwt/jwa/eddsa.rb +42 -0
  14. data/lib/jwt/jwa/hmac.rb +75 -0
  15. data/lib/jwt/jwa/hmac_rbnacl.rb +50 -0
  16. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +46 -0
  17. data/lib/jwt/{algos → jwa}/none.rb +4 -2
  18. data/lib/jwt/jwa/ps.rb +30 -0
  19. data/lib/jwt/jwa/rsa.rb +25 -0
  20. data/lib/jwt/{algos → jwa}/unsupported.rb +1 -1
  21. data/lib/jwt/jwa/wrapper.rb +26 -0
  22. data/lib/jwt/jwa.rb +62 -0
  23. data/lib/jwt/jwk/ec.rb +168 -116
  24. data/lib/jwt/jwk/hmac.rb +64 -28
  25. data/lib/jwt/jwk/key_base.rb +33 -11
  26. data/lib/jwt/jwk/key_finder.rb +19 -35
  27. data/lib/jwt/jwk/okp_rbnacl.rb +110 -0
  28. data/lib/jwt/jwk/rsa.rb +142 -77
  29. data/lib/jwt/jwk/set.rb +80 -0
  30. data/lib/jwt/jwk.rb +14 -11
  31. data/lib/jwt/verify.rb +8 -4
  32. data/lib/jwt/version.rb +20 -3
  33. data/lib/jwt/x5c_key_finder.rb +0 -3
  34. data/lib/jwt.rb +1 -0
  35. data/ruby-jwt.gemspec +11 -4
  36. metadata +35 -27
  37. data/.codeclimate.yml +0 -8
  38. data/.github/workflows/coverage.yml +0 -27
  39. data/.github/workflows/test.yml +0 -67
  40. data/.gitignore +0 -13
  41. data/.reek.yml +0 -22
  42. data/.rspec +0 -2
  43. data/.rubocop.yml +0 -67
  44. data/.sourcelevel.yml +0 -17
  45. data/Appraisals +0 -13
  46. data/Gemfile +0 -7
  47. data/Rakefile +0 -16
  48. data/lib/jwt/algos/eddsa.rb +0 -35
  49. data/lib/jwt/algos/hmac.rb +0 -36
  50. data/lib/jwt/algos/ps.rb +0 -43
  51. data/lib/jwt/algos/rsa.rb +0 -22
  52. data/lib/jwt/algos.rb +0 -44
  53. data/lib/jwt/security_utils.rb +0 -59
  54. data/lib/jwt/signature.rb +0 -35
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module JWK
5
+ class OKPRbNaCl < KeyBase
6
+ KTY = 'OKP'
7
+ KTYS = [KTY, JWT::JWK::OKPRbNaCl, RbNaCl::Signatures::Ed25519::SigningKey, RbNaCl::Signatures::Ed25519::VerifyKey].freeze
8
+ OKP_PUBLIC_KEY_ELEMENTS = %i[kty n x].freeze
9
+ OKP_PRIVATE_KEY_ELEMENTS = %i[d].freeze
10
+
11
+ def initialize(key, params = nil, options = {})
12
+ params ||= {}
13
+
14
+ # For backwards compatibility when kid was a String
15
+ params = { kid: params } if params.is_a?(String)
16
+
17
+ key_params = extract_key_params(key)
18
+
19
+ params = params.transform_keys(&:to_sym)
20
+ check_jwk_params!(key_params, params)
21
+ super(options, key_params.merge(params))
22
+ end
23
+
24
+ def verify_key
25
+ return @verify_key if defined?(@verify_key)
26
+
27
+ @verify_key = verify_key_from_parameters
28
+ end
29
+
30
+ def signing_key
31
+ return @signing_key if defined?(@signing_key)
32
+
33
+ @signing_key = signing_key_from_parameters
34
+ end
35
+
36
+ def key_digest
37
+ Thumbprint.new(self).to_s
38
+ end
39
+
40
+ def private?
41
+ !signing_key.nil?
42
+ end
43
+
44
+ def members
45
+ OKP_PUBLIC_KEY_ELEMENTS.each_with_object({}) { |i, h| h[i] = self[i] }
46
+ end
47
+
48
+ def export(options = {})
49
+ exported = parameters.clone
50
+ exported.reject! { |k, _| OKP_PRIVATE_KEY_ELEMENTS.include?(k) } unless private? && options[:include_private] == true
51
+ exported
52
+ end
53
+
54
+ private
55
+
56
+ def extract_key_params(key)
57
+ case key
58
+ when JWT::JWK::KeyBase
59
+ key.export(include_private: true)
60
+ when RbNaCl::Signatures::Ed25519::SigningKey
61
+ @signing_key = key
62
+ @verify_key = key.verify_key
63
+ parse_okp_key_params(@verify_key, @signing_key)
64
+ when RbNaCl::Signatures::Ed25519::VerifyKey
65
+ @signing_key = nil
66
+ @verify_key = key
67
+ parse_okp_key_params(@verify_key)
68
+ when Hash
69
+ key.transform_keys(&:to_sym)
70
+ else
71
+ raise ArgumentError, 'key must be of type RbNaCl::Signatures::Ed25519::SigningKey, RbNaCl::Signatures::Ed25519::VerifyKey or Hash with key parameters'
72
+ end
73
+ end
74
+
75
+ def check_jwk_params!(key_params, _given_params)
76
+ raise JWT::JWKError, "Incorrect 'kty' value: #{key_params[:kty]}, expected #{KTY}" unless key_params[:kty] == KTY
77
+ end
78
+
79
+ def parse_okp_key_params(verify_key, signing_key = nil)
80
+ params = {
81
+ kty: KTY,
82
+ crv: 'Ed25519',
83
+ x: ::JWT::Base64.url_encode(verify_key.to_bytes)
84
+ }
85
+
86
+ if signing_key
87
+ params[:d] = ::JWT::Base64.url_encode(signing_key.to_bytes)
88
+ end
89
+
90
+ params
91
+ end
92
+
93
+ def verify_key_from_parameters
94
+ RbNaCl::Signatures::Ed25519::VerifyKey.new(::JWT::Base64.url_decode(self[:x]))
95
+ end
96
+
97
+ def signing_key_from_parameters
98
+ return nil unless self[:d]
99
+
100
+ RbNaCl::Signatures::Ed25519::SigningKey.new(::JWT::Base64.url_decode(self[:d]))
101
+ end
102
+
103
+ class << self
104
+ def import(jwk_data)
105
+ new(jwk_data)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
data/lib/jwt/jwk/rsa.rb CHANGED
@@ -2,44 +2,59 @@
2
2
 
3
3
  module JWT
4
4
  module JWK
5
- class RSA < KeyBase
5
+ class RSA < KeyBase # rubocop:disable Metrics/ClassLength
6
6
  BINARY = 2
7
7
  KTY = 'RSA'
8
- KTYS = [KTY, OpenSSL::PKey::RSA].freeze
9
- RSA_KEY_ELEMENTS = %i[n e d p q dp dq qi].freeze
8
+ KTYS = [KTY, OpenSSL::PKey::RSA, JWT::JWK::RSA].freeze
9
+ RSA_PUBLIC_KEY_ELEMENTS = %i[kty n e].freeze
10
+ RSA_PRIVATE_KEY_ELEMENTS = %i[d p q dp dq qi].freeze
11
+ RSA_KEY_ELEMENTS = (RSA_PRIVATE_KEY_ELEMENTS + RSA_PUBLIC_KEY_ELEMENTS).freeze
10
12
 
11
- attr_reader :keypair
13
+ RSA_OPT_PARAMS = %i[p q dp dq qi].freeze
14
+ RSA_ASN1_SEQUENCE = (%i[n e d] + RSA_OPT_PARAMS).freeze # https://www.rfc-editor.org/rfc/rfc3447#appendix-A.1.2
12
15
 
13
- def initialize(keypair, options = {})
14
- raise ArgumentError, 'keypair must be of type OpenSSL::PKey::RSA' unless keypair.is_a?(OpenSSL::PKey::RSA)
16
+ def initialize(key, params = nil, options = {})
17
+ params ||= {}
15
18
 
16
- @keypair = keypair
19
+ # For backwards compatibility when kid was a String
20
+ params = { kid: params } if params.is_a?(String)
17
21
 
18
- super(options)
22
+ key_params = extract_key_params(key)
23
+
24
+ params = params.transform_keys(&:to_sym)
25
+ check_jwk_params!(key_params, params)
26
+
27
+ super(options, key_params.merge(params))
28
+ end
29
+
30
+ def keypair
31
+ rsa_key
19
32
  end
20
33
 
21
34
  def private?
22
- keypair.private?
35
+ rsa_key.private?
23
36
  end
24
37
 
25
38
  def public_key
26
- keypair.public_key
39
+ rsa_key.public_key
27
40
  end
28
41
 
29
- def export(options = {})
30
- exported_hash = members.merge(kid: kid)
42
+ def signing_key
43
+ rsa_key if private?
44
+ end
31
45
 
32
- return exported_hash unless private? && options[:include_private] == true
46
+ def verify_key
47
+ rsa_key.public_key
48
+ end
33
49
 
34
- append_private_parts(exported_hash)
50
+ def export(options = {})
51
+ exported = parameters.clone
52
+ exported.reject! { |k, _| RSA_PRIVATE_KEY_ELEMENTS.include? k } unless private? && options[:include_private] == true
53
+ exported
35
54
  end
36
55
 
37
56
  def members
38
- {
39
- kty: KTY,
40
- n: encode_open_ssl_bn(public_key.n),
41
- e: encode_open_ssl_bn(public_key.e)
42
- }
57
+ RSA_PUBLIC_KEY_ELEMENTS.each_with_object({}) { |i, h| h[i] = self[i] }
43
58
  end
44
59
 
45
60
  def key_digest
@@ -48,89 +63,139 @@ module JWT
48
63
  OpenSSL::Digest::SHA256.hexdigest(sequence.to_der)
49
64
  end
50
65
 
66
+ def []=(key, value)
67
+ if RSA_KEY_ELEMENTS.include?(key.to_sym)
68
+ raise ArgumentError, 'cannot overwrite cryptographic key attributes'
69
+ end
70
+
71
+ super(key, value)
72
+ end
73
+
51
74
  private
52
75
 
53
- def append_private_parts(the_hash)
54
- the_hash.merge(
55
- d: encode_open_ssl_bn(keypair.d),
56
- p: encode_open_ssl_bn(keypair.p),
57
- q: encode_open_ssl_bn(keypair.q),
58
- dp: encode_open_ssl_bn(keypair.dmp1),
59
- dq: encode_open_ssl_bn(keypair.dmq1),
60
- qi: encode_open_ssl_bn(keypair.iqmp)
61
- )
76
+ def rsa_key
77
+ @rsa_key ||= self.class.create_rsa_key(jwk_attributes(*(RSA_KEY_ELEMENTS - [:kty])))
78
+ end
79
+
80
+ def extract_key_params(key)
81
+ case key
82
+ when JWT::JWK::RSA
83
+ key.export(include_private: true)
84
+ when OpenSSL::PKey::RSA # Accept OpenSSL key as input
85
+ @rsa_key = key # Preserve the object to avoid recreation
86
+ parse_rsa_key(key)
87
+ when Hash
88
+ key.transform_keys(&:to_sym)
89
+ else
90
+ raise ArgumentError, 'key must be of type OpenSSL::PKey::RSA or Hash with key parameters'
91
+ end
92
+ end
93
+
94
+ def check_jwk_params!(key_params, params)
95
+ raise ArgumentError, 'cannot overwrite cryptographic key attributes' unless (RSA_KEY_ELEMENTS & params.keys).empty?
96
+ raise JWT::JWKError, "Incorrect 'kty' value: #{key_params[:kty]}, expected #{KTY}" unless key_params[:kty] == KTY
97
+ raise JWT::JWKError, 'Key format is invalid for RSA' unless key_params[:n] && key_params[:e]
98
+ end
99
+
100
+ def parse_rsa_key(key)
101
+ {
102
+ kty: KTY,
103
+ n: encode_open_ssl_bn(key.n),
104
+ e: encode_open_ssl_bn(key.e),
105
+ d: encode_open_ssl_bn(key.d),
106
+ p: encode_open_ssl_bn(key.p),
107
+ q: encode_open_ssl_bn(key.q),
108
+ dp: encode_open_ssl_bn(key.dmp1),
109
+ dq: encode_open_ssl_bn(key.dmq1),
110
+ qi: encode_open_ssl_bn(key.iqmp)
111
+ }.compact
112
+ end
113
+
114
+ def jwk_attributes(*attributes)
115
+ attributes.each_with_object({}) do |attribute, hash|
116
+ hash[attribute] = decode_open_ssl_bn(self[attribute])
117
+ end
62
118
  end
63
119
 
64
120
  def encode_open_ssl_bn(key_part)
121
+ return unless key_part
122
+
65
123
  ::JWT::Base64.url_encode(key_part.to_s(BINARY))
66
124
  end
67
125
 
126
+ def decode_open_ssl_bn(jwk_data)
127
+ self.class.decode_open_ssl_bn(jwk_data)
128
+ end
129
+
68
130
  class << self
69
131
  def import(jwk_data)
70
- pkey_params = jwk_attributes(jwk_data, *RSA_KEY_ELEMENTS) do |value|
71
- decode_open_ssl_bn(value)
72
- end
73
- new(rsa_pkey(pkey_params), kid: jwk_attributes(jwk_data, :kid)[:kid])
132
+ new(jwk_data)
74
133
  end
75
134
 
76
- private
135
+ def decode_open_ssl_bn(jwk_data)
136
+ return nil unless jwk_data
77
137
 
78
- def jwk_attributes(jwk_data, *attributes)
79
- attributes.each_with_object({}) do |attribute, hash|
80
- value = jwk_data[attribute] || jwk_data[attribute.to_s]
81
- value = yield(value) if block_given?
82
- hash[attribute] = value
83
- end
138
+ OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
84
139
  end
85
140
 
86
- def rsa_pkey(rsa_parameters)
87
- raise JWT::JWKError, 'Key format is invalid for RSA' unless rsa_parameters[:n] && rsa_parameters[:e]
141
+ def create_rsa_key_using_der(rsa_parameters)
142
+ validate_rsa_parameters!(rsa_parameters)
88
143
 
89
- create_rsa_key(rsa_parameters)
90
- end
91
-
92
- if ::JWT.openssl_3?
93
- ASN1_SEQUENCE = %i[n e d p q dp dq qi].freeze
94
- def create_rsa_key(rsa_parameters)
95
- sequence = ASN1_SEQUENCE.each_with_object([]) do |key, arr|
96
- next if rsa_parameters[key].nil?
144
+ sequence = RSA_ASN1_SEQUENCE.each_with_object([]) do |key, arr|
145
+ next if rsa_parameters[key].nil?
97
146
 
98
- arr << OpenSSL::ASN1::Integer.new(rsa_parameters[key])
99
- end
147
+ arr << OpenSSL::ASN1::Integer.new(rsa_parameters[key])
148
+ end
100
149
 
101
- if sequence.size > 2 # For a private key
102
- sequence.unshift(OpenSSL::ASN1::Integer.new(0))
103
- end
150
+ if sequence.size > 2 # Append "two-prime" version for private key
151
+ sequence.unshift(OpenSSL::ASN1::Integer.new(0))
104
152
 
105
- OpenSSL::PKey::RSA.new(OpenSSL::ASN1::Sequence(sequence).to_der)
153
+ raise JWT::JWKError, 'Creating a RSA key with a private key requires the CRT parameters to be defined' if sequence.size < RSA_ASN1_SEQUENCE.size
106
154
  end
107
- elsif OpenSSL::PKey::RSA.new.respond_to?(:set_key)
108
- def create_rsa_key(rsa_parameters)
109
- OpenSSL::PKey::RSA.new.tap do |rsa_key|
110
- rsa_key.set_key(rsa_parameters[:n], rsa_parameters[:e], rsa_parameters[:d])
111
- rsa_key.set_factors(rsa_parameters[:p], rsa_parameters[:q]) if rsa_parameters[:p] && rsa_parameters[:q]
112
- rsa_key.set_crt_params(rsa_parameters[:dp], rsa_parameters[:dq], rsa_parameters[:qi]) if rsa_parameters[:dp] && rsa_parameters[:dq] && rsa_parameters[:qi]
113
- end
155
+
156
+ OpenSSL::PKey::RSA.new(OpenSSL::ASN1::Sequence(sequence).to_der)
157
+ end
158
+
159
+ def create_rsa_key_using_sets(rsa_parameters)
160
+ validate_rsa_parameters!(rsa_parameters)
161
+
162
+ OpenSSL::PKey::RSA.new.tap do |rsa_key|
163
+ rsa_key.set_key(rsa_parameters[:n], rsa_parameters[:e], rsa_parameters[:d])
164
+ rsa_key.set_factors(rsa_parameters[:p], rsa_parameters[:q]) if rsa_parameters[:p] && rsa_parameters[:q]
165
+ rsa_key.set_crt_params(rsa_parameters[:dp], rsa_parameters[:dq], rsa_parameters[:qi]) if rsa_parameters[:dp] && rsa_parameters[:dq] && rsa_parameters[:qi]
114
166
  end
115
- else
116
- def create_rsa_key(rsa_parameters) # rubocop:disable Metrics/AbcSize
117
- OpenSSL::PKey::RSA.new.tap do |rsa_key|
118
- rsa_key.n = rsa_parameters[:n]
119
- rsa_key.e = rsa_parameters[:e]
120
- rsa_key.d = rsa_parameters[:d] if rsa_parameters[:d]
121
- rsa_key.p = rsa_parameters[:p] if rsa_parameters[:p]
122
- rsa_key.q = rsa_parameters[:q] if rsa_parameters[:q]
123
- rsa_key.dmp1 = rsa_parameters[:dp] if rsa_parameters[:dp]
124
- rsa_key.dmq1 = rsa_parameters[:dq] if rsa_parameters[:dq]
125
- rsa_key.iqmp = rsa_parameters[:qi] if rsa_parameters[:qi]
126
- end
167
+ end
168
+
169
+ def create_rsa_key_using_accessors(rsa_parameters) # rubocop:disable Metrics/AbcSize
170
+ validate_rsa_parameters!(rsa_parameters)
171
+
172
+ OpenSSL::PKey::RSA.new.tap do |rsa_key|
173
+ rsa_key.n = rsa_parameters[:n]
174
+ rsa_key.e = rsa_parameters[:e]
175
+ rsa_key.d = rsa_parameters[:d] if rsa_parameters[:d]
176
+ rsa_key.p = rsa_parameters[:p] if rsa_parameters[:p]
177
+ rsa_key.q = rsa_parameters[:q] if rsa_parameters[:q]
178
+ rsa_key.dmp1 = rsa_parameters[:dp] if rsa_parameters[:dp]
179
+ rsa_key.dmq1 = rsa_parameters[:dq] if rsa_parameters[:dq]
180
+ rsa_key.iqmp = rsa_parameters[:qi] if rsa_parameters[:qi]
127
181
  end
128
182
  end
129
183
 
130
- def decode_open_ssl_bn(jwk_data)
131
- return nil unless jwk_data
184
+ def validate_rsa_parameters!(rsa_parameters)
185
+ return unless rsa_parameters.key?(:d)
132
186
 
133
- OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
187
+ parameters = RSA_OPT_PARAMS - rsa_parameters.keys
188
+ return if parameters.empty? || parameters.size == RSA_OPT_PARAMS.size
189
+
190
+ raise JWT::JWKError, 'When one of p, q, dp, dq or qi is given all the other optimization parameters also needs to be defined' # https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.2
191
+ end
192
+
193
+ if ::JWT.openssl_3?
194
+ alias create_rsa_key create_rsa_key_using_der
195
+ elsif OpenSSL::PKey::RSA.new.respond_to?(:set_key)
196
+ alias create_rsa_key create_rsa_key_using_sets
197
+ else
198
+ alias create_rsa_key create_rsa_key_using_accessors
134
199
  end
135
200
  end
136
201
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module JWT
6
+ module JWK
7
+ class Set
8
+ include Enumerable
9
+ extend Forwardable
10
+
11
+ attr_reader :keys
12
+
13
+ def initialize(jwks = nil, options = {}) # rubocop:disable Metrics/CyclomaticComplexity
14
+ jwks ||= {}
15
+
16
+ @keys = case jwks
17
+ when JWT::JWK::Set # Simple duplication
18
+ jwks.keys
19
+ when JWT::JWK::KeyBase # Singleton
20
+ [jwks]
21
+ when Hash
22
+ jwks = jwks.transform_keys(&:to_sym)
23
+ [*jwks[:keys]].map { |k| JWT::JWK.new(k, nil, options) }
24
+ when Array
25
+ jwks.map { |k| JWT::JWK.new(k, nil, options) }
26
+ else
27
+ raise ArgumentError, 'Can only create new JWKS from Hash, Array and JWK'
28
+ end
29
+ end
30
+
31
+ def export(options = {})
32
+ { keys: @keys.map { |k| k.export(options) } }
33
+ end
34
+
35
+ def_delegators :@keys, :each, :size, :delete, :dig
36
+
37
+ def select!(&block)
38
+ return @keys.select! unless block
39
+
40
+ self if @keys.select!(&block)
41
+ end
42
+
43
+ def reject!(&block)
44
+ return @keys.reject! unless block
45
+
46
+ self if @keys.reject!(&block)
47
+ end
48
+
49
+ def uniq!(&block)
50
+ self if @keys.uniq!(&block)
51
+ end
52
+
53
+ def merge(enum)
54
+ @keys += JWT::JWK::Set.new(enum.to_a).keys
55
+ self
56
+ end
57
+
58
+ def union(enum)
59
+ dup.merge(enum)
60
+ end
61
+
62
+ def add(key)
63
+ @keys << JWT::JWK.new(key)
64
+ self
65
+ end
66
+
67
+ def ==(other)
68
+ other.is_a?(JWT::JWK::Set) && keys.sort == other.keys.sort
69
+ end
70
+
71
+ alias eql? ==
72
+ alias filter! select!
73
+ alias length size
74
+ # For symbolic manipulation
75
+ alias | union
76
+ alias + union
77
+ alias << add
78
+ end
79
+ end
80
+ end
data/lib/jwt/jwk.rb CHANGED
@@ -1,23 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'jwk/key_finder'
4
+ require_relative 'jwk/set'
4
5
 
5
6
  module JWT
6
7
  module JWK
7
8
  class << self
8
- def import(jwk_data)
9
- jwk_kty = jwk_data[:kty] || jwk_data['kty']
10
- raise JWT::JWKError, 'Key type (kty) not provided' unless jwk_kty
11
-
12
- mappings.fetch(jwk_kty.to_s) do |kty|
13
- raise JWT::JWKError, "Key type #{kty} not supported"
14
- end.import(jwk_data)
15
- end
9
+ def create_from(key, params = nil, options = {})
10
+ if key.is_a?(Hash)
11
+ jwk_kty = key[:kty] || key['kty']
12
+ raise JWT::JWKError, 'Key type (kty) not provided' unless jwk_kty
13
+
14
+ return mappings.fetch(jwk_kty.to_s) do |kty|
15
+ raise JWT::JWKError, "Key type #{kty} not supported"
16
+ end.new(key, params, options)
17
+ end
16
18
 
17
- def create_from(keypair, kid = nil)
18
- mappings.fetch(keypair.class) do |klass|
19
+ mappings.fetch(key.class) do |klass|
19
20
  raise JWT::JWKError, "Cannot create JWK from a #{klass.name}"
20
- end.new(keypair, kid)
21
+ end.new(key, params, options)
21
22
  end
22
23
 
23
24
  def classes
@@ -26,6 +27,7 @@ module JWT
26
27
  end
27
28
 
28
29
  alias new create_from
30
+ alias import create_from
29
31
 
30
32
  private
31
33
 
@@ -50,3 +52,4 @@ require_relative 'jwk/key_base'
50
52
  require_relative 'jwk/ec'
51
53
  require_relative 'jwk/rsa'
52
54
  require_relative 'jwk/hmac'
55
+ require_relative 'jwk/okp_rbnacl' if JWT.rbnacl?
data/lib/jwt/verify.rb CHANGED
@@ -38,12 +38,12 @@ module JWT
38
38
  end
39
39
 
40
40
  def verify_expiration
41
- return unless @payload.include?('exp')
41
+ return unless contains_key?(@payload, 'exp')
42
42
  raise(JWT::ExpiredSignature, 'Signature has expired') if @payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
43
43
  end
44
44
 
45
45
  def verify_iat
46
- return unless @payload.include?('iat')
46
+ return unless contains_key?(@payload, 'iat')
47
47
 
48
48
  iat = @payload['iat']
49
49
  raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(Numeric) || iat.to_f > Time.now.to_f
@@ -77,7 +77,7 @@ module JWT
77
77
  end
78
78
 
79
79
  def verify_not_before
80
- return unless @payload.include?('nbf')
80
+ return unless contains_key?(@payload, 'nbf')
81
81
  raise(JWT::ImmatureSignature, 'Signature nbf has not been reached') if @payload['nbf'].to_i > (Time.now.to_i + nbf_leeway)
82
82
  end
83
83
 
@@ -92,7 +92,7 @@ module JWT
92
92
  return unless (options_required_claims = @options[:required_claims])
93
93
 
94
94
  options_required_claims.each do |required_claim|
95
- raise(JWT::MissingRequiredClaim, "Missing required claim #{required_claim}") unless @payload.include?(required_claim)
95
+ raise(JWT::MissingRequiredClaim, "Missing required claim #{required_claim}") unless contains_key?(@payload, required_claim)
96
96
  end
97
97
  end
98
98
 
@@ -109,5 +109,9 @@ module JWT
109
109
  def nbf_leeway
110
110
  @options[:nbf_leeway] || global_leeway
111
111
  end
112
+
113
+ def contains_key?(payload, key)
114
+ payload.respond_to?(:key?) && payload.key?(key)
115
+ end
112
116
  end
113
117
  end
data/lib/jwt/version.rb CHANGED
@@ -11,9 +11,9 @@ module JWT
11
11
  # major version
12
12
  MAJOR = 2
13
13
  # minor version
14
- MINOR = 5
14
+ MINOR = 8
15
15
  # tiny version
16
- TINY = 0
16
+ TINY = 1
17
17
  # alpha, beta, etc. tag
18
18
  PRE = nil
19
19
 
@@ -23,6 +23,23 @@ module JWT
23
23
 
24
24
  def self.openssl_3?
25
25
  return false if OpenSSL::OPENSSL_VERSION.include?('LibreSSL')
26
- return true if OpenSSL::OPENSSL_VERSION_NUMBER >= 3 * 0x10000000
26
+
27
+ true if 3 * 0x10000000 <= OpenSSL::OPENSSL_VERSION_NUMBER
28
+ end
29
+
30
+ def self.rbnacl?
31
+ defined?(::RbNaCl)
32
+ end
33
+
34
+ def self.rbnacl_6_or_greater?
35
+ rbnacl? && ::Gem::Version.new(::RbNaCl::VERSION) >= ::Gem::Version.new('6.0.0')
36
+ end
37
+
38
+ def self.openssl_3_hmac_empty_key_regression?
39
+ openssl_3? && openssl_version <= ::Gem::Version.new('3.0.0')
40
+ end
41
+
42
+ def self.openssl_version
43
+ @openssl_version ||= ::Gem::Version.new(OpenSSL::VERSION)
27
44
  end
28
45
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
- require 'jwt/error'
5
-
6
3
  module JWT
7
4
  # If the x5c header certificate chain can be validated by trusted root
8
5
  # certificates, and none of the certificates are revoked, returns the public
data/lib/jwt.rb CHANGED
@@ -5,6 +5,7 @@ require 'jwt/base64'
5
5
  require 'jwt/json'
6
6
  require 'jwt/decode'
7
7
  require 'jwt/configuration'
8
+ require 'jwt/deprecations'
8
9
  require 'jwt/encode'
9
10
  require 'jwt/error'
10
11
  require 'jwt/jwk'
data/ruby-jwt.gemspec CHANGED
@@ -18,18 +18,25 @@ Gem::Specification.new do |spec|
18
18
  spec.required_ruby_version = '>= 2.5'
19
19
  spec.metadata = {
20
20
  'bug_tracker_uri' => 'https://github.com/jwt/ruby-jwt/issues',
21
- 'changelog_uri' => "https://github.com/jwt/ruby-jwt/blob/v#{JWT.gem_version}/CHANGELOG.md"
21
+ 'changelog_uri' => "https://github.com/jwt/ruby-jwt/blob/v#{JWT.gem_version}/CHANGELOG.md",
22
+ 'rubygems_mfa_required' => 'true'
22
23
  }
23
24
 
24
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|gemfiles|coverage|bin)/}) }
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(spec|gemfiles|coverage|bin)/}) || # Irrelevant folders
27
+ f.match(/^\.+/) || # Files and folders starting with .
28
+ f.match(/^(Appraisals|Gemfile|Rakefile)$/) # Irrelevant files
29
+ end
30
+
25
31
  spec.executables = []
26
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
27
32
  spec.require_paths = %w[lib]
28
33
 
34
+ spec.add_dependency 'base64'
35
+
29
36
  spec.add_development_dependency 'appraisal'
30
37
  spec.add_development_dependency 'bundler'
31
38
  spec.add_development_dependency 'rake'
32
- spec.add_development_dependency 'reek'
33
39
  spec.add_development_dependency 'rspec'
40
+ spec.add_development_dependency 'rubocop'
34
41
  spec.add_development_dependency 'simplecov'
35
42
  end