jwt 2.5.0 → 2.8.1
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 +4 -4
- data/CHANGELOG.md +92 -23
- data/CONTRIBUTING.md +7 -7
- data/README.md +125 -47
- data/lib/jwt/base64.rb +16 -2
- data/lib/jwt/claims_validator.rb +1 -1
- data/lib/jwt/configuration/container.rb +14 -3
- data/lib/jwt/decode.rb +41 -24
- data/lib/jwt/deprecations.rb +29 -0
- data/lib/jwt/encode.rb +23 -19
- data/lib/jwt/error.rb +1 -0
- data/lib/jwt/{algos → jwa}/ecdsa.rb +19 -7
- data/lib/jwt/jwa/eddsa.rb +42 -0
- data/lib/jwt/jwa/hmac.rb +75 -0
- data/lib/jwt/jwa/hmac_rbnacl.rb +50 -0
- data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +46 -0
- data/lib/jwt/{algos → jwa}/none.rb +4 -2
- data/lib/jwt/jwa/ps.rb +30 -0
- data/lib/jwt/jwa/rsa.rb +25 -0
- data/lib/jwt/{algos → jwa}/unsupported.rb +1 -1
- data/lib/jwt/jwa/wrapper.rb +26 -0
- data/lib/jwt/jwa.rb +62 -0
- data/lib/jwt/jwk/ec.rb +168 -116
- data/lib/jwt/jwk/hmac.rb +64 -28
- data/lib/jwt/jwk/key_base.rb +33 -11
- data/lib/jwt/jwk/key_finder.rb +19 -35
- data/lib/jwt/jwk/okp_rbnacl.rb +110 -0
- data/lib/jwt/jwk/rsa.rb +142 -77
- data/lib/jwt/jwk/set.rb +80 -0
- data/lib/jwt/jwk.rb +14 -11
- data/lib/jwt/verify.rb +8 -4
- data/lib/jwt/version.rb +20 -3
- data/lib/jwt/x5c_key_finder.rb +0 -3
- data/lib/jwt.rb +1 -0
- data/ruby-jwt.gemspec +11 -4
- metadata +35 -27
- data/.codeclimate.yml +0 -8
- data/.github/workflows/coverage.yml +0 -27
- data/.github/workflows/test.yml +0 -67
- data/.gitignore +0 -13
- data/.reek.yml +0 -22
- data/.rspec +0 -2
- data/.rubocop.yml +0 -67
- data/.sourcelevel.yml +0 -17
- data/Appraisals +0 -13
- data/Gemfile +0 -7
- data/Rakefile +0 -16
- data/lib/jwt/algos/eddsa.rb +0 -35
- data/lib/jwt/algos/hmac.rb +0 -36
- data/lib/jwt/algos/ps.rb +0 -43
- data/lib/jwt/algos/rsa.rb +0 -22
- data/lib/jwt/algos.rb +0 -44
- data/lib/jwt/security_utils.rb +0 -59
- data/lib/jwt/signature.rb +0 -35
@@ -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,24 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
3
|
+
require_relative 'jwa'
|
4
|
+
require_relative 'claims_validator'
|
5
5
|
|
6
6
|
# JWT::Encode module
|
7
7
|
module JWT
|
8
8
|
# Encoding logic for JWT
|
9
9
|
class Encode
|
10
|
-
|
11
|
-
ALG_KEY = 'alg'
|
10
|
+
ALG_KEY = 'alg'
|
12
11
|
|
13
12
|
def initialize(options)
|
14
|
-
@payload
|
15
|
-
@key
|
16
|
-
|
17
|
-
@headers
|
13
|
+
@payload = options[:payload]
|
14
|
+
@key = options[:key]
|
15
|
+
@algorithm = JWA.create(options[:algorithm])
|
16
|
+
@headers = options[:headers].transform_keys(&:to_s)
|
17
|
+
@headers[ALG_KEY] = @algorithm.alg
|
18
18
|
end
|
19
19
|
|
20
20
|
def segments
|
21
|
-
|
21
|
+
validate_claims!
|
22
|
+
combine(encoded_header_and_payload, encoded_signature)
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
@@ -40,25 +41,28 @@ module JWT
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def encode_header
|
43
|
-
@headers
|
44
|
-
encode(@headers)
|
44
|
+
encode_data(@headers)
|
45
45
|
end
|
46
46
|
|
47
47
|
def encode_payload
|
48
|
-
|
49
|
-
|
50
|
-
end
|
48
|
+
encode_data(@payload)
|
49
|
+
end
|
51
50
|
|
52
|
-
|
51
|
+
def signature
|
52
|
+
@algorithm.sign(data: encoded_header_and_payload, signing_key: @key)
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
56
|
-
return
|
55
|
+
def validate_claims!
|
56
|
+
return unless @payload.is_a?(Hash)
|
57
57
|
|
58
|
-
|
58
|
+
ClaimsValidator.new(@payload).validate!
|
59
|
+
end
|
60
|
+
|
61
|
+
def encode_signature
|
62
|
+
::JWT::Base64.url_encode(signature)
|
59
63
|
end
|
60
64
|
|
61
|
-
def
|
65
|
+
def encode_data(data)
|
62
66
|
::JWT::Base64.url_encode(JWT::JSON.generate(data))
|
63
67
|
end
|
64
68
|
|
data/lib/jwt/error.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
module
|
4
|
+
module JWA
|
5
5
|
module Ecdsa
|
6
6
|
module_function
|
7
7
|
|
@@ -30,8 +30,7 @@ module JWT
|
|
30
30
|
|
31
31
|
SUPPORTED = NAMED_CURVES.map { |_, c| c[:algorithm] }.uniq.freeze
|
32
32
|
|
33
|
-
def sign(
|
34
|
-
algorithm, msg, key = to_sign.values
|
33
|
+
def sign(algorithm, msg, key)
|
35
34
|
curve_definition = curve_by_name(key.group.curve_name)
|
36
35
|
key_algorithm = curve_definition[:algorithm]
|
37
36
|
if algorithm != key_algorithm
|
@@ -39,11 +38,10 @@ module JWT
|
|
39
38
|
end
|
40
39
|
|
41
40
|
digest = OpenSSL::Digest.new(curve_definition[:digest])
|
42
|
-
|
41
|
+
asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
|
43
42
|
end
|
44
43
|
|
45
|
-
def verify(
|
46
|
-
algorithm, public_key, signing_input, signature = to_verify.values
|
44
|
+
def verify(algorithm, public_key, signing_input, signature)
|
47
45
|
curve_definition = curve_by_name(public_key.group.curve_name)
|
48
46
|
key_algorithm = curve_definition[:algorithm]
|
49
47
|
if algorithm != key_algorithm
|
@@ -51,7 +49,9 @@ module JWT
|
|
51
49
|
end
|
52
50
|
|
53
51
|
digest = OpenSSL::Digest.new(curve_definition[:digest])
|
54
|
-
public_key.dsa_verify_asn1(digest.digest(signing_input),
|
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
55
|
end
|
56
56
|
|
57
57
|
def curve_by_name(name)
|
@@ -59,6 +59,18 @@ module JWT
|
|
59
59
|
raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
|
60
60
|
end
|
61
61
|
end
|
62
|
+
|
63
|
+
def raw_to_asn1(signature, private_key)
|
64
|
+
byte_size = (private_key.group.degree + 7) / 8
|
65
|
+
sig_bytes = signature[0..(byte_size - 1)]
|
66
|
+
sig_char = signature[byte_size..-1] || ''
|
67
|
+
OpenSSL::ASN1::Sequence.new([sig_bytes, sig_char].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
|
68
|
+
end
|
69
|
+
|
70
|
+
def asn1_to_raw(signature, public_key)
|
71
|
+
byte_size = (public_key.group.degree + 7) / 8
|
72
|
+
OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
|
73
|
+
end
|
62
74
|
end
|
63
75
|
end
|
64
76
|
end
|
@@ -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
|
data/lib/jwt/jwa/hmac.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module JWA
|
5
|
+
module Hmac
|
6
|
+
module_function
|
7
|
+
|
8
|
+
MAPPING = {
|
9
|
+
'HS256' => OpenSSL::Digest::SHA256,
|
10
|
+
'HS384' => OpenSSL::Digest::SHA384,
|
11
|
+
'HS512' => OpenSSL::Digest::SHA512
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
SUPPORTED = MAPPING.keys
|
15
|
+
|
16
|
+
def sign(algorithm, msg, key)
|
17
|
+
key ||= ''
|
18
|
+
|
19
|
+
raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
|
20
|
+
|
21
|
+
OpenSSL::HMAC.digest(MAPPING[algorithm].new, key, msg)
|
22
|
+
rescue OpenSSL::HMACError => e
|
23
|
+
if key == '' && e.message == 'EVP_PKEY_new_mac_key: malloc failure'
|
24
|
+
raise JWT::DecodeError, 'OpenSSL 3.0 does not support nil or empty hmac_secret'
|
25
|
+
end
|
26
|
+
|
27
|
+
raise e
|
28
|
+
end
|
29
|
+
|
30
|
+
def verify(algorithm, key, signing_input, signature)
|
31
|
+
SecurityUtils.secure_compare(signature, sign(algorithm, signing_input, key))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Copy of https://github.com/rails/rails/blob/v7.0.3.1/activesupport/lib/active_support/security_utils.rb
|
35
|
+
# rubocop:disable Naming/MethodParameterName, Style/StringLiterals, Style/NumericPredicate
|
36
|
+
module SecurityUtils
|
37
|
+
# Constant time string comparison, for fixed length strings.
|
38
|
+
#
|
39
|
+
# The values compared should be of fixed length, such as strings
|
40
|
+
# that have already been processed by HMAC. Raises in case of length mismatch.
|
41
|
+
|
42
|
+
if defined?(OpenSSL.fixed_length_secure_compare)
|
43
|
+
def fixed_length_secure_compare(a, b)
|
44
|
+
OpenSSL.fixed_length_secure_compare(a, b)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
# :nocov:
|
48
|
+
def fixed_length_secure_compare(a, b)
|
49
|
+
raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
|
50
|
+
|
51
|
+
l = a.unpack "C#{a.bytesize}"
|
52
|
+
|
53
|
+
res = 0
|
54
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
55
|
+
res == 0
|
56
|
+
end
|
57
|
+
# :nocov:
|
58
|
+
end
|
59
|
+
module_function :fixed_length_secure_compare
|
60
|
+
|
61
|
+
# Secure string comparison for strings of variable length.
|
62
|
+
#
|
63
|
+
# While a timing attack would not be able to discern the content of
|
64
|
+
# a secret compared via secure_compare, it is possible to determine
|
65
|
+
# the secret length. This should be considered when using secure_compare
|
66
|
+
# to compare weak, short secrets to user input.
|
67
|
+
def secure_compare(a, b)
|
68
|
+
a.bytesize == b.bytesize && fixed_length_secure_compare(a, b)
|
69
|
+
end
|
70
|
+
module_function :secure_compare
|
71
|
+
end
|
72
|
+
# rubocop:enable Naming/MethodParameterName, Style/StringLiterals, Style/NumericPredicate
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -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
|
data/lib/jwt/jwa/ps.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module JWA
|
5
|
+
module Ps
|
6
|
+
# RSASSA-PSS signing algorithms
|
7
|
+
|
8
|
+
module_function
|
9
|
+
|
10
|
+
SUPPORTED = %w[PS256 PS384 PS512].freeze
|
11
|
+
|
12
|
+
def sign(algorithm, msg, key)
|
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
|
+
|
17
|
+
translated_algorithm = algorithm.sub('PS', 'sha')
|
18
|
+
|
19
|
+
key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
|
20
|
+
end
|
21
|
+
|
22
|
+
def verify(algorithm, public_key, signing_input, signature)
|
23
|
+
translated_algorithm = algorithm.sub('PS', 'sha')
|
24
|
+
public_key.verify_pss(translated_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: translated_algorithm)
|
25
|
+
rescue OpenSSL::PKey::PKeyError
|
26
|
+
raise JWT::VerificationError, 'Signature verification raised'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/jwt/jwa/rsa.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module JWA
|
5
|
+
module Rsa
|
6
|
+
module_function
|
7
|
+
|
8
|
+
SUPPORTED = %w[RS256 RS384 RS512].freeze
|
9
|
+
|
10
|
+
def sign(algorithm, msg, key)
|
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
|
14
|
+
|
15
|
+
key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
|
16
|
+
end
|
17
|
+
|
18
|
+
def verify(algorithm, public_key, signing_input, signature)
|
19
|
+
public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
|
20
|
+
rescue OpenSSL::PKey::PKeyError
|
21
|
+
raise JWT::VerificationError, 'Signature verification raised'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module JWA
|
5
|
+
class Wrapper
|
6
|
+
attr_reader :alg, :cls
|
7
|
+
|
8
|
+
def initialize(alg, cls)
|
9
|
+
@alg = alg
|
10
|
+
@cls = cls
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid_alg?(alg_to_check)
|
14
|
+
alg&.casecmp(alg_to_check)&.zero? == true
|
15
|
+
end
|
16
|
+
|
17
|
+
def sign(data:, signing_key:)
|
18
|
+
cls.sign(alg, data, signing_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def verify(data:, signature:, verification_key:)
|
22
|
+
cls.verify(alg, verification_key, data, signature)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
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
|