jwt 2.7.1 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11007e8ec36d148026cd5b6761681b0a71437b7b461efba4ae492622fc5ff27b
4
- data.tar.gz: 8090bbba3dce57e42cc203ef168d3d00c624e79e076de0e949b4390b531b4d55
3
+ metadata.gz: af3792b982f014801d3ff7ae3410be6bd1e0b27f199c500942eb86267bb2b764
4
+ data.tar.gz: c37fc4b72cf3819210b15164548eff44579de1e8f8c48d9ce35864a84f007d9a
5
5
  SHA512:
6
- metadata.gz: a035f44be760ad325105329cbaec12e90515afdade9649e798ee5c7cefced3271d4f3084afeef693c03c961541d9e5e45b2876734d1947dccdd24cc194acbca2
7
- data.tar.gz: 61e3d071ce44809767f3501f12b452cae574f9ea0de668ca937731838d58e2a9fa85831829ce564f6a015177a86b2a05b1b6ddf60ba34ac515ea12c69ac618fe
6
+ metadata.gz: 3f61fd13a1d56c657691abb4bfde3671a7d93b5c853785b804c0d119d27f4641685828eb9c65a8e4856487782530e791ecbce5f1a5f1cb61c883daff97a44367
7
+ data.tar.gz: ef36aa81991e28cb9d3df65f1ebbeb6599eb99dee8e1953aff1a6231e91c6a3f9633e3afd22fbbc6c09f6318c4e516ee7a908428eee303f3952fed890395b267
data/CHANGELOG.md CHANGED
@@ -1,13 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.8.1](https://github.com/jwt/ruby-jwt/tree/v2.8.1) (2024-02-29)
4
+
5
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.0...v2.8.1)
6
+
7
+ **Features:**
8
+
9
+ - Configurable base64 decode behaviour [#589](https://github.com/jwt/ruby-jwt/pull/589) ([@anakinj](https://github.com/anakinj))
10
+
11
+ **Fixes and enhancements:**
12
+
13
+ - Output deprecation warnings once [#589](https://github.com/jwt/ruby-jwt/pull/589) ([@anakinj](https://github.com/anakinj))
14
+
15
+ ## [v2.8.0](https://github.com/jwt/ruby-jwt/tree/v2.8.0) (2024-02-17)
16
+
17
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.1...v2.8.0)
18
+
19
+ **Features:**
20
+
21
+ - Updated rubocop to 1.56 [#573](https://github.com/jwt/ruby-jwt/pull/573) ([@anakinj](https://github.com/anakinj))
22
+ - Run CI on Ruby 3.3 [#577](https://github.com/jwt/ruby-jwt/pull/577) ([@anakinj](https://github.com/anakinj))
23
+ - Deprecation warning added for the HMAC algorithm HS512256 (HMAC-SHA-512 truncated to 256-bits) [#575](https://github.com/jwt/ruby-jwt/pull/575) ([@anakinj](https://github.com/anakinj))
24
+ - Stop using RbNaCl for standard HMAC algorithms [#575](https://github.com/jwt/ruby-jwt/pull/575) ([@anakinj](https://github.com/anakinj))
25
+
26
+ **Fixes and enhancements:**
27
+
28
+ - Fix signature has expired error if payload is a string [#555](https://github.com/jwt/ruby-jwt/pull/555) ([@GobinathAL](https://github.com/GobinathAL))
29
+ - Fix key base equality and spaceship operators [#569](https://github.com/jwt/ruby-jwt/pull/569) ([@magneland](https://github.com/magneland))
30
+ - Remove explicit base64 require from x5c_key_finder [#580](https://github.com/jwt/ruby-jwt/pull/580) ([@anakinj](https://github.com/anakinj))
31
+ - Performance improvements and cleanup of tests [#581](https://github.com/jwt/ruby-jwt/pull/581) ([@anakinj](https://github.com/anakinj))
32
+ - Repair EC x/y coordinates when importing JWK [#585](https://github.com/jwt/ruby-jwt/pull/585) ([@julik](https://github.com/julik))
33
+ - Explicit dependency to the base64 gem [#582](https://github.com/jwt/ruby-jwt/pull/582) ([@anakinj](https://github.com/anakinj))
34
+ - Deprecation warning for decoding content not compliant with RFC 4648 [#582](https://github.com/jwt/ruby-jwt/pull/582) ([@anakinj](https://github.com/anakinj))
35
+ - Algorithms moved under the `::JWT::JWA` module ([@anakinj](https://github.com/anakinj))
36
+
3
37
  ## [v2.7.1](https://github.com/jwt/ruby-jwt/tree/v2.8.0) (2023-06-09)
4
38
 
5
- [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.0...v2.8.0)
39
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.0...v2.7.1)
6
40
 
7
41
  **Fixes and enhancements:**
8
42
 
9
- - Handle invalid algorithm when decoding JWT [#559](https://github.com/jwt/ruby-jwt/pull/559) - [@nataliastanko](https://github.com/nataliastanko)
10
- - Do not raise error when verifying bad HMAC signature [#563](https://github.com/jwt/ruby-jwt/pull/563) - [@hieuk09](https://github.com/hieuk09)
43
+ - Handle invalid algorithm when decoding JWT [#559](https://github.com/jwt/ruby-jwt/pull/559) ([@nataliastanko](https://github.com/nataliastanko))
44
+ - Do not raise error when verifying bad HMAC signature [#563](https://github.com/jwt/ruby-jwt/pull/563) ([@hieuk09](https://github.com/hieuk09))
11
45
 
12
46
  ## [v2.7.0](https://github.com/jwt/ruby-jwt/tree/v2.7.0) (2023-02-01)
13
47
 
@@ -29,14 +63,14 @@
29
63
 
30
64
  **Features:**
31
65
 
32
- - Support custom algorithms by passing algorithm objects[#512](https://github.com/jwt/ruby-jwt/pull/512) ([@anakinj](https://github.com/anakinj)).
33
- - Support descriptive (not key related) JWK parameters[#520](https://github.com/jwt/ruby-jwt/pull/520) ([@bellebaum](https://github.com/bellebaum)).
34
- - Support for JSON Web Key Sets[#525](https://github.com/jwt/ruby-jwt/pull/525) ([@bellebaum](https://github.com/bellebaum)).
35
- - Support HMAC keys over 32 chars when using RbNaCl[#521](https://github.com/jwt/ruby-jwt/pull/521) ([@anakinj](https://github.com/anakinj)).
66
+ - Support custom algorithms by passing algorithm objects [#512](https://github.com/jwt/ruby-jwt/pull/512) ([@anakinj](https://github.com/anakinj))
67
+ - Support descriptive (not key related) JWK parameters [#520](https://github.com/jwt/ruby-jwt/pull/520) ([@bellebaum](https://github.com/bellebaum))
68
+ - Support for JSON Web Key Sets [#525](https://github.com/jwt/ruby-jwt/pull/525) ([@bellebaum](https://github.com/bellebaum))
69
+ - Support HMAC keys over 32 chars when using RbNaCl [#521](https://github.com/jwt/ruby-jwt/pull/521) ([@anakinj](https://github.com/anakinj))
36
70
 
37
71
  **Fixes and enhancements:**
38
72
 
39
- - Raise descriptive error on empty hmac_secret and OpenSSL 3.0/openssl gem <3.0.1 [#530](https://github.com/jwt/ruby-jwt/pull/530) ([@jonmchan](https://github.com/jonmchan)).
73
+ - Raise descriptive error on empty hmac_secret and OpenSSL 3.0/openssl gem <3.0.1 [#530](https://github.com/jwt/ruby-jwt/pull/530) ([@jonmchan](https://github.com/jonmchan))
40
74
 
41
75
  ## [v2.5.0](https://github.com/jwt/ruby-jwt/tree/v2.5.0) (2022-08-25)
42
76
 
@@ -44,21 +78,21 @@
44
78
 
45
79
  **Features:**
46
80
 
47
- - Support JWK thumbprints as key ids [#481](https://github.com/jwt/ruby-jwt/pull/481) ([@anakinj](https://github.com/anakinj)).
48
- - Support OpenSSL >= 3.0 [#496](https://github.com/jwt/ruby-jwt/pull/496) ([@anakinj](https://github.com/anakinj)).
81
+ - Support JWK thumbprints as key ids [#481](https://github.com/jwt/ruby-jwt/pull/481) ([@anakinj](https://github.com/anakinj))
82
+ - Support OpenSSL >= 3.0 [#496](https://github.com/jwt/ruby-jwt/pull/496) ([@anakinj](https://github.com/anakinj))
49
83
 
50
84
  **Fixes and enhancements:**
51
- - Bring back the old Base64 (RFC2045) deocode mechanisms [#488](https://github.com/jwt/ruby-jwt/pull/488) ([@anakinj](https://github.com/anakinj)).
52
- - Rescue RbNaCl exception for EdDSA wrong key [#491](https://github.com/jwt/ruby-jwt/pull/491) ([@n-studio](https://github.com/n-studio)).
53
- - New parameter name for cases when kid is not found using JWK key loader proc [#501](https://github.com/jwt/ruby-jwt/pull/501) ([@anakinj](https://github.com/anakinj)).
54
- - Fix NoMethodError when a 2 segment token is missing 'alg' header [#502](https://github.com/jwt/ruby-jwt/pull/502) ([@cmrd-senya](https://github.com/cmrd-senya)).
85
+ - Bring back the old Base64 (RFC2045) deocode mechanisms [#488](https://github.com/jwt/ruby-jwt/pull/488) ([@anakinj](https://github.com/anakinj))
86
+ - Rescue RbNaCl exception for EdDSA wrong key [#491](https://github.com/jwt/ruby-jwt/pull/491) ([@n-studio](https://github.com/n-studio))
87
+ - New parameter name for cases when kid is not found using JWK key loader proc [#501](https://github.com/jwt/ruby-jwt/pull/501) ([@anakinj](https://github.com/anakinj))
88
+ - Fix NoMethodError when a 2 segment token is missing 'alg' header [#502](https://github.com/jwt/ruby-jwt/pull/502) ([@cmrd-senya](https://github.com/cmrd-senya))
55
89
 
56
90
  ## [v2.4.1](https://github.com/jwt/ruby-jwt/tree/v2.4.1) (2022-06-07)
57
91
 
58
92
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.4.0...v2.4.1)
59
93
 
60
94
  **Fixes and enhancements:**
61
- - Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!)).
95
+ - Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!))
62
96
 
63
97
  ## [v2.4.0](https://github.com/jwt/ruby-jwt/tree/v2.4.0) (2022-06-06)
64
98
 
@@ -66,20 +100,20 @@
66
100
 
67
101
  **Features:**
68
102
 
69
- - Dropped support for Ruby 2.5 and older [#453](https://github.com/jwt/ruby-jwt/pull/453) - [@anakinj](https://github.com/anakinj).
70
- - Use Ruby built-in url-safe base64 methods [#454](https://github.com/jwt/ruby-jwt/pull/454) - [@bdewater](https://github.com/bdewater).
71
- - Updated rubocop to 1.23.0 [#457](https://github.com/jwt/ruby-jwt/pull/457) - [@anakinj](https://github.com/anakinj).
72
- - Add x5c header key finder [#338](https://github.com/jwt/ruby-jwt/pull/338) - [@bdewater](https://github.com/bdewater).
73
- - Author driven changelog process [#463](https://github.com/jwt/ruby-jwt/pull/463) - [@anakinj](https://github.com/anakinj).
74
- - Allow regular expressions and procs to verify issuer [\#437](https://github.com/jwt/ruby-jwt/pull/437) ([rewritten](https://github.com/rewritten)).
75
- - Add Support to be able to verify from multiple keys [\#425](https://github.com/jwt/ruby-jwt/pull/425) ([ritikesh](https://github.com/ritikesh)).
103
+ - Dropped support for Ruby 2.5 and older [#453](https://github.com/jwt/ruby-jwt/pull/453) - ([@anakinj](https://github.com/anakinj))
104
+ - Use Ruby built-in url-safe base64 methods [#454](https://github.com/jwt/ruby-jwt/pull/454) - ([@bdewater](https://github.com/bdewater))
105
+ - Updated rubocop to 1.23.0 [#457](https://github.com/jwt/ruby-jwt/pull/457) - ([@anakinj](https://github.com/anakinj))
106
+ - Add x5c header key finder [#338](https://github.com/jwt/ruby-jwt/pull/338) - ([@bdewater](https://github.com/bdewater))
107
+ - Author driven changelog process [#463](https://github.com/jwt/ruby-jwt/pull/463) - ([@anakinj](https://github.com/anakinj))
108
+ - Allow regular expressions and procs to verify issuer [\#437](https://github.com/jwt/ruby-jwt/pull/437) ([rewritten](https://github.com/rewritten))
109
+ - Add Support to be able to verify from multiple keys [\#425](https://github.com/jwt/ruby-jwt/pull/425) ([ritikesh](https://github.com/ritikesh))
76
110
 
77
111
  **Fixes and enhancements:**
78
- - Readme: Typo fix re MissingRequiredClaim [\#451](https://github.com/jwt/ruby-jwt/pull/451) ([antonmorant](https://github.com/antonmorant)).
79
- - Fix RuboCop TODOs [\#476](https://github.com/jwt/ruby-jwt/pull/476) ([typhoon2099](https://github.com/typhoon2099)).
80
- - Make specific algorithms in README linkable [\#472](https://github.com/jwt/ruby-jwt/pull/472) ([milieu](https://github.com/milieu)).
81
- - Update note about supported JWK types [\#475](https://github.com/jwt/ruby-jwt/pull/475) ([dpashkevich](https://github.com/dpashkevich)).
82
- - Create CODE\_OF\_CONDUCT.md [\#449](https://github.com/jwt/ruby-jwt/pull/449) ([loic5](https://github.com/loic5)).
112
+ - Readme: Typo fix re MissingRequiredClaim [\#451](https://github.com/jwt/ruby-jwt/pull/451) ([antonmorant](https://github.com/antonmorant))
113
+ - Fix RuboCop TODOs [\#476](https://github.com/jwt/ruby-jwt/pull/476) ([typhoon2099](https://github.com/typhoon2099))
114
+ - Make specific algorithms in README linkable [\#472](https://github.com/jwt/ruby-jwt/pull/472) ([milieu](https://github.com/milieu))
115
+ - Update note about supported JWK types [\#475](https://github.com/jwt/ruby-jwt/pull/475) ([dpashkevich](https://github.com/dpashkevich))
116
+ - Create CODE\_OF\_CONDUCT.md [\#449](https://github.com/jwt/ruby-jwt/pull/449) ([loic5](https://github.com/loic5))
83
117
 
84
118
  ## [v2.3.0](https://github.com/jwt/ruby-jwt/tree/v2.3.0) (2021-10-03)
85
119
 
data/README.md CHANGED
@@ -43,6 +43,23 @@ The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cr
43
43
 
44
44
  See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
45
45
 
46
+ ### Deprecation warnings
47
+
48
+ Deprecation warnings are logged once (`:once` option) by default to avoid spam in logs. Other options are `:silent` to completely silence warnings and `:warn` to log every time a deprecated path is executed.
49
+
50
+ ```ruby
51
+ JWT.configuration.deprecation_warnings = :warn # default is :once
52
+ ```
53
+
54
+ ### Base64 decoding
55
+
56
+ In the past the gem has been supporting the Base64 decoding specified in [RFC2045](https://www.rfc-editor.org/rfc/rfc2045) allowing newlines and blanks in the base64 encoded payload. In future versions base64 decoding will be stricter and only comply to [RFC4648](https://www.rfc-editor.org/rfc/rfc4648).
57
+
58
+ The stricter base64 decoding when processing tokens can be done via the `strict_base64_decoding` configuration accessor.
59
+ ```ruby
60
+ JWT.configuration.strict_base64_decoding = true # default is false
61
+ ```
62
+
46
63
  ### **NONE**
47
64
 
48
65
  * none - unsigned token
@@ -72,7 +89,6 @@ puts decoded_token
72
89
  ### **HMAC**
73
90
 
74
91
  * HS256 - HMAC using SHA-256 hash algorithm
75
- * HS512256 - HMAC using SHA-512-256 hash algorithm (only available with RbNaCl; see note below)
76
92
  * HS384 - HMAC using SHA-384 hash algorithm
77
93
  * HS512 - HMAC using SHA-512 hash algorithm
78
94
 
@@ -95,12 +111,6 @@ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
95
111
  puts decoded_token
96
112
  ```
97
113
 
98
- Note: If [RbNaCl](https://github.com/RubyCrypto/rbnacl) is loadable, ruby-jwt will use it for HMAC-SHA256, HMAC-SHA512-256, and HMAC-SHA512. RbNaCl prior to 6.0.0 only support a maximum key size of 32 bytes for these algorithms.
99
-
100
- [RbNaCl](https://github.com/RubyCrypto/rbnacl) requires
101
- [libsodium](https://github.com/jedisct1/libsodium), it can be installed
102
- on MacOS with `brew install libsodium`.
103
-
104
114
  ### **RSA**
105
115
 
106
116
  * RS256 - RSA using SHA-256 hash algorithm
@@ -561,7 +571,7 @@ crls = crl_uris.map do |uri|
561
571
  end
562
572
 
563
573
  begin
564
- JWT.decode(token, nil, true, { x5c: { root_certificates: root_certificates, crls: crls })
574
+ JWT.decode(token, nil, true, { x5c: { root_certificates: root_certificates, crls: crls } })
565
575
  rescue JWT::DecodeError
566
576
  # Handle error, e.g. x5c header certificate revoked or expired
567
577
  end
data/lib/jwt/base64.rb CHANGED
@@ -3,14 +3,28 @@
3
3
  require 'base64'
4
4
 
5
5
  module JWT
6
- # Base64 helpers
6
+ # Base64 encoding and decoding
7
7
  class Base64
8
8
  class << self
9
+ # Encode a string with URL-safe Base64 complying with RFC 4648 (not padded).
9
10
  def url_encode(str)
10
- ::Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
11
+ ::Base64.urlsafe_encode64(str, padding: false)
11
12
  end
12
13
 
14
+ # Decode a string with URL-safe Base64 complying with RFC 4648.
15
+ # Deprecated support for RFC 2045 remains for now. ("All line breaks or other characters not found in Table 1 must be ignored by decoding software")
13
16
  def url_decode(str)
17
+ ::Base64.urlsafe_decode64(str)
18
+ rescue ArgumentError => e
19
+ raise unless e.message == 'invalid base64'
20
+ raise Base64DecodeError, 'Invalid base64 encoding' if JWT.configuration.strict_base64_decoding
21
+
22
+ loose_urlsafe_decode64(str).tap do
23
+ Deprecations.warning('Invalid base64 input detected, could be because of invalid padding, trailing whitespaces or newline chars. Graceful handling of invalid input will be dropped in the next major version of ruby-jwt')
24
+ end
25
+ end
26
+
27
+ def loose_urlsafe_decode64(str)
14
28
  str += '=' * (4 - str.length.modulo(4))
15
29
  ::Base64.decode64(str.tr('-_', '+/'))
16
30
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './error'
3
+ require_relative 'error'
4
4
 
5
5
  module JWT
6
6
  class ClaimsValidator
@@ -6,15 +6,26 @@ require_relative 'jwk_configuration'
6
6
  module JWT
7
7
  module Configuration
8
8
  class Container
9
- attr_accessor :decode, :jwk
9
+ attr_accessor :decode, :jwk, :strict_base64_decoding
10
+ attr_reader :deprecation_warnings
10
11
 
11
12
  def initialize
12
13
  reset!
13
14
  end
14
15
 
15
16
  def reset!
16
- @decode = DecodeConfiguration.new
17
- @jwk = JwkConfiguration.new
17
+ @decode = DecodeConfiguration.new
18
+ @jwk = JwkConfiguration.new
19
+ @strict_base64_decoding = false
20
+
21
+ self.deprecation_warnings = :once
22
+ end
23
+
24
+ DEPRECATION_WARNINGS_VALUES = %i[once warn silent].freeze
25
+ def deprecation_warnings=(value)
26
+ raise ArgumentError, "Invalid deprecation_warnings value #{value}. Supported values: #{DEPRECATION_WARNINGS_VALUES}" unless DEPRECATION_WARNINGS_VALUES.include?(value)
27
+
28
+ @deprecation_warnings = value
18
29
  end
19
30
  end
20
31
  end
data/lib/jwt/decode.rb CHANGED
@@ -92,13 +92,7 @@ module JWT
92
92
  end
93
93
 
94
94
  def resolve_allowed_algorithms
95
- algs = given_algorithms.map do |alg|
96
- if Algos.implementation?(alg)
97
- alg
98
- else
99
- Algos.create(alg)
100
- end
101
- end
95
+ algs = given_algorithms.map { |alg| JWA.create(alg) }
102
96
 
103
97
  sort_by_alg_header(algs)
104
98
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ # Deprecations module to handle deprecation warnings in the gem
5
+ module Deprecations
6
+ class << self
7
+ def warning(message)
8
+ case JWT.configuration.deprecation_warnings
9
+ when :warn
10
+ warn("[DEPRECATION WARNING] #{message}")
11
+ when :once
12
+ return if record_warned(message)
13
+
14
+ warn("[DEPRECATION WARNING] #{message}")
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def record_warned(message)
21
+ @warned ||= []
22
+ return true if @warned.include?(message)
23
+
24
+ @warned << message
25
+ false
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/jwt/encode.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'algos'
3
+ require_relative 'jwa'
4
4
  require_relative 'claims_validator'
5
5
 
6
6
  # JWT::Encode module
@@ -12,7 +12,7 @@ module JWT
12
12
  def initialize(options)
13
13
  @payload = options[:payload]
14
14
  @key = options[:key]
15
- @algorithm = resolve_algorithm(options[:algorithm])
15
+ @algorithm = JWA.create(options[:algorithm])
16
16
  @headers = options[:headers].transform_keys(&:to_s)
17
17
  @headers[ALG_KEY] = @algorithm.alg
18
18
  end
@@ -24,12 +24,6 @@ module JWT
24
24
 
25
25
  private
26
26
 
27
- def resolve_algorithm(algorithm)
28
- return algorithm if Algos.implementation?(algorithm)
29
-
30
- Algos.create(algorithm)
31
- end
32
-
33
27
  def encoded_header
34
28
  @encoded_header ||= encode_header
35
29
  end
data/lib/jwt/error.rb CHANGED
@@ -17,6 +17,7 @@ module JWT
17
17
  class InvalidJtiError < DecodeError; end
18
18
  class InvalidPayload < DecodeError; end
19
19
  class MissingRequiredClaim < DecodeError; end
20
+ class Base64DecodeError < DecodeError; end
20
21
 
21
22
  class JWKError < DecodeError; end
22
23
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
4
+ module JWA
5
5
  module Ecdsa
6
6
  module_function
7
7
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module JWA
5
+ module Eddsa
6
+ SUPPORTED = %w[ED25519 EdDSA].freeze
7
+ SUPPORTED_DOWNCASED = SUPPORTED.map(&:downcase).freeze
8
+
9
+ class << self
10
+ def sign(algorithm, msg, key)
11
+ unless key.is_a?(RbNaCl::Signatures::Ed25519::SigningKey)
12
+ raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
13
+ end
14
+
15
+ validate_algorithm!(algorithm)
16
+
17
+ key.sign(msg)
18
+ end
19
+
20
+ def verify(algorithm, public_key, signing_input, signature)
21
+ unless public_key.is_a?(RbNaCl::Signatures::Ed25519::VerifyKey)
22
+ raise DecodeError, "key given is a #{public_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey"
23
+ end
24
+
25
+ validate_algorithm!(algorithm)
26
+
27
+ public_key.verify(signature, signing_input)
28
+ rescue RbNaCl::CryptoError
29
+ false
30
+ end
31
+
32
+ private
33
+
34
+ def validate_algorithm!(algorithm)
35
+ return if SUPPORTED_DOWNCASED.include?(algorithm.downcase)
36
+
37
+ raise IncorrectAlgorithm, "Algorithm #{algorithm} not supported. Supported algoritms are #{SUPPORTED.join(', ')}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
4
+ module JWA
5
5
  module Hmac
6
6
  module_function
7
7
 
@@ -44,6 +44,7 @@ module JWT
44
44
  OpenSSL.fixed_length_secure_compare(a, b)
45
45
  end
46
46
  else
47
+ # :nocov:
47
48
  def fixed_length_secure_compare(a, b)
48
49
  raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
49
50
 
@@ -53,6 +54,7 @@ module JWT
53
54
  b.each_byte { |byte| res |= byte ^ l.shift }
54
55
  res == 0
55
56
  end
57
+ # :nocov:
56
58
  end
57
59
  module_function :fixed_length_secure_compare
58
60
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module HmacRbNaCl
6
+ MAPPING = { 'HS512256' => ::RbNaCl::HMAC::SHA512256 }.freeze
7
+ SUPPORTED = MAPPING.keys
8
+ class << self
9
+ def sign(algorithm, msg, key)
10
+ Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
11
+ if (hmac = resolve_algorithm(algorithm))
12
+ hmac.auth(key_for_rbnacl(hmac, key).encode('binary'), msg.encode('binary'))
13
+ else
14
+ Hmac.sign(algorithm, msg, key)
15
+ end
16
+ end
17
+
18
+ def verify(algorithm, key, signing_input, signature)
19
+ Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
20
+ if (hmac = resolve_algorithm(algorithm))
21
+ hmac.verify(key_for_rbnacl(hmac, key).encode('binary'), signature.encode('binary'), signing_input.encode('binary'))
22
+ else
23
+ Hmac.verify(algorithm, key, signing_input, signature)
24
+ end
25
+ rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
26
+ false
27
+ end
28
+
29
+ private
30
+
31
+ def key_for_rbnacl(hmac, key)
32
+ key ||= ''
33
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
34
+
35
+ return padded_empty_key(hmac.key_bytes) if key == ''
36
+
37
+ key
38
+ end
39
+
40
+ def resolve_algorithm(algorithm)
41
+ MAPPING.fetch(algorithm)
42
+ end
43
+
44
+ def padded_empty_key(length)
45
+ Array.new(length, 0x0).pack('C*').encode('binary')
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module HmacRbNaClFixed
6
+ MAPPING = { 'HS512256' => ::RbNaCl::HMAC::SHA512256 }.freeze
7
+ SUPPORTED = MAPPING.keys
8
+
9
+ class << self
10
+ def sign(algorithm, msg, key)
11
+ key ||= ''
12
+ Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
13
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
14
+
15
+ if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
16
+ hmac.auth(padded_key_bytes(key, hmac.key_bytes), msg.encode('binary'))
17
+ else
18
+ Hmac.sign(algorithm, msg, key)
19
+ end
20
+ end
21
+
22
+ def verify(algorithm, key, signing_input, signature)
23
+ key ||= ''
24
+ Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
25
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
26
+
27
+ if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
28
+ hmac.verify(padded_key_bytes(key, hmac.key_bytes), signature.encode('binary'), signing_input.encode('binary'))
29
+ else
30
+ Hmac.verify(algorithm, key, signing_input, signature)
31
+ end
32
+ rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
33
+ false
34
+ end
35
+
36
+ def resolve_algorithm(algorithm)
37
+ MAPPING.fetch(algorithm)
38
+ end
39
+
40
+ def padded_key_bytes(key, bytesize)
41
+ key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
4
+ module JWA
5
5
  module None
6
6
  module_function
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
4
+ module JWA
5
5
  module Ps
6
6
  # RSASSA-PSS signing algorithms
7
7
 
@@ -10,9 +10,9 @@ module JWT
10
10
  SUPPORTED = %w[PS256 PS384 PS512].freeze
11
11
 
12
12
  def sign(algorithm, msg, key)
13
- require_openssl!
14
-
15
- raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance." if key.is_a?(String)
13
+ unless key.is_a?(::OpenSSL::PKey::RSA)
14
+ raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance."
15
+ end
16
16
 
17
17
  translated_algorithm = algorithm.sub('PS', 'sha')
18
18
 
@@ -20,22 +20,11 @@ module JWT
20
20
  end
21
21
 
22
22
  def verify(algorithm, public_key, signing_input, signature)
23
- require_openssl!
24
23
  translated_algorithm = algorithm.sub('PS', 'sha')
25
24
  public_key.verify_pss(translated_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: translated_algorithm)
26
25
  rescue OpenSSL::PKey::PKeyError
27
26
  raise JWT::VerificationError, 'Signature verification raised'
28
27
  end
29
-
30
- def require_openssl!
31
- if Object.const_defined?('OpenSSL')
32
- if ::Gem::Version.new(OpenSSL::VERSION) < ::Gem::Version.new('2.1')
33
- raise JWT::RequiredDependencyError, "You currently have OpenSSL #{OpenSSL::VERSION}. PS support requires >= 2.1"
34
- end
35
- else
36
- raise JWT::RequiredDependencyError, 'PS signing requires OpenSSL +2.1'
37
- end
38
- end
39
28
  end
40
29
  end
41
30
  end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
4
+ module JWA
5
5
  module Rsa
6
6
  module_function
7
7
 
8
8
  SUPPORTED = %w[RS256 RS384 RS512].freeze
9
9
 
10
10
  def sign(algorithm, msg, key)
11
- raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance." if key.instance_of?(String)
11
+ unless key.is_a?(OpenSSL::PKey::RSA)
12
+ raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance"
13
+ end
12
14
 
13
15
  key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
14
16
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
4
+ module JWA
5
5
  module Unsupported
6
6
  module_function
7
7
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
5
- class AlgoWrapper
4
+ module JWA
5
+ class Wrapper
6
6
  attr_reader :alg, :cls
7
7
 
8
8
  def initialize(alg, cls)
data/lib/jwt/jwa.rb ADDED
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ begin
6
+ require 'rbnacl'
7
+ rescue LoadError
8
+ raise if defined?(RbNaCl)
9
+ end
10
+
11
+ require_relative 'jwa/hmac'
12
+ require_relative 'jwa/eddsa'
13
+ require_relative 'jwa/ecdsa'
14
+ require_relative 'jwa/rsa'
15
+ require_relative 'jwa/ps'
16
+ require_relative 'jwa/none'
17
+ require_relative 'jwa/unsupported'
18
+ require_relative 'jwa/wrapper'
19
+
20
+ module JWT
21
+ module JWA
22
+ ALGOS = [Hmac, Ecdsa, Rsa, Eddsa, Ps, None, Unsupported].tap do |l|
23
+ if ::JWT.rbnacl_6_or_greater?
24
+ require_relative 'jwa/hmac_rbnacl'
25
+ l << Algos::HmacRbNaCl
26
+ elsif ::JWT.rbnacl?
27
+ require_relative 'jwa/hmac_rbnacl_fixed'
28
+ l << Algos::HmacRbNaClFixed
29
+ end
30
+ end.freeze
31
+
32
+ class << self
33
+ def find(algorithm)
34
+ indexed[algorithm&.downcase]
35
+ end
36
+
37
+ def create(algorithm)
38
+ return algorithm if JWA.implementation?(algorithm)
39
+
40
+ Wrapper.new(*find(algorithm))
41
+ end
42
+
43
+ def implementation?(algorithm)
44
+ (algorithm.respond_to?(:valid_alg?) && algorithm.respond_to?(:verify)) ||
45
+ (algorithm.respond_to?(:alg) && algorithm.respond_to?(:sign))
46
+ end
47
+
48
+ private
49
+
50
+ def indexed
51
+ @indexed ||= begin
52
+ fallback = [nil, Unsupported]
53
+ ALGOS.each_with_object(Hash.new(fallback)) do |cls, hash|
54
+ cls.const_get(:SUPPORTED).each do |alg|
55
+ hash[alg.downcase] = [alg, cls]
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
data/lib/jwt/jwk/ec.rb CHANGED
@@ -11,6 +11,7 @@ module JWT
11
11
  EC_PUBLIC_KEY_ELEMENTS = %i[kty crv x y].freeze
12
12
  EC_PRIVATE_KEY_ELEMENTS = %i[d].freeze
13
13
  EC_KEY_ELEMENTS = (EC_PRIVATE_KEY_ELEMENTS + EC_PUBLIC_KEY_ELEMENTS).freeze
14
+ ZERO_BYTE = "\0".b.freeze
14
15
 
15
16
  def initialize(key, params = nil, options = {})
16
17
  params ||= {}
@@ -143,7 +144,6 @@ module JWT
143
144
  if ::JWT.openssl_3?
144
145
  def create_ec_key(jwk_crv, jwk_x, jwk_y, jwk_d) # rubocop:disable Metrics/MethodLength
145
146
  curve = EC.to_openssl_curve(jwk_crv)
146
-
147
147
  x_octets = decode_octets(jwk_x)
148
148
  y_octets = decode_octets(jwk_y)
149
149
 
@@ -205,12 +205,27 @@ module JWT
205
205
  end
206
206
  end
207
207
 
208
- def decode_octets(jwk_data)
209
- ::JWT::Base64.url_decode(jwk_data)
210
- end
211
-
212
- def decode_open_ssl_bn(jwk_data)
213
- OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
208
+ def decode_octets(base64_encoded_coordinate)
209
+ bytes = ::JWT::Base64.url_decode(base64_encoded_coordinate)
210
+ # Some base64 encoders on some platform omit a single 0-byte at
211
+ # the start of either Y or X coordinate of the elliptic curve point.
212
+ # This leads to an encoding error when data is passed to OpenSSL BN.
213
+ # It is know to have happend to exported JWKs on a Java application and
214
+ # on a Flutter/Dart application (both iOS and Android). All that is
215
+ # needed to fix the problem is adding a leading 0-byte. We know the
216
+ # required byte is 0 because with any other byte the point is no longer
217
+ # on the curve - and OpenSSL will actually communicate this via another
218
+ # exception. The indication of a stripped byte will be the fact that the
219
+ # coordinates - once decoded into bytes - should always be an even
220
+ # bytesize. For example, with a P-521 curve, both x and y must be 66 bytes.
221
+ # With a P-256 curve, both x and y must be 32 and so on. The simplest way
222
+ # to check for this truncation is thus to check whether the number of bytes
223
+ # is odd, and restore the leading 0-byte if it is.
224
+ if bytes.bytesize.odd?
225
+ ZERO_BYTE + bytes
226
+ else
227
+ bytes
228
+ end
214
229
  end
215
230
 
216
231
  class << self
@@ -38,12 +38,14 @@ module JWT
38
38
  end
39
39
 
40
40
  def ==(other)
41
- self[:kid] == other[:kid]
41
+ other.is_a?(::JWT::JWK::KeyBase) && self[:kid] == other[:kid]
42
42
  end
43
43
 
44
44
  alias eql? ==
45
45
 
46
46
  def <=>(other)
47
+ return nil unless other.is_a?(::JWT::JWK::KeyBase)
48
+
47
49
  self[:kid] <=> other[:kid]
48
50
  end
49
51
 
data/lib/jwt/jwk.rb CHANGED
@@ -52,4 +52,4 @@ require_relative 'jwk/key_base'
52
52
  require_relative 'jwk/ec'
53
53
  require_relative 'jwk/rsa'
54
54
  require_relative 'jwk/hmac'
55
- require_relative 'jwk/okp_rbnacl' if ::JWT.rbnacl?
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,7 +11,7 @@ module JWT
11
11
  # major version
12
12
  MAJOR = 2
13
13
  # minor version
14
- MINOR = 7
14
+ MINOR = 8
15
15
  # tiny version
16
16
  TINY = 1
17
17
  # alpha, beta, etc. tag
@@ -23,7 +23,8 @@ 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
27
28
  end
28
29
 
29
30
  def self.rbnacl?
@@ -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
@@ -31,9 +31,12 @@ Gem::Specification.new do |spec|
31
31
  spec.executables = []
32
32
  spec.require_paths = %w[lib]
33
33
 
34
+ spec.add_dependency 'base64'
35
+
34
36
  spec.add_development_dependency 'appraisal'
35
37
  spec.add_development_dependency 'bundler'
36
38
  spec.add_development_dependency 'rake'
37
39
  spec.add_development_dependency 'rspec'
40
+ spec.add_development_dependency 'rubocop'
38
41
  spec.add_development_dependency 'simplecov'
39
42
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.1
4
+ version: 2.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Rudat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-09 00:00:00.000000000 Z
11
+ date: 2024-02-29 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base64
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: appraisal
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,20 @@ dependencies:
66
80
  - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: simplecov
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -94,17 +122,6 @@ files:
94
122
  - LICENSE
95
123
  - README.md
96
124
  - lib/jwt.rb
97
- - lib/jwt/algos.rb
98
- - lib/jwt/algos/algo_wrapper.rb
99
- - lib/jwt/algos/ecdsa.rb
100
- - lib/jwt/algos/eddsa.rb
101
- - lib/jwt/algos/hmac.rb
102
- - lib/jwt/algos/hmac_rbnacl.rb
103
- - lib/jwt/algos/hmac_rbnacl_fixed.rb
104
- - lib/jwt/algos/none.rb
105
- - lib/jwt/algos/ps.rb
106
- - lib/jwt/algos/rsa.rb
107
- - lib/jwt/algos/unsupported.rb
108
125
  - lib/jwt/base64.rb
109
126
  - lib/jwt/claims_validator.rb
110
127
  - lib/jwt/configuration.rb
@@ -112,9 +129,21 @@ files:
112
129
  - lib/jwt/configuration/decode_configuration.rb
113
130
  - lib/jwt/configuration/jwk_configuration.rb
114
131
  - lib/jwt/decode.rb
132
+ - lib/jwt/deprecations.rb
115
133
  - lib/jwt/encode.rb
116
134
  - lib/jwt/error.rb
117
135
  - lib/jwt/json.rb
136
+ - lib/jwt/jwa.rb
137
+ - lib/jwt/jwa/ecdsa.rb
138
+ - lib/jwt/jwa/eddsa.rb
139
+ - lib/jwt/jwa/hmac.rb
140
+ - lib/jwt/jwa/hmac_rbnacl.rb
141
+ - lib/jwt/jwa/hmac_rbnacl_fixed.rb
142
+ - lib/jwt/jwa/none.rb
143
+ - lib/jwt/jwa/ps.rb
144
+ - lib/jwt/jwa/rsa.rb
145
+ - lib/jwt/jwa/unsupported.rb
146
+ - lib/jwt/jwa/wrapper.rb
118
147
  - lib/jwt/jwk.rb
119
148
  - lib/jwt/jwk/ec.rb
120
149
  - lib/jwt/jwk/hmac.rb
@@ -134,7 +163,7 @@ licenses:
134
163
  - MIT
135
164
  metadata:
136
165
  bug_tracker_uri: https://github.com/jwt/ruby-jwt/issues
137
- changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.7.1/CHANGELOG.md
166
+ changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.8.1/CHANGELOG.md
138
167
  rubygems_mfa_required: 'true'
139
168
  post_install_message:
140
169
  rdoc_options: []
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module Eddsa
6
- module_function
7
-
8
- SUPPORTED = %w[ED25519 EdDSA].freeze
9
-
10
- def sign(algorithm, msg, key)
11
- if key.class != RbNaCl::Signatures::Ed25519::SigningKey
12
- raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
13
- end
14
- unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
15
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
16
- end
17
-
18
- key.sign(msg)
19
- end
20
-
21
- def verify(algorithm, public_key, signing_input, signature)
22
- unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
23
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
24
- end
25
- raise DecodeError, "key given is a #{public_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey" if public_key.class != RbNaCl::Signatures::Ed25519::VerifyKey
26
-
27
- public_key.verify(signature, signing_input)
28
- rescue RbNaCl::CryptoError
29
- false
30
- end
31
- end
32
- end
33
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module HmacRbNaCl
6
- module_function
7
-
8
- MAPPING = {
9
- 'HS256' => ::RbNaCl::HMAC::SHA256,
10
- 'HS512256' => ::RbNaCl::HMAC::SHA512256,
11
- 'HS384' => nil,
12
- 'HS512' => ::RbNaCl::HMAC::SHA512
13
- }.freeze
14
-
15
- SUPPORTED = MAPPING.keys
16
-
17
- def sign(algorithm, msg, key)
18
- if (hmac = resolve_algorithm(algorithm))
19
- hmac.auth(key_for_rbnacl(hmac, key).encode('binary'), msg.encode('binary'))
20
- else
21
- Hmac.sign(algorithm, msg, key)
22
- end
23
- end
24
-
25
- def verify(algorithm, key, signing_input, signature)
26
- if (hmac = resolve_algorithm(algorithm))
27
- hmac.verify(key_for_rbnacl(hmac, key).encode('binary'), signature.encode('binary'), signing_input.encode('binary'))
28
- else
29
- Hmac.verify(algorithm, key, signing_input, signature)
30
- end
31
- rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
32
- false
33
- end
34
-
35
- def key_for_rbnacl(hmac, key)
36
- key ||= ''
37
- raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
38
-
39
- return padded_empty_key(hmac.key_bytes) if key == ''
40
-
41
- key
42
- end
43
-
44
- def resolve_algorithm(algorithm)
45
- MAPPING.fetch(algorithm)
46
- end
47
-
48
- def padded_empty_key(length)
49
- Array.new(length, 0x0).pack('C*').encode('binary')
50
- end
51
- end
52
- end
53
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module HmacRbNaClFixed
6
- module_function
7
-
8
- MAPPING = {
9
- 'HS256' => ::RbNaCl::HMAC::SHA256,
10
- 'HS512256' => ::RbNaCl::HMAC::SHA512256,
11
- 'HS384' => nil,
12
- 'HS512' => ::RbNaCl::HMAC::SHA512
13
- }.freeze
14
-
15
- SUPPORTED = MAPPING.keys
16
-
17
- def sign(algorithm, msg, key)
18
- key ||= ''
19
-
20
- raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
21
-
22
- if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
23
- hmac.auth(padded_key_bytes(key, hmac.key_bytes), msg.encode('binary'))
24
- else
25
- Hmac.sign(algorithm, msg, key)
26
- end
27
- end
28
-
29
- def verify(algorithm, key, signing_input, signature)
30
- key ||= ''
31
-
32
- raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
33
-
34
- if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
35
- hmac.verify(padded_key_bytes(key, hmac.key_bytes), signature.encode('binary'), signing_input.encode('binary'))
36
- else
37
- Hmac.verify(algorithm, key, signing_input, signature)
38
- end
39
- rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
40
- false
41
- end
42
-
43
- def resolve_algorithm(algorithm)
44
- MAPPING.fetch(algorithm)
45
- end
46
-
47
- def padded_key_bytes(key, bytesize)
48
- key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
49
- end
50
- end
51
- end
52
- end
data/lib/jwt/algos.rb DELETED
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- begin
4
- require 'rbnacl'
5
- rescue LoadError
6
- raise if defined?(RbNaCl)
7
- end
8
- require 'openssl'
9
-
10
- require 'jwt/algos/hmac'
11
- require 'jwt/algos/eddsa'
12
- require 'jwt/algos/ecdsa'
13
- require 'jwt/algos/rsa'
14
- require 'jwt/algos/ps'
15
- require 'jwt/algos/none'
16
- require 'jwt/algos/unsupported'
17
- require 'jwt/algos/algo_wrapper'
18
-
19
- module JWT
20
- module Algos
21
- extend self
22
-
23
- ALGOS = [Algos::Ecdsa,
24
- Algos::Rsa,
25
- Algos::Eddsa,
26
- Algos::Ps,
27
- Algos::None,
28
- Algos::Unsupported].tap do |l|
29
- if ::JWT.rbnacl_6_or_greater?
30
- require_relative 'algos/hmac_rbnacl'
31
- l.unshift(Algos::HmacRbNaCl)
32
- elsif ::JWT.rbnacl?
33
- require_relative 'algos/hmac_rbnacl_fixed'
34
- l.unshift(Algos::HmacRbNaClFixed)
35
- else
36
- l.unshift(Algos::Hmac)
37
- end
38
- end.freeze
39
-
40
- def find(algorithm)
41
- indexed[algorithm && algorithm.downcase]
42
- end
43
-
44
- def create(algorithm)
45
- Algos::AlgoWrapper.new(*find(algorithm))
46
- end
47
-
48
- def implementation?(algorithm)
49
- (algorithm.respond_to?(:valid_alg?) && algorithm.respond_to?(:verify)) ||
50
- (algorithm.respond_to?(:alg) && algorithm.respond_to?(:sign))
51
- end
52
-
53
- private
54
-
55
- def indexed
56
- @indexed ||= begin
57
- fallback = [nil, Algos::Unsupported]
58
- ALGOS.each_with_object(Hash.new(fallback)) do |cls, hash|
59
- cls.const_get(:SUPPORTED).each do |alg|
60
- hash[alg.downcase] = [alg, cls]
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end