jwt 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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