jwt 2.4.1 → 2.9.3

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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +177 -14
  3. data/CONTRIBUTING.md +7 -7
  4. data/README.md +180 -37
  5. data/lib/jwt/base64.rb +33 -0
  6. data/lib/jwt/claims/audience.rb +20 -0
  7. data/lib/jwt/claims/decode_verifier.rb +40 -0
  8. data/lib/jwt/claims/expiration.rb +22 -0
  9. data/lib/jwt/claims/issued_at.rb +15 -0
  10. data/lib/jwt/claims/issuer.rb +24 -0
  11. data/lib/jwt/claims/jwt_id.rb +25 -0
  12. data/lib/jwt/claims/not_before.rb +22 -0
  13. data/lib/jwt/claims/numeric.rb +55 -0
  14. data/lib/jwt/claims/required.rb +23 -0
  15. data/lib/jwt/claims/subject.rb +20 -0
  16. data/lib/jwt/claims/verifier.rb +62 -0
  17. data/lib/jwt/claims.rb +82 -0
  18. data/lib/jwt/claims_validator.rb +3 -24
  19. data/lib/jwt/configuration/container.rb +32 -0
  20. data/lib/jwt/configuration/decode_configuration.rb +46 -0
  21. data/lib/jwt/configuration/jwk_configuration.rb +27 -0
  22. data/lib/jwt/configuration.rb +15 -0
  23. data/lib/jwt/decode.rb +54 -41
  24. data/lib/jwt/deprecations.rb +48 -0
  25. data/lib/jwt/encode.rb +21 -21
  26. data/lib/jwt/error.rb +1 -0
  27. data/lib/jwt/jwa/compat.rb +29 -0
  28. data/lib/jwt/jwa/ecdsa.rb +93 -0
  29. data/lib/jwt/jwa/eddsa.rb +34 -0
  30. data/lib/jwt/jwa/hmac.rb +83 -0
  31. data/lib/jwt/jwa/hmac_rbnacl.rb +49 -0
  32. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +46 -0
  33. data/lib/jwt/jwa/none.rb +23 -0
  34. data/lib/jwt/jwa/ps.rb +36 -0
  35. data/lib/jwt/jwa/rsa.rb +36 -0
  36. data/lib/jwt/jwa/signing_algorithm.rb +60 -0
  37. data/lib/jwt/jwa/unsupported.rb +19 -0
  38. data/lib/jwt/jwa/wrapper.rb +43 -0
  39. data/lib/jwt/jwa.rb +50 -0
  40. data/lib/jwt/jwk/ec.rb +162 -65
  41. data/lib/jwt/jwk/hmac.rb +69 -24
  42. data/lib/jwt/jwk/key_base.rb +45 -7
  43. data/lib/jwt/jwk/key_finder.rb +19 -35
  44. data/lib/jwt/jwk/kid_as_key_digest.rb +15 -0
  45. data/lib/jwt/jwk/okp_rbnacl.rb +110 -0
  46. data/lib/jwt/jwk/rsa.rb +141 -54
  47. data/lib/jwt/jwk/set.rb +80 -0
  48. data/lib/jwt/jwk/thumbprint.rb +26 -0
  49. data/lib/jwt/jwk.rb +14 -11
  50. data/lib/jwt/verify.rb +10 -89
  51. data/lib/jwt/version.rb +24 -2
  52. data/lib/jwt/x5c_key_finder.rb +3 -6
  53. data/lib/jwt.rb +12 -4
  54. data/ruby-jwt.gemspec +11 -4
  55. metadata +59 -31
  56. data/.codeclimate.yml +0 -8
  57. data/.github/workflows/coverage.yml +0 -27
  58. data/.github/workflows/test.yml +0 -66
  59. data/.gitignore +0 -13
  60. data/.reek.yml +0 -22
  61. data/.rspec +0 -2
  62. data/.rubocop.yml +0 -67
  63. data/.sourcelevel.yml +0 -17
  64. data/Appraisals +0 -13
  65. data/Gemfile +0 -7
  66. data/Rakefile +0 -16
  67. data/lib/jwt/algos/ecdsa.rb +0 -64
  68. data/lib/jwt/algos/eddsa.rb +0 -33
  69. data/lib/jwt/algos/hmac.rb +0 -36
  70. data/lib/jwt/algos/none.rb +0 -17
  71. data/lib/jwt/algos/ps.rb +0 -43
  72. data/lib/jwt/algos/rsa.rb +0 -22
  73. data/lib/jwt/algos/unsupported.rb +0 -19
  74. data/lib/jwt/algos.rb +0 -44
  75. data/lib/jwt/default_options.rb +0 -18
  76. data/lib/jwt/security_utils.rb +0 -59
  77. data/lib/jwt/signature.rb +0 -35
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module Ecdsa
6
- module_function
7
-
8
- NAMED_CURVES = {
9
- 'prime256v1' => {
10
- algorithm: 'ES256',
11
- digest: 'sha256'
12
- },
13
- 'secp256r1' => { # alias for prime256v1
14
- algorithm: 'ES256',
15
- digest: 'sha256'
16
- },
17
- 'secp384r1' => {
18
- algorithm: 'ES384',
19
- digest: 'sha384'
20
- },
21
- 'secp521r1' => {
22
- algorithm: 'ES512',
23
- digest: 'sha512'
24
- },
25
- 'secp256k1' => {
26
- algorithm: 'ES256K',
27
- digest: 'sha256'
28
- }
29
- }.freeze
30
-
31
- SUPPORTED = NAMED_CURVES.map { |_, c| c[:algorithm] }.uniq.freeze
32
-
33
- def sign(to_sign)
34
- algorithm, msg, key = to_sign.values
35
- curve_definition = curve_by_name(key.group.curve_name)
36
- key_algorithm = curve_definition[:algorithm]
37
- if algorithm != key_algorithm
38
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
39
- end
40
-
41
- digest = OpenSSL::Digest.new(curve_definition[:digest])
42
- SecurityUtils.asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
43
- end
44
-
45
- def verify(to_verify)
46
- algorithm, public_key, signing_input, signature = to_verify.values
47
- curve_definition = curve_by_name(public_key.group.curve_name)
48
- key_algorithm = curve_definition[:algorithm]
49
- if algorithm != key_algorithm
50
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
51
- end
52
-
53
- digest = OpenSSL::Digest.new(curve_definition[:digest])
54
- public_key.dsa_verify_asn1(digest.digest(signing_input), SecurityUtils.raw_to_asn1(signature, public_key))
55
- end
56
-
57
- def curve_by_name(name)
58
- NAMED_CURVES.fetch(name) do
59
- raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
60
- end
61
- end
62
- end
63
- end
64
- end
@@ -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(to_sign)
11
- algorithm, msg, key = to_sign.values
12
- if key.class != RbNaCl::Signatures::Ed25519::SigningKey
13
- raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
14
- end
15
- unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
16
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
17
- end
18
-
19
- key.sign(msg)
20
- end
21
-
22
- def verify(to_verify)
23
- algorithm, public_key, signing_input, signature = to_verify.values
24
- unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
25
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
26
- end
27
- 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
28
-
29
- public_key.verify(signature, signing_input)
30
- end
31
- end
32
- end
33
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module Hmac
6
- module_function
7
-
8
- SUPPORTED = %w[HS256 HS512256 HS384 HS512].freeze
9
-
10
- def sign(to_sign)
11
- algorithm, msg, key = to_sign.values
12
- key ||= ''
13
- authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
14
- if authenticator && padded_key
15
- authenticator.auth(padded_key, msg.encode('binary'))
16
- else
17
- OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
18
- end
19
- end
20
-
21
- def verify(to_verify)
22
- algorithm, public_key, signing_input, signature = to_verify.values
23
- authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, public_key)
24
- if authenticator && padded_key
25
- begin
26
- authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
27
- rescue RbNaCl::BadAuthenticatorError
28
- false
29
- end
30
- else
31
- SecurityUtils.secure_compare(signature, sign(JWT::Signature::ToSign.new(algorithm, signing_input, public_key)))
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module None
6
- module_function
7
-
8
- SUPPORTED = %w[none].freeze
9
-
10
- def sign(*); end
11
-
12
- def verify(*)
13
- true
14
- end
15
- end
16
- end
17
- end
data/lib/jwt/algos/ps.rb DELETED
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module Ps
6
- # RSASSA-PSS signing algorithms
7
-
8
- module_function
9
-
10
- SUPPORTED = %w[PS256 PS384 PS512].freeze
11
-
12
- def sign(to_sign)
13
- require_openssl!
14
-
15
- algorithm, msg, key = to_sign.values
16
-
17
- key_class = key.class
18
-
19
- raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance." if key_class == String
20
-
21
- translated_algorithm = algorithm.sub('PS', 'sha')
22
-
23
- key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
24
- end
25
-
26
- def verify(to_verify)
27
- require_openssl!
28
-
29
- SecurityUtils.verify_ps(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
30
- end
31
-
32
- def require_openssl!
33
- if Object.const_defined?('OpenSSL')
34
- if ::Gem::Version.new(OpenSSL::VERSION) < ::Gem::Version.new('2.1')
35
- raise JWT::RequiredDependencyError, "You currently have OpenSSL #{OpenSSL::VERSION}. PS support requires >= 2.1"
36
- end
37
- else
38
- raise JWT::RequiredDependencyError, 'PS signing requires OpenSSL +2.1'
39
- end
40
- end
41
- end
42
- end
43
- end
data/lib/jwt/algos/rsa.rb DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module Rsa
6
- module_function
7
-
8
- SUPPORTED = %w[RS256 RS384 RS512].freeze
9
-
10
- def sign(to_sign)
11
- algorithm, msg, key = to_sign.values
12
- raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance." if key.instance_of?(String)
13
-
14
- key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
15
- end
16
-
17
- def verify(to_verify)
18
- SecurityUtils.verify_rsa(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
19
- end
20
- end
21
- end
22
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module Algos
5
- module Unsupported
6
- module_function
7
-
8
- SUPPORTED = [].freeze
9
-
10
- def sign(*)
11
- raise NotImplementedError, 'Unsupported signing method'
12
- end
13
-
14
- def verify(*)
15
- raise JWT::VerificationError, 'Algorithm not supported'
16
- end
17
- end
18
- end
19
- end
data/lib/jwt/algos.rb DELETED
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'jwt/algos/hmac'
4
- require 'jwt/algos/eddsa'
5
- require 'jwt/algos/ecdsa'
6
- require 'jwt/algos/rsa'
7
- require 'jwt/algos/ps'
8
- require 'jwt/algos/none'
9
- require 'jwt/algos/unsupported'
10
-
11
- # JWT::Signature module
12
- module JWT
13
- # Signature logic for JWT
14
- module Algos
15
- extend self
16
-
17
- ALGOS = [
18
- Algos::Hmac,
19
- Algos::Ecdsa,
20
- Algos::Rsa,
21
- Algos::Eddsa,
22
- Algos::Ps,
23
- Algos::None,
24
- Algos::Unsupported
25
- ].freeze
26
-
27
- def find(algorithm)
28
- indexed[algorithm && algorithm.downcase]
29
- end
30
-
31
- private
32
-
33
- def indexed
34
- @indexed ||= begin
35
- fallback = [Algos::Unsupported, nil]
36
- ALGOS.each_with_object(Hash.new(fallback)) do |alg, hash|
37
- alg.const_get(:SUPPORTED).each do |code|
38
- hash[code.downcase] = [alg, code]
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- module DefaultOptions
5
- DEFAULT_OPTIONS = {
6
- verify_expiration: true,
7
- verify_not_before: true,
8
- verify_iss: false,
9
- verify_iat: false,
10
- verify_jti: false,
11
- verify_aud: false,
12
- verify_sub: false,
13
- leeway: 0,
14
- algorithms: ['HS256'],
15
- required_claims: []
16
- }.freeze
17
- end
18
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JWT
4
- # Collection of security methods
5
- #
6
- # @see: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/security_utils.rb
7
- module SecurityUtils
8
- module_function
9
-
10
- def secure_compare(left, right)
11
- left_bytesize = left.bytesize
12
-
13
- return false unless left_bytesize == right.bytesize
14
-
15
- unpacked_left = left.unpack "C#{left_bytesize}"
16
- result = 0
17
- right.each_byte { |byte| result |= byte ^ unpacked_left.shift }
18
- result.zero?
19
- end
20
-
21
- def verify_rsa(algorithm, public_key, signing_input, signature)
22
- public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
23
- end
24
-
25
- def verify_ps(algorithm, public_key, signing_input, signature)
26
- formatted_algorithm = algorithm.sub('PS', 'sha')
27
-
28
- public_key.verify_pss(formatted_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: formatted_algorithm)
29
- end
30
-
31
- def asn1_to_raw(signature, public_key)
32
- byte_size = (public_key.group.degree + 7) / 8
33
- OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
34
- end
35
-
36
- def raw_to_asn1(signature, private_key)
37
- byte_size = (private_key.group.degree + 7) / 8
38
- sig_bytes = signature[0..(byte_size - 1)]
39
- sig_char = signature[byte_size..-1] || ''
40
- OpenSSL::ASN1::Sequence.new([sig_bytes, sig_char].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
41
- end
42
-
43
- def rbnacl_fixup(algorithm, key)
44
- algorithm = algorithm.sub('HS', 'SHA').to_sym
45
-
46
- return [] unless defined?(RbNaCl) && RbNaCl::HMAC.constants(false).include?(algorithm)
47
-
48
- authenticator = RbNaCl::HMAC.const_get(algorithm)
49
-
50
- # Fall back to OpenSSL for keys larger than 32 bytes.
51
- return [] if key.bytesize > authenticator.key_bytes
52
-
53
- [
54
- authenticator,
55
- key.bytes.fill(0, key.bytesize...authenticator.key_bytes).pack('C*')
56
- ]
57
- end
58
- end
59
- end
data/lib/jwt/signature.rb DELETED
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'jwt/security_utils'
4
- require 'openssl'
5
- require 'jwt/algos'
6
- begin
7
- require 'rbnacl'
8
- rescue LoadError
9
- raise if defined?(RbNaCl)
10
- end
11
-
12
- # JWT::Signature module
13
- module JWT
14
- # Signature logic for JWT
15
- module Signature
16
- module_function
17
-
18
- ToSign = Struct.new(:algorithm, :msg, :key)
19
- ToVerify = Struct.new(:algorithm, :public_key, :signing_input, :signature)
20
-
21
- def sign(algorithm, msg, key)
22
- algo, code = Algos.find(algorithm)
23
- algo.sign ToSign.new(code, msg, key)
24
- end
25
-
26
- def verify(algorithm, key, signing_input, signature)
27
- algo, code = Algos.find(algorithm)
28
- algo.verify(ToVerify.new(code, key, signing_input, signature))
29
- rescue OpenSSL::PKey::PKeyError
30
- raise JWT::VerificationError, 'Signature verification raised'
31
- ensure
32
- OpenSSL.errors.clear
33
- end
34
- end
35
- end