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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/jwt.gemspec +3 -3
  4. data/lib/jwt.rb +34 -0
  5. data/spec/jwt_spec.rb +74 -0
  6. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cdfac4049ccdded9be1f09b66e5af478ef37fc4d
4
- data.tar.gz: af9de309fd5e3a80c94c28003b203c7776782591
3
+ metadata.gz: ee5f493cb3c4ed9c97a60b59f7377f410b280218
4
+ data.tar.gz: f86ae6f76dbdd064ff4a7a83370b1ba343981d90
5
5
  SHA512:
6
- metadata.gz: 175bb4f9a775da249f7d3825469d8970f6c2d0d2e78316f2f152be4c2446062d9480d9f0ca6033f2f7c109da9f4d5d6ec56c14386dba0024cc62c4d68d565c54
7
- data.tar.gz: ca034a2e46c9cbac727a8822fc0c170d2adf04799cc6e698cfe2dc6e014507726d32a939bb4e87739518aca7d60660afc13cfa09a7bc9a6cce30248d7e76be61
6
+ metadata.gz: 79802a75028b87314162658551582a1c4ace40439ac718bc8ac82ed57ccaa9b5d0ac695cf4daa318bcc89616ddc8e93bf3ff79d7cb4c47903c03aee51e5bb8a1
7
+ data.tar.gz: c14849f306b4952c7eba478d8fbc11c0a5045f38b931b563c67fef6367d9a8e89a66b78cccd9c8e4fbc546645e221b9b70437cde4aed18ca584269a95e94e9b6
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('jwt', '1.4.1') do |p|
5
+ Echoe.new('jwt', '1.5.0') do |p|
6
6
  p.description = 'JSON Web Token implementation in Ruby'
7
7
  p.url = 'http://github.com/progrium/ruby-jwt'
8
8
  p.author = 'Jeff Lindsay'
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: jwt 1.4.1 ruby lib
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.4.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-03-12"
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
@@ -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.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-03-12 00:00:00.000000000 Z
11
+ date: 2015-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: echoe