jwt 2.8.2 → 2.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -0
- data/README.md +38 -12
- data/lib/jwt/claims/audience.rb +20 -0
- data/lib/jwt/claims/decode_verifier.rb +40 -0
- data/lib/jwt/claims/expiration.rb +22 -0
- data/lib/jwt/claims/issued_at.rb +15 -0
- data/lib/jwt/claims/issuer.rb +24 -0
- data/lib/jwt/claims/jwt_id.rb +25 -0
- data/lib/jwt/claims/not_before.rb +22 -0
- data/lib/jwt/claims/numeric.rb +55 -0
- data/lib/jwt/claims/required.rb +23 -0
- data/lib/jwt/claims/subject.rb +20 -0
- data/lib/jwt/claims/verifier.rb +62 -0
- data/lib/jwt/claims.rb +82 -0
- data/lib/jwt/claims_validator.rb +2 -23
- data/lib/jwt/decode.rb +3 -5
- data/lib/jwt/encode.rb +3 -7
- data/lib/jwt/jwa/compat.rb +29 -0
- data/lib/jwt/jwa/ecdsa.rb +42 -25
- data/lib/jwt/jwa/eddsa.rb +19 -27
- data/lib/jwt/jwa/hmac.rb +25 -17
- data/lib/jwt/jwa/hmac_rbnacl.rb +42 -43
- data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +39 -39
- data/lib/jwt/jwa/none.rb +7 -3
- data/lib/jwt/jwa/ps.rb +20 -14
- data/lib/jwt/jwa/rsa.rb +20 -9
- data/lib/jwt/jwa/signing_algorithm.rb +60 -0
- data/lib/jwt/jwa/unsupported.rb +8 -8
- data/lib/jwt/jwa/wrapper.rb +26 -9
- data/lib/jwt/jwa.rb +24 -36
- data/lib/jwt/verify.rb +10 -93
- data/lib/jwt/version.rb +2 -2
- data/lib/jwt.rb +4 -0
- metadata +21 -7
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.
|
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
|
-
|
54
|
+
Claims.verify_payload!(@payload, :numeric)
|
59
55
|
end
|
60
56
|
|
61
57
|
def encode_signature
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module JWA
|
5
|
+
module Compat
|
6
|
+
module ClassMethods
|
7
|
+
def from_algorithm(algorithm)
|
8
|
+
new(algorithm)
|
9
|
+
end
|
10
|
+
|
11
|
+
def sign(algorithm, msg, key)
|
12
|
+
Deprecations.warning('Support for calling sign with positional arguments will be removed in future ruby-jwt versions')
|
13
|
+
|
14
|
+
from_algorithm(algorithm).sign(data: msg, signing_key: key)
|
15
|
+
end
|
16
|
+
|
17
|
+
def verify(algorithm, key, signing_input, signature)
|
18
|
+
Deprecations.warning('Support for calling verify with positional arguments will be removed in future ruby-jwt versions')
|
19
|
+
|
20
|
+
from_algorithm(algorithm).verify(data: signing_input, signature: signature, verification_key: key)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.included(klass)
|
25
|
+
klass.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/jwt/jwa/ecdsa.rb
CHANGED
@@ -2,8 +2,35 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module JWA
|
5
|
-
|
6
|
-
|
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,38 +55,28 @@ module JWT
|
|
28
55
|
}
|
29
56
|
}.freeze
|
30
57
|
|
31
|
-
|
32
|
-
|
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"
|
38
|
-
end
|
39
|
-
|
40
|
-
digest = OpenSSL::Digest.new(curve_definition[:digest])
|
41
|
-
asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
|
58
|
+
NAMED_CURVES.each_value do |v|
|
59
|
+
register_algorithm(new(v[:algorithm], v[:digest]))
|
42
60
|
end
|
43
61
|
|
44
|
-
def
|
45
|
-
|
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
|
50
|
-
|
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'
|
62
|
+
def self.from_algorithm(algorithm)
|
63
|
+
new(algorithm, algorithm.downcase.gsub('es', 'sha'))
|
55
64
|
end
|
56
65
|
|
57
|
-
def curve_by_name(name)
|
66
|
+
def self.curve_by_name(name)
|
58
67
|
NAMED_CURVES.fetch(name) do
|
59
68
|
raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
|
60
69
|
end
|
61
70
|
end
|
62
71
|
|
72
|
+
private
|
73
|
+
|
74
|
+
attr_reader :digest
|
75
|
+
|
76
|
+
def curve_by_name(name)
|
77
|
+
self.class.curve_by_name(name)
|
78
|
+
end
|
79
|
+
|
63
80
|
def raw_to_asn1(signature, private_key)
|
64
81
|
byte_size = (private_key.group.degree + 7) / 8
|
65
82
|
sig_bytes = signature[0..(byte_size - 1)]
|
data/lib/jwt/jwa/eddsa.rb
CHANGED
@@ -2,41 +2,33 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module JWA
|
5
|
-
|
6
|
-
|
7
|
-
SUPPORTED_DOWNCASED = SUPPORTED.map(&:downcase).freeze
|
5
|
+
class Eddsa
|
6
|
+
include JWT::JWA::SigningAlgorithm
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
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)
|
8
|
+
def initialize(alg)
|
9
|
+
@alg = alg
|
10
|
+
end
|
16
11
|
|
17
|
-
|
12
|
+
def sign(data:, signing_key:)
|
13
|
+
unless signing_key.is_a?(RbNaCl::Signatures::Ed25519::SigningKey)
|
14
|
+
raise_encode_error!("Key given is a #{signing_key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey")
|
18
15
|
end
|
19
16
|
|
20
|
-
|
21
|
-
|
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)
|
17
|
+
signing_key.sign(data)
|
18
|
+
end
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
def verify(data:, signature:, verification_key:)
|
21
|
+
unless verification_key.is_a?(RbNaCl::Signatures::Ed25519::VerifyKey)
|
22
|
+
raise_decode_error!("key given is a #{verification_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey")
|
30
23
|
end
|
31
24
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
return if SUPPORTED_DOWNCASED.include?(algorithm.downcase)
|
36
|
-
|
37
|
-
raise IncorrectAlgorithm, "Algorithm #{algorithm} not supported. Supported algoritms are #{SUPPORTED.join(', ')}"
|
38
|
-
end
|
25
|
+
verification_key.verify(signature, data)
|
26
|
+
rescue RbNaCl::CryptoError
|
27
|
+
false
|
39
28
|
end
|
29
|
+
|
30
|
+
register_algorithm(new('ED25519'))
|
31
|
+
register_algorithm(new('EdDSA'))
|
40
32
|
end
|
41
33
|
end
|
42
34
|
end
|
data/lib/jwt/jwa/hmac.rb
CHANGED
@@ -2,35 +2,43 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module JWA
|
5
|
-
|
6
|
-
|
5
|
+
class Hmac
|
6
|
+
include JWT::JWA::SigningAlgorithm
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
'HS512' => OpenSSL::Digest::SHA512
|
12
|
-
}.freeze
|
13
|
-
|
14
|
-
SUPPORTED = MAPPING.keys
|
8
|
+
def self.from_algorithm(algorithm)
|
9
|
+
new(algorithm, OpenSSL::Digest.new(algorithm.downcase.gsub('hs', 'sha')))
|
10
|
+
end
|
15
11
|
|
16
|
-
def
|
17
|
-
|
12
|
+
def initialize(alg, digest)
|
13
|
+
@alg = alg
|
14
|
+
@digest = digest
|
15
|
+
end
|
18
16
|
|
19
|
-
|
17
|
+
def sign(data:, signing_key:)
|
18
|
+
signing_key ||= ''
|
19
|
+
raise_verify_error!('HMAC key expected to be a String') unless signing_key.is_a?(String)
|
20
20
|
|
21
|
-
OpenSSL::HMAC.digest(
|
21
|
+
OpenSSL::HMAC.digest(digest.new, signing_key, data)
|
22
22
|
rescue OpenSSL::HMACError => e
|
23
|
-
if
|
24
|
-
|
23
|
+
if signing_key == '' && e.message == 'EVP_PKEY_new_mac_key: malloc failure'
|
24
|
+
raise_verify_error!('OpenSSL 3.0 does not support nil or empty hmac_secret')
|
25
25
|
end
|
26
26
|
|
27
27
|
raise e
|
28
28
|
end
|
29
29
|
|
30
|
-
def verify(
|
31
|
-
SecurityUtils.secure_compare(signature, sign(
|
30
|
+
def verify(data:, signature:, verification_key:)
|
31
|
+
SecurityUtils.secure_compare(signature, sign(data: data, signing_key: verification_key))
|
32
32
|
end
|
33
33
|
|
34
|
+
register_algorithm(new('HS256', OpenSSL::Digest::SHA256))
|
35
|
+
register_algorithm(new('HS384', OpenSSL::Digest::SHA384))
|
36
|
+
register_algorithm(new('HS512', OpenSSL::Digest::SHA512))
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :digest
|
41
|
+
|
34
42
|
# Copy of https://github.com/rails/rails/blob/v7.0.3.1/activesupport/lib/active_support/security_utils.rb
|
35
43
|
# rubocop:disable Naming/MethodParameterName, Style/StringLiterals, Style/NumericPredicate
|
36
44
|
module SecurityUtils
|
data/lib/jwt/jwa/hmac_rbnacl.rb
CHANGED
@@ -1,49 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
4
|
+
module JWA
|
5
|
+
class HmacRbNaCl
|
6
|
+
include JWT::JWA::SigningAlgorithm
|
7
|
+
|
8
|
+
def self.from_algorithm(algorithm)
|
9
|
+
new(algorithm, ::RbNaCl::HMAC.const_get(algorithm.upcase.gsub('HS', 'SHA')))
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(alg, hmac)
|
13
|
+
@alg = alg
|
14
|
+
@hmac = hmac
|
15
|
+
end
|
16
|
+
|
17
|
+
def sign(data:, signing_key:)
|
18
|
+
Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
|
19
|
+
hmac.auth(key_for_rbnacl(hmac, signing_key).encode('binary'), data.encode('binary'))
|
20
|
+
end
|
21
|
+
|
22
|
+
def verify(data:, signature:, verification_key:)
|
23
|
+
Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
|
24
|
+
hmac.verify(key_for_rbnacl(hmac, verification_key).encode('binary'), signature.encode('binary'), data.encode('binary'))
|
25
|
+
rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
register_algorithm(new('HS512256', ::RbNaCl::HMAC::SHA512256))
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :hmac
|
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 padded_empty_key(length)
|
45
|
+
Array.new(length, 0x0).pack('C*').encode('binary')
|
47
46
|
end
|
48
47
|
end
|
49
48
|
end
|
@@ -1,45 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
4
|
+
module JWA
|
5
|
+
class HmacRbNaClFixed
|
6
|
+
include JWT::JWA::SigningAlgorithm
|
7
|
+
|
8
|
+
def self.from_algorithm(algorithm)
|
9
|
+
new(algorithm, ::RbNaCl::HMAC.const_get(algorithm.upcase.gsub('HS', 'SHA')))
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(alg, hmac)
|
13
|
+
@alg = alg
|
14
|
+
@hmac = hmac
|
15
|
+
end
|
16
|
+
|
17
|
+
def sign(data:, signing_key:)
|
18
|
+
signing_key ||= ''
|
19
|
+
Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
|
20
|
+
raise JWT::DecodeError, 'HMAC key expected to be a String' unless signing_key.is_a?(String)
|
21
|
+
|
22
|
+
hmac.auth(padded_key_bytes(signing_key, hmac.key_bytes), data.encode('binary'))
|
23
|
+
end
|
24
|
+
|
25
|
+
def verify(data:, signature:, verification_key:)
|
26
|
+
verification_key ||= ''
|
27
|
+
Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
|
28
|
+
raise JWT::DecodeError, 'HMAC key expected to be a String' unless verification_key.is_a?(String)
|
29
|
+
|
30
|
+
hmac.verify(padded_key_bytes(verification_key, hmac.key_bytes), signature.encode('binary'), data.encode('binary'))
|
31
|
+
rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
register_algorithm(new('HS512256', ::RbNaCl::HMAC::SHA512256))
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :hmac
|
40
|
+
|
41
|
+
def padded_key_bytes(key, bytesize)
|
42
|
+
key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
data/lib/jwt/jwa/none.rb
CHANGED
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module JWA
|
5
|
-
|
6
|
-
|
5
|
+
class None
|
6
|
+
include JWT::JWA::SigningAlgorithm
|
7
7
|
|
8
|
-
|
8
|
+
def initialize
|
9
|
+
@alg = 'none'
|
10
|
+
end
|
9
11
|
|
10
12
|
def sign(*)
|
11
13
|
''
|
@@ -14,6 +16,8 @@ module JWT
|
|
14
16
|
def verify(*)
|
15
17
|
true
|
16
18
|
end
|
19
|
+
|
20
|
+
register_algorithm(new)
|
17
21
|
end
|
18
22
|
end
|
19
23
|
end
|
data/lib/jwt/jwa/ps.rb
CHANGED
@@ -2,29 +2,35 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module JWA
|
5
|
-
|
6
|
-
|
5
|
+
class Ps
|
6
|
+
include JWT::JWA::SigningAlgorithm
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def initialize(alg)
|
9
|
+
@alg = alg
|
10
|
+
@digest_algorithm = alg.sub('PS', 'sha')
|
11
|
+
end
|
11
12
|
|
12
|
-
def sign(
|
13
|
-
unless
|
14
|
-
|
13
|
+
def sign(data:, signing_key:)
|
14
|
+
unless signing_key.is_a?(::OpenSSL::PKey::RSA)
|
15
|
+
raise_sign_error!("The given key is a #{signing_key.class}. It has to be an OpenSSL::PKey::RSA instance.")
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
|
18
|
+
signing_key.sign_pss(digest_algorithm, data, salt_length: :digest, mgf1_hash: digest_algorithm)
|
20
19
|
end
|
21
20
|
|
22
|
-
def verify(
|
23
|
-
|
24
|
-
public_key.verify_pss(translated_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: translated_algorithm)
|
21
|
+
def verify(data:, signature:, verification_key:)
|
22
|
+
verification_key.verify_pss(digest_algorithm, signature, data, salt_length: :auto, mgf1_hash: digest_algorithm)
|
25
23
|
rescue OpenSSL::PKey::PKeyError
|
26
24
|
raise JWT::VerificationError, 'Signature verification raised'
|
27
25
|
end
|
26
|
+
|
27
|
+
register_algorithm(new('PS256'))
|
28
|
+
register_algorithm(new('PS384'))
|
29
|
+
register_algorithm(new('PS512'))
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :digest_algorithm
|
28
34
|
end
|
29
35
|
end
|
30
36
|
end
|
data/lib/jwt/jwa/rsa.rb
CHANGED
@@ -2,24 +2,35 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module JWA
|
5
|
-
|
6
|
-
|
5
|
+
class Rsa
|
6
|
+
include JWT::JWA::SigningAlgorithm
|
7
7
|
|
8
|
-
|
8
|
+
def initialize(alg)
|
9
|
+
@alg = alg
|
10
|
+
@digest = OpenSSL::Digest.new(alg.sub('RS', 'SHA'))
|
11
|
+
end
|
9
12
|
|
10
|
-
def sign(
|
11
|
-
unless
|
12
|
-
|
13
|
+
def sign(data:, signing_key:)
|
14
|
+
unless signing_key.is_a?(OpenSSL::PKey::RSA)
|
15
|
+
raise_sign_error!("The given key is a #{signing_key.class}. It has to be an OpenSSL::PKey::RSA instance")
|
13
16
|
end
|
14
17
|
|
15
|
-
|
18
|
+
signing_key.sign(digest, data)
|
16
19
|
end
|
17
20
|
|
18
|
-
def verify(
|
19
|
-
|
21
|
+
def verify(data:, signature:, verification_key:)
|
22
|
+
verification_key.verify(digest, signature, data)
|
20
23
|
rescue OpenSSL::PKey::PKeyError
|
21
24
|
raise JWT::VerificationError, 'Signature verification raised'
|
22
25
|
end
|
26
|
+
|
27
|
+
register_algorithm(new('RS256'))
|
28
|
+
register_algorithm(new('RS384'))
|
29
|
+
register_algorithm(new('RS512'))
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :digest
|
23
34
|
end
|
24
35
|
end
|
25
36
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module JWA
|
5
|
+
module SigningAlgorithm
|
6
|
+
module ClassMethods
|
7
|
+
def register_algorithm(algo)
|
8
|
+
::JWT::JWA.register_algorithm(algo)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(klass)
|
13
|
+
klass.extend(ClassMethods)
|
14
|
+
klass.include(JWT::JWA::Compat)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :alg
|
18
|
+
|
19
|
+
def valid_alg?(alg_to_check)
|
20
|
+
alg&.casecmp(alg_to_check)&.zero? == true
|
21
|
+
end
|
22
|
+
|
23
|
+
def header(*)
|
24
|
+
{ 'alg' => alg }
|
25
|
+
end
|
26
|
+
|
27
|
+
def sign(*)
|
28
|
+
raise_sign_error!('Algorithm implementation is missing the sign method')
|
29
|
+
end
|
30
|
+
|
31
|
+
def verify(*)
|
32
|
+
raise_verify_error!('Algorithm implementation is missing the verify method')
|
33
|
+
end
|
34
|
+
|
35
|
+
def raise_verify_error!(message)
|
36
|
+
raise(DecodeError.new(message).tap { |e| e.set_backtrace(caller(1)) })
|
37
|
+
end
|
38
|
+
|
39
|
+
def raise_sign_error!(message)
|
40
|
+
raise(EncodeError.new(message).tap { |e| e.set_backtrace(caller(1)) })
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def register_algorithm(algo)
|
46
|
+
algorithms[algo.alg.to_s.downcase] = algo
|
47
|
+
end
|
48
|
+
|
49
|
+
def find(algo)
|
50
|
+
algorithms.fetch(algo.to_s.downcase, Unsupported)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def algorithms
|
56
|
+
@algorithms ||= {}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/jwt/jwa/unsupported.rb
CHANGED
@@ -3,16 +3,16 @@
|
|
3
3
|
module JWT
|
4
4
|
module JWA
|
5
5
|
module Unsupported
|
6
|
-
|
6
|
+
class << self
|
7
|
+
include JWT::JWA::SigningAlgorithm
|
7
8
|
|
8
|
-
|
9
|
+
def sign(*)
|
10
|
+
raise_sign_error!('Unsupported signing method')
|
11
|
+
end
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def verify(*)
|
15
|
-
raise JWT::VerificationError, 'Algorithm not supported'
|
13
|
+
def verify(*)
|
14
|
+
raise JWT::VerificationError, 'Algorithm not supported'
|
15
|
+
end
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|