jwt 2.8.1 → 2.9.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: af3792b982f014801d3ff7ae3410be6bd1e0b27f199c500942eb86267bb2b764
4
- data.tar.gz: c37fc4b72cf3819210b15164548eff44579de1e8f8c48d9ce35864a84f007d9a
3
+ metadata.gz: 0f3d3ea752a6b4dee8f1b020a3e0af95e003f74a8c40f730faab73b616fc9e98
4
+ data.tar.gz: 8dcdb470771f56931485824c32739bfcec1ce310b7096d6cd28c656d0f8c21fd
5
5
  SHA512:
6
- metadata.gz: 3f61fd13a1d56c657691abb4bfde3671a7d93b5c853785b804c0d119d27f4641685828eb9c65a8e4856487782530e791ecbce5f1a5f1cb61c883daff97a44367
7
- data.tar.gz: ef36aa81991e28cb9d3df65f1ebbeb6599eb99dee8e1953aff1a6231e91c6a3f9633e3afd22fbbc6c09f6318c4e516ee7a908428eee303f3952fed890395b267
6
+ metadata.gz: c68cccb66154a824751df6e7cd4d0b9d31372c0bada498097770feb8992ac3c7a462cd11eb759171d096e8d957b2a67b088fb35dc79fe7b4dd6eac6b25140d63
7
+ data.tar.gz: 03fe234584cce6458fb1a0f64cb399f81bc3c7aee40e25c011f0c27484ede3c6c6a6d950a9c5f3f13e4d66db5a76de98f2801942762df514097b2f663bf79247
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.9.0](https://github.com/jwt/ruby-jwt/tree/v2.9.0) (NEXT)
4
+
5
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.2...v2.9.0)
6
+
7
+ **Features:**
8
+
9
+ - Build and push gem using a GH action [#612](https://github.com/jwt/ruby-jwt/pull/612) ([@anakinj](https://github.com/anakinj))
10
+
11
+ **Fixes and enhancements:**
12
+
13
+ - Refactor claim validators into their own classes [#605](https://github.com/jwt/ruby-jwt/pull/605) ([@anakinj](https://github.com/anakinj), [@MatteoPierro](https://github.com/MatteoPierro))
14
+ - Allow extending available algorithms [#607](https://github.com/jwt/ruby-jwt/pull/607) ([@anakinj](https://github.com/anakinj))
15
+ - Do not include the EdDSA algorithm if rbnacl not available [#613](https://github.com/jwt/ruby-jwt/pull/613) ([@anakinj](https://github.com/anakinj))
16
+
17
+ ## [v2.8.2](https://github.com/jwt/ruby-jwt/tree/v2.8.2) (2024-06-18)
18
+
19
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.1...v2.8.2)
20
+
21
+ **Fixes and enhancements:**
22
+
23
+ - Print deprecation warnings only on when token decoding succeeds [#600](https://github.com/jwt/ruby-jwt/pull/600) ([@anakinj](https://github.com/anakinj))
24
+ - Unify code style [#602](https://github.com/jwt/ruby-jwt/pull/602) ([@anakinj](https://github.com/anakinj))
25
+
3
26
  ## [v2.8.1](https://github.com/jwt/ruby-jwt/tree/v2.8.1) (2024-02-29)
4
27
 
5
28
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.0...v2.8.1)
data/README.md CHANGED
@@ -223,18 +223,22 @@ puts decoded_token
223
223
 
224
224
  ### **Custom algorithms**
225
225
 
226
- An object implementing custom signing or verification behaviour can be passed in the `algorithm` option when encoding and decoding. The given object needs to implement the method `valid_alg?` and `verify` and/or `alg` and `sign`, depending if object is used for encoding or decoding.
226
+ When encoding or decoding a token, you can pass in a custom object through the `algorithm` option to handle signing or verification. This custom object must include or extend the `JWT::JWA::SigningAlgorithm` module and implement certain methods:
227
+
228
+ - For decoding/verifying: The object must implement the methods `alg` and `verify`.
229
+ - For encoding/signing: The object must implement the methods `alg` and `sign`.
230
+
231
+ For customization options check the details from `JWT::JWA::SigningAlgorithm`.
232
+
227
233
 
228
234
  ```ruby
229
235
  module CustomHS512Algorithm
236
+ extend JWT::JWA::SigningAlgorithm
237
+
230
238
  def self.alg
231
239
  'HS512'
232
240
  end
233
241
 
234
- def self.valid_alg?(alg_to_validate)
235
- alg_to_validate == alg
236
- end
237
-
238
242
  def self.sign(data:, signing_key:)
239
243
  OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), data, signing_key)
240
244
  end
@@ -690,21 +694,25 @@ jwk_hash = jwk.export
690
694
  thumbprint_as_the_kid = jwk_hash[:kid]
691
695
  ```
692
696
 
693
- # Development and Tests
697
+ # Development and testing
694
698
 
695
- We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
699
+ The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
696
700
 
697
701
  ```bash
698
- rake release
702
+ bundle install
703
+ bundle exec appraisal rake test
699
704
  ```
700
705
 
701
- The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
706
+ # Releasing
707
+
708
+ To cut a new release adjust the [version.rb](lib/jwt/version.rb) and [CHANGELOG](CHANGELOG.md) with desired version numbers and dates and commit the changes. Tag the release with the version number using the following command:
702
709
 
703
710
  ```bash
704
- bundle install
705
- bundle exec appraisal rake test
711
+ rake release:source_control_push
706
712
  ```
707
713
 
714
+ This will tag a new version an trigger a [GitHub action](.github/workflows/push_gem.yml) that eventually will push the gem to rubygems.org.
715
+
708
716
  ## How to contribute
709
717
  See [CONTRIBUTING](CONTRIBUTING.md).
710
718
 
data/lib/jwt/base64.rb CHANGED
@@ -20,7 +20,7 @@ module JWT
20
20
  raise Base64DecodeError, 'Invalid base64 encoding' if JWT.configuration.strict_base64_decoding
21
21
 
22
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')
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', only_if_valid: true)
24
24
  end
25
25
  end
26
26
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Audience
6
+ def initialize(expected_audience:)
7
+ @expected_audience = expected_audience
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ aud = context.payload['aud']
12
+ raise JWT::InvalidAudError, "Invalid audience. Expected #{expected_audience}, received #{aud || '<none>'}" if ([*aud] & [*expected_audience]).empty?
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :expected_audience
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Expiration
6
+ def initialize(leeway:)
7
+ @leeway = leeway || 0
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ return unless context.payload.is_a?(Hash)
12
+ return unless context.payload.key?('exp')
13
+
14
+ raise JWT::ExpiredSignature, 'Signature has expired' if context.payload['exp'].to_i <= (Time.now.to_i - leeway)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :leeway
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class IssuedAt
6
+ def verify!(context:, **_args)
7
+ return unless context.payload.is_a?(Hash)
8
+ return unless context.payload.key?('iat')
9
+
10
+ iat = context.payload['iat']
11
+ raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(::Numeric) || iat.to_f > Time.now.to_f
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Issuer
6
+ def initialize(issuers:)
7
+ @issuers = Array(issuers).map { |item| item.is_a?(Symbol) ? item.to_s : item }
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ case (iss = context.payload['iss'])
12
+ when *issuers
13
+ nil
14
+ else
15
+ raise JWT::InvalidIssuerError, "Invalid issuer. Expected #{issuers}, received #{iss || '<none>'}"
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :issuers
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class JwtId
6
+ def initialize(validator:)
7
+ @validator = validator
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ jti = context.payload['jti']
12
+ if validator.respond_to?(:call)
13
+ verified = validator.arity == 2 ? validator.call(jti, context.payload) : validator.call(jti)
14
+ raise(JWT::InvalidJtiError, 'Invalid jti') unless verified
15
+ elsif jti.to_s.strip.empty?
16
+ raise(JWT::InvalidJtiError, 'Missing jti')
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :validator
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class NotBefore
6
+ def initialize(leeway:)
7
+ @leeway = leeway || 0
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ return unless context.payload.is_a?(Hash)
12
+ return unless context.payload.key?('nbf')
13
+
14
+ raise JWT::ImmatureSignature, 'Signature nbf has not been reached' if context.payload['nbf'].to_i > (Time.now.to_i + leeway)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :leeway
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Numeric
6
+ def self.verify!(payload:, **_args)
7
+ return unless payload.is_a?(Hash)
8
+
9
+ new(payload).verify!
10
+ end
11
+
12
+ NUMERIC_CLAIMS = %i[
13
+ exp
14
+ iat
15
+ nbf
16
+ ].freeze
17
+
18
+ def initialize(payload)
19
+ @payload = payload.transform_keys(&:to_sym)
20
+ end
21
+
22
+ def verify!
23
+ validate_numeric_claims
24
+
25
+ true
26
+ end
27
+
28
+ private
29
+
30
+ def validate_numeric_claims
31
+ NUMERIC_CLAIMS.each do |claim|
32
+ validate_is_numeric(claim) if @payload.key?(claim)
33
+ end
34
+ end
35
+
36
+ def validate_is_numeric(claim)
37
+ return if @payload[claim].is_a?(::Numeric)
38
+
39
+ raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{@payload[claim].class}"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Required
6
+ def initialize(required_claims:)
7
+ @required_claims = required_claims
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ required_claims.each do |required_claim|
12
+ next if context.payload.is_a?(Hash) && context.payload.key?(required_claim)
13
+
14
+ raise JWT::MissingRequiredClaim, "Missing required claim #{required_claim}"
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :required_claims
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Subject
6
+ def initialize(expected_subject:)
7
+ @expected_subject = expected_subject.to_s
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ sub = context.payload['sub']
12
+ raise(JWT::InvalidSubError, "Invalid subject. Expected #{expected_subject}, received #{sub || '<none>'}") unless sub.to_s == expected_subject
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :expected_subject
18
+ end
19
+ end
20
+ end
data/lib/jwt/claims.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'claims/audience'
4
+ require_relative 'claims/expiration'
5
+ require_relative 'claims/issued_at'
6
+ require_relative 'claims/issuer'
7
+ require_relative 'claims/jwt_id'
8
+ require_relative 'claims/not_before'
9
+ require_relative 'claims/numeric'
10
+ require_relative 'claims/required'
11
+ require_relative 'claims/subject'
12
+
13
+ module JWT
14
+ module Claims
15
+ VerificationContext = Struct.new(:payload, keyword_init: true)
16
+
17
+ VERIFIERS = {
18
+ verify_expiration: ->(options) { Claims::Expiration.new(leeway: options[:exp_leeway] || options[:leeway]) },
19
+ verify_not_before: ->(options) { Claims::NotBefore.new(leeway: options[:nbf_leeway] || options[:leeway]) },
20
+ verify_iss: ->(options) { Claims::Issuer.new(issuers: options[:iss]) },
21
+ verify_iat: ->(*) { Claims::IssuedAt.new },
22
+ verify_jti: ->(options) { Claims::JwtId.new(validator: options[:verify_jti]) },
23
+ verify_aud: ->(options) { Claims::Audience.new(expected_audience: options[:aud]) },
24
+ verify_sub: ->(options) { options[:sub] && Claims::Subject.new(expected_subject: options[:sub]) },
25
+ required_claims: ->(options) { Claims::Required.new(required_claims: options[:required_claims]) }
26
+ }.freeze
27
+
28
+ class << self
29
+ def verify!(payload, options)
30
+ VERIFIERS.each do |key, verifier_builder|
31
+ next unless options[key]
32
+
33
+ verifier_builder&.call(options)&.verify!(context: VerificationContext.new(payload: payload))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -18,7 +18,7 @@ module JWT
18
18
  JWT::JWK::Thumbprint
19
19
  else
20
20
  raise ArgumentError, "#{value} is not a valid kid generator type."
21
- end
21
+ end
22
22
  end
23
23
 
24
24
  attr_accessor :kid_generator
data/lib/jwt/decode.rb CHANGED
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
-
5
- require 'jwt/verify'
6
4
  require 'jwt/x5c_key_finder'
7
5
 
8
6
  # JWT::Decode module
@@ -10,7 +8,7 @@ module JWT
10
8
  # Decoding logic for JWT
11
9
  class Decode
12
10
  def initialize(jwt, key, verify, options, &keyfinder)
13
- raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
11
+ raise JWT::DecodeError, 'Nil JSON web token' unless jwt
14
12
 
15
13
  @jwt = jwt
16
14
  @key = key
@@ -30,7 +28,7 @@ module JWT
30
28
  verify_signature
31
29
  verify_claims
32
30
  end
33
- raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
31
+ raise JWT::DecodeError, 'Not enough or too many segments' unless header && payload
34
32
 
35
33
  [payload, header]
36
34
  end
@@ -46,21 +44,21 @@ module JWT
46
44
 
47
45
  return if Array(@key).any? { |key| verify_signature_for?(key) }
48
46
 
49
- raise(JWT::VerificationError, 'Signature verification failed')
47
+ raise JWT::VerificationError, 'Signature verification failed'
50
48
  end
51
49
 
52
50
  def verify_algo
53
- raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty?
54
- raise(JWT::IncorrectAlgorithm, 'Token is missing alg header') unless alg_in_header
55
- raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') if allowed_and_valid_algorithms.empty?
51
+ raise JWT::IncorrectAlgorithm, 'An algorithm must be specified' if allowed_algorithms.empty?
52
+ raise JWT::IncorrectAlgorithm, 'Token is missing alg header' unless alg_in_header
53
+ raise JWT::IncorrectAlgorithm, 'Expected a different algorithm' if allowed_and_valid_algorithms.empty?
56
54
  end
57
55
 
58
56
  def set_key
59
57
  @key = find_key(&@keyfinder) if @keyfinder
60
58
  @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks], allow_nil_kid: @options[:allow_nil_kid]).key_for(header['kid']) if @options[:jwks]
61
- if (x5c_options = @options[:x5c])
62
- @key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(header['x5c'])
63
- end
59
+ return unless (x5c_options = @options[:x5c])
60
+
61
+ @key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(header['x5c'])
64
62
  end
65
63
 
66
64
  def verify_signature_for?(key)
@@ -92,7 +90,7 @@ module JWT
92
90
  end
93
91
 
94
92
  def resolve_allowed_algorithms
95
- algs = given_algorithms.map { |alg| JWA.create(alg) }
93
+ algs = given_algorithms.map { |alg| JWA.resolve(alg) }
96
94
 
97
95
  sort_by_alg_header(algs)
98
96
  end
@@ -113,8 +111,7 @@ module JWT
113
111
  end
114
112
 
115
113
  def verify_claims
116
- Verify.verify_claims(payload, @options)
117
- Verify.verify_required_claims(payload, @options)
114
+ Claims.verify!(payload, @options)
118
115
  end
119
116
 
120
117
  def validate_segment_count!
@@ -122,7 +119,7 @@ module JWT
122
119
  return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
123
120
  return if segment_length == 2 && none_algorithm?
124
121
 
125
- raise(JWT::DecodeError, 'Not enough or too many segments')
122
+ raise JWT::DecodeError, 'Not enough or too many segments'
126
123
  end
127
124
 
128
125
  def segment_length
@@ -4,15 +4,34 @@ module JWT
4
4
  # Deprecations module to handle deprecation warnings in the gem
5
5
  module Deprecations
6
6
  class << self
7
- def warning(message)
7
+ def context
8
+ yield.tap { emit_warnings }
9
+ ensure
10
+ Thread.current[:jwt_warning_store] = nil
11
+ end
12
+
13
+ def warning(message, only_if_valid: false)
14
+ method_name = only_if_valid ? :store : :warn
8
15
  case JWT.configuration.deprecation_warnings
9
- when :warn
10
- warn("[DEPRECATION WARNING] #{message}")
11
16
  when :once
12
17
  return if record_warned(message)
13
-
14
- warn("[DEPRECATION WARNING] #{message}")
18
+ when :warn
19
+ # noop
20
+ else
21
+ return
15
22
  end
23
+
24
+ send(method_name, "[DEPRECATION WARNING] #{message}")
25
+ end
26
+
27
+ def store(message)
28
+ (Thread.current[:jwt_warning_store] ||= []) << message
29
+ end
30
+
31
+ def emit_warnings
32
+ return if Thread.current[:jwt_warning_store].nil?
33
+
34
+ Thread.current[:jwt_warning_store].each { |warning| warn(warning) }
16
35
  end
17
36
 
18
37
  private
data/lib/jwt/encode.rb CHANGED
@@ -1,20 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'jwa'
4
- require_relative 'claims_validator'
5
4
 
6
5
  # JWT::Encode module
7
6
  module JWT
8
7
  # Encoding logic for JWT
9
8
  class Encode
10
- ALG_KEY = 'alg'
11
-
12
9
  def initialize(options)
13
10
  @payload = options[:payload]
14
11
  @key = options[:key]
15
- @algorithm = JWA.create(options[:algorithm])
12
+ @algorithm = JWA.resolve(options[:algorithm])
16
13
  @headers = options[:headers].transform_keys(&:to_s)
17
- @headers[ALG_KEY] = @algorithm.alg
18
14
  end
19
15
 
20
16
  def segments
@@ -41,7 +37,7 @@ module JWT
41
37
  end
42
38
 
43
39
  def encode_header
44
- encode_data(@headers)
40
+ encode_data(@headers.merge(@algorithm.header(signing_key: @key)))
45
41
  end
46
42
 
47
43
  def encode_payload
@@ -55,7 +51,7 @@ module JWT
55
51
  def validate_claims!
56
52
  return unless @payload.is_a?(Hash)
57
53
 
58
- ClaimsValidator.new(@payload).validate!
54
+ Claims::Numeric.new(@payload).verify!
59
55
  end
60
56
 
61
57
  def encode_signature
data/lib/jwt/jwa/ecdsa.rb CHANGED
@@ -2,8 +2,35 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module Ecdsa
6
- module_function
5
+ class Ecdsa
6
+ include JWT::JWA::SigningAlgorithm
7
+
8
+ def initialize(alg, digest)
9
+ @alg = alg
10
+ @digest = OpenSSL::Digest.new(digest)
11
+ end
12
+
13
+ def sign(data:, signing_key:)
14
+ curve_definition = curve_by_name(signing_key.group.curve_name)
15
+ key_algorithm = curve_definition[:algorithm]
16
+ if alg != key_algorithm
17
+ raise IncorrectAlgorithm, "payload algorithm is #{alg} but #{key_algorithm} signing key was provided"
18
+ end
19
+
20
+ asn1_to_raw(signing_key.dsa_sign_asn1(digest.digest(data)), signing_key)
21
+ end
22
+
23
+ def verify(data:, signature:, verification_key:)
24
+ curve_definition = curve_by_name(verification_key.group.curve_name)
25
+ key_algorithm = curve_definition[:algorithm]
26
+ if alg != key_algorithm
27
+ raise IncorrectAlgorithm, "payload algorithm is #{alg} but #{key_algorithm} verification key was provided"
28
+ end
29
+
30
+ verification_key.dsa_verify_asn1(digest.digest(data), raw_to_asn1(signature, verification_key))
31
+ rescue OpenSSL::PKey::PKeyError
32
+ raise JWT::VerificationError, 'Signature verification raised'
33
+ end
7
34
 
8
35
  NAMED_CURVES = {
9
36
  'prime256v1' => {
@@ -28,36 +55,22 @@ module JWT
28
55
  }
29
56
  }.freeze
30
57
 
31
- SUPPORTED = NAMED_CURVES.map { |_, c| c[:algorithm] }.uniq.freeze
58
+ NAMED_CURVES.each_value do |v|
59
+ register_algorithm(new(v[:algorithm], v[:digest]))
60
+ end
32
61
 
33
- def sign(algorithm, msg, key)
34
- curve_definition = curve_by_name(key.group.curve_name)
35
- key_algorithm = curve_definition[:algorithm]
36
- if algorithm != key_algorithm
37
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
62
+ def self.curve_by_name(name)
63
+ NAMED_CURVES.fetch(name) do
64
+ raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
38
65
  end
39
-
40
- digest = OpenSSL::Digest.new(curve_definition[:digest])
41
- asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
42
66
  end
43
67
 
44
- def verify(algorithm, public_key, signing_input, signature)
45
- curve_definition = curve_by_name(public_key.group.curve_name)
46
- key_algorithm = curve_definition[:algorithm]
47
- if algorithm != key_algorithm
48
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
49
- end
68
+ private
50
69
 
51
- digest = OpenSSL::Digest.new(curve_definition[:digest])
52
- public_key.dsa_verify_asn1(digest.digest(signing_input), raw_to_asn1(signature, public_key))
53
- rescue OpenSSL::PKey::PKeyError
54
- raise JWT::VerificationError, 'Signature verification raised'
55
- end
70
+ attr_reader :digest
56
71
 
57
72
  def curve_by_name(name)
58
- NAMED_CURVES.fetch(name) do
59
- raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
60
- end
73
+ self.class.curve_by_name(name)
61
74
  end
62
75
 
63
76
  def raw_to_asn1(signature, private_key)