jwt 2.8.1 → 2.9.0

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: 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)