jwt 1.4.1 → 1.5.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 +4 -4
- data/Rakefile +1 -1
- data/jwt.gemspec +3 -3
- data/lib/jwt.rb +34 -0
- data/spec/jwt_spec.rb +74 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee5f493cb3c4ed9c97a60b59f7377f410b280218
|
4
|
+
data.tar.gz: f86ae6f76dbdd064ff4a7a83370b1ba343981d90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79802a75028b87314162658551582a1c4ace40439ac718bc8ac82ed57ccaa9b5d0ac695cf4daa318bcc89616ddc8e93bf3ff79d7cb4c47903c03aee51e5bb8a1
|
7
|
+
data.tar.gz: c14849f306b4952c7eba478d8fbc11c0a5045f38b931b563c67fef6367d9a8e89a66b78cccd9c8e4fbc546645e221b9b70437cde4aed18ca584269a95e94e9b6
|
data/Rakefile
CHANGED
data/jwt.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: jwt 1.
|
2
|
+
# stub: jwt 1.5.0 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "jwt"
|
6
|
-
s.version = "1.
|
6
|
+
s.version = "1.5.0"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib"]
|
10
10
|
s.authors = ["Jeff Lindsay"]
|
11
|
-
s.date = "2015-
|
11
|
+
s.date = "2015-05-09"
|
12
12
|
s.description = "JSON Web Token implementation in Ruby"
|
13
13
|
s.email = "progrium@gmail.com"
|
14
14
|
s.extra_rdoc_files = ["lib/jwt.rb", "lib/jwt/json.rb"]
|
data/lib/jwt.rb
CHANGED
@@ -12,6 +12,7 @@ module JWT
|
|
12
12
|
class DecodeError < StandardError; end
|
13
13
|
class VerificationError < DecodeError; end
|
14
14
|
class ExpiredSignature < DecodeError; end
|
15
|
+
class IncorrectAlgorithm < DecodeError; end
|
15
16
|
class ImmatureSignature < DecodeError; end
|
16
17
|
class InvalidIssuerError < DecodeError; end
|
17
18
|
class InvalidIatError < DecodeError; end
|
@@ -20,6 +21,12 @@ module JWT
|
|
20
21
|
class InvalidJtiError < DecodeError; end
|
21
22
|
extend JWT::Json
|
22
23
|
|
24
|
+
NAMED_CURVES = {
|
25
|
+
'prime256v1' => 'ES256',
|
26
|
+
'secp384r1' => 'ES384',
|
27
|
+
'secp521r1' => 'ES512',
|
28
|
+
}
|
29
|
+
|
23
30
|
module_function
|
24
31
|
|
25
32
|
def sign(algorithm, msg, key)
|
@@ -27,6 +34,8 @@ module JWT
|
|
27
34
|
sign_hmac(algorithm, msg, key)
|
28
35
|
elsif ['RS256', 'RS384', 'RS512'].include?(algorithm)
|
29
36
|
sign_rsa(algorithm, msg, key)
|
37
|
+
elsif ['ES256', 'ES384', 'ES512'].include?(algorithm)
|
38
|
+
sign_ecdsa(algorithm, msg, key)
|
30
39
|
else
|
31
40
|
raise NotImplementedError.new('Unsupported signing method')
|
32
41
|
end
|
@@ -36,10 +45,30 @@ module JWT
|
|
36
45
|
private_key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
|
37
46
|
end
|
38
47
|
|
48
|
+
def sign_ecdsa(algorithm, msg, private_key)
|
49
|
+
key_algorithm = NAMED_CURVES[private_key.group.curve_name]
|
50
|
+
if algorithm != key_algorithm
|
51
|
+
raise IncorrectAlgorithm.new("payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided")
|
52
|
+
end
|
53
|
+
|
54
|
+
digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
|
55
|
+
private_key.dsa_sign_asn1(digest.digest(msg))
|
56
|
+
end
|
57
|
+
|
39
58
|
def verify_rsa(algorithm, public_key, signing_input, signature)
|
40
59
|
public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
|
41
60
|
end
|
42
61
|
|
62
|
+
def verify_ecdsa(algorithm, public_key, signing_input, signature)
|
63
|
+
key_algorithm = NAMED_CURVES[public_key.group.curve_name]
|
64
|
+
if algorithm != key_algorithm
|
65
|
+
raise IncorrectAlgorithm.new("payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided")
|
66
|
+
end
|
67
|
+
|
68
|
+
digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
|
69
|
+
public_key.dsa_verify_asn1(digest.digest(signing_input), signature)
|
70
|
+
end
|
71
|
+
|
43
72
|
def sign_hmac(algorithm, msg, key)
|
44
73
|
OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
|
45
74
|
end
|
@@ -122,6 +151,9 @@ module JWT
|
|
122
151
|
|
123
152
|
if verify
|
124
153
|
algo, key = signature_algorithm_and_key(header, key, &keyfinder)
|
154
|
+
if options[:algorithm] && algo != options[:algorithm]
|
155
|
+
raise JWT::IncorrectAlgorithm.new('Expected a different algorithm')
|
156
|
+
end
|
125
157
|
verify_signature(algo, key, signing_input, signature)
|
126
158
|
end
|
127
159
|
|
@@ -168,6 +200,8 @@ module JWT
|
|
168
200
|
raise JWT::VerificationError.new('Signature verification failed') unless secure_compare(signature, sign_hmac(algo, signing_input, key))
|
169
201
|
elsif ['RS256', 'RS384', 'RS512'].include?(algo)
|
170
202
|
raise JWT::VerificationError.new('Signature verification failed') unless verify_rsa(algo, key, signing_input, signature)
|
203
|
+
elsif ['ES256', 'ES384', 'ES512'].include?(algo)
|
204
|
+
raise JWT::VerificationError.new('Signature verification failed') unless verify_ecdsa(algo, key, signing_input, signature)
|
171
205
|
else
|
172
206
|
raise JWT::VerificationError.new('Algorithm not supported')
|
173
207
|
end
|
data/spec/jwt_spec.rb
CHANGED
@@ -19,6 +19,36 @@ describe JWT do
|
|
19
19
|
expect(decoded_payload).to include(@payload)
|
20
20
|
end
|
21
21
|
|
22
|
+
it 'encodes and decodes JWTs for ECDSA P-256 signatures' do
|
23
|
+
private_key = OpenSSL::PKey::EC.new('prime256v1')
|
24
|
+
private_key.generate_key
|
25
|
+
public_key = OpenSSL::PKey::EC.new(private_key)
|
26
|
+
public_key.private_key = nil
|
27
|
+
jwt = JWT.encode(@payload, private_key, 'ES256')
|
28
|
+
decoded_payload = JWT.decode(jwt, public_key)
|
29
|
+
expect(decoded_payload).to include(@payload)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'encodes and decodes JWTs for ECDSA P-384 signatures' do
|
33
|
+
private_key = OpenSSL::PKey::EC.new('secp384r1')
|
34
|
+
private_key.generate_key
|
35
|
+
public_key = OpenSSL::PKey::EC.new(private_key)
|
36
|
+
public_key.private_key = nil
|
37
|
+
jwt = JWT.encode(@payload, private_key, 'ES384')
|
38
|
+
decoded_payload = JWT.decode(jwt, public_key)
|
39
|
+
expect(decoded_payload).to include(@payload)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'encodes and decodes JWTs for ECDSA P-521 signatures' do
|
43
|
+
private_key = OpenSSL::PKey::EC.new('secp521r1')
|
44
|
+
private_key.generate_key
|
45
|
+
public_key = OpenSSL::PKey::EC.new(private_key)
|
46
|
+
public_key.private_key = nil
|
47
|
+
jwt = JWT.encode(@payload, private_key, 'ES512')
|
48
|
+
decoded_payload = JWT.decode(jwt, public_key)
|
49
|
+
expect(decoded_payload).to include(@payload)
|
50
|
+
end
|
51
|
+
|
22
52
|
it 'encodes and decodes JWTs with custom header fields' do
|
23
53
|
private_key = OpenSSL::PKey::RSA.generate(512)
|
24
54
|
jwt = JWT.encode(@payload, private_key, 'RS256', {'kid' => 'default'})
|
@@ -29,6 +59,14 @@ describe JWT do
|
|
29
59
|
expect(decoded_payload).to include(@payload)
|
30
60
|
end
|
31
61
|
|
62
|
+
it 'raises encode exception when ECDSA algorithm does not match key' do
|
63
|
+
private_key = OpenSSL::PKey::EC.new('prime256v1')
|
64
|
+
private_key.generate_key
|
65
|
+
expect do
|
66
|
+
JWT.encode(@payload, private_key, 'ES512')
|
67
|
+
end.to raise_error(JWT::IncorrectAlgorithm, 'payload algorithm is ES512 but ES256 signing key was provided')
|
68
|
+
end
|
69
|
+
|
32
70
|
it 'decodes valid JWTs' do
|
33
71
|
example_payload = {'hello' => 'world'}
|
34
72
|
example_secret = 'secret'
|
@@ -146,6 +184,21 @@ describe JWT do
|
|
146
184
|
expect { JWT.decode(jwt_message, bad_secret) }.to raise_error(JWT::VerificationError)
|
147
185
|
end
|
148
186
|
|
187
|
+
it 'raises decode exception when ECDSA algorithm does not match key' do
|
188
|
+
right_private_key = OpenSSL::PKey::EC.new('prime256v1')
|
189
|
+
right_private_key.generate_key
|
190
|
+
right_public_key = OpenSSL::PKey::EC.new(right_private_key)
|
191
|
+
right_public_key.private_key = nil
|
192
|
+
bad_private_key = OpenSSL::PKey::EC.new('secp384r1')
|
193
|
+
bad_private_key.generate_key
|
194
|
+
bad_public_key = OpenSSL::PKey::EC.new(bad_private_key)
|
195
|
+
bad_public_key.private_key = nil
|
196
|
+
jwt = JWT.encode(@payload, right_private_key, 'ES256')
|
197
|
+
expect do
|
198
|
+
JWT.decode(jwt, bad_public_key)
|
199
|
+
end.to raise_error(JWT::IncorrectAlgorithm, 'payload algorithm is ES256 but ES384 verification key was provided')
|
200
|
+
end
|
201
|
+
|
149
202
|
it 'raises verification exception with wrong rsa key' do
|
150
203
|
right_private_key = OpenSSL::PKey::RSA.generate(512)
|
151
204
|
bad_private_key = OpenSSL::PKey::RSA.generate(512)
|
@@ -153,6 +206,17 @@ describe JWT do
|
|
153
206
|
expect { JWT.decode(jwt, bad_private_key.public_key) }.to raise_error(JWT::VerificationError)
|
154
207
|
end
|
155
208
|
|
209
|
+
it 'raises verification exception with wrong ECDSA key' do
|
210
|
+
right_private_key = OpenSSL::PKey::EC.new('prime256v1')
|
211
|
+
right_private_key.generate_key
|
212
|
+
bad_private_key = OpenSSL::PKey::EC.new('prime256v1')
|
213
|
+
bad_private_key.generate_key
|
214
|
+
bad_public_key = OpenSSL::PKey::EC.new(bad_private_key)
|
215
|
+
bad_public_key.private_key = nil
|
216
|
+
jwt = JWT.encode(@payload, right_private_key, 'ES256')
|
217
|
+
expect { JWT.decode(jwt, bad_public_key) }.to raise_error(JWT::VerificationError)
|
218
|
+
end
|
219
|
+
|
156
220
|
it 'raises decode exception with invalid signature' do
|
157
221
|
example_secret = 'secret'
|
158
222
|
example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.'
|
@@ -191,6 +255,16 @@ describe JWT do
|
|
191
255
|
expect { JWT.encode(@payload, 'secret', 'HS1024') }.to raise_error(NotImplementedError)
|
192
256
|
end
|
193
257
|
|
258
|
+
it 'raises exception when decoded with a different algorithm than it was encoded with' do
|
259
|
+
jwt = JWT.encode(@payload, 'foo', 'HS384')
|
260
|
+
expect { JWT.decode(jwt, 'foo', true, :algorithm => 'HS512') }.to raise_error(JWT::IncorrectAlgorithm)
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'does not raise exception when encoded with the expected algorithm' do
|
264
|
+
jwt = JWT.encode(@payload, 'foo', 'HS512')
|
265
|
+
JWT.decode(jwt, 'foo', true, :algorithm => 'HS512')
|
266
|
+
end
|
267
|
+
|
194
268
|
it 'encodes and decodes plaintext JWTs' do
|
195
269
|
jwt = JWT.encode(@payload, nil, nil)
|
196
270
|
expect(jwt.split('.').length).to eq(2)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Lindsay
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: echoe
|